|
@@ -0,0 +1,240 @@
|
|
|
+import * as THREE from "three";
|
|
|
+import { THREEMaterialType, IBaseProps, IGroupParams, IMeshParams, IPointLight } from "./type/types";
|
|
|
+
|
|
|
+/**
|
|
|
+ * glb模型并发加载工作原理
|
|
|
+ * web worker 可以解析glb模型 但是postMessage发送的数据类型只是普通的对象,
|
|
|
+ * 并且不能存在方法 方法无法传递,带方法会导致发送失败,并且不会触发onerror
|
|
|
+ * THREE构建物体所需的bufferGeometry,还是BufferAttribute 或者Material等原型对象无法被传递
|
|
|
+ * 传递到主线程的只是一个普通对象和上面的属性(对象中不能有函数)
|
|
|
+ * 可以通过生成一个THREE所需的类型 把传递过来的对象上的参数复制给THREE需要的对象上
|
|
|
+ * 这样在主线程生成一个同样的模型,但是省去了解析模型时间(模型解析在web worker中与js主线程并发执行)
|
|
|
+ * 实现并发加载
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * 通过设置attributes index来复刻一个集合体
|
|
|
+ */
|
|
|
+const genGeometry = (geometry: IMeshParams["geometry"], IMeshParams) => {
|
|
|
+ // console.log('目标------>',geometry);
|
|
|
+ const geom = new THREE.BufferGeometry();
|
|
|
+ const {
|
|
|
+ attributes: { position, uv, normal },
|
|
|
+ index,
|
|
|
+ } = geometry;
|
|
|
+
|
|
|
+
|
|
|
+ //处理几何坐标
|
|
|
+ if(uv){
|
|
|
+ const attributes = {
|
|
|
+ position: new THREE.BufferAttribute(position.array, position.itemSize, position.normalized),
|
|
|
+ uv: new THREE.BufferAttribute(uv.array, uv.itemSize, uv.normalized),
|
|
|
+ normal: new THREE.BufferAttribute(normal.array, normal.itemSize, normal.normalized),
|
|
|
+ };
|
|
|
+ geom.attributes = attributes;
|
|
|
+ }else {
|
|
|
+ const attributes = {
|
|
|
+ position: new THREE.BufferAttribute(position.array, position.itemSize, position.normalized),
|
|
|
+ normal: new THREE.BufferAttribute(normal.array, normal.itemSize, normal.normalized),
|
|
|
+ };
|
|
|
+ // geom.attributes['position'] = new THREE.BufferAttribute(position.array, position.itemSize, position.normalized),
|
|
|
+ // geom.attributes['normal'] = new THREE.BufferAttribute(normal.array, normal.itemSize, normal.normalized)
|
|
|
+ geom.attributes = attributes;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ geom.index = index ? new THREE.BufferAttribute(index.array, index.itemSize, index.normalized) : null;
|
|
|
+ return geom;
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * 根据传入纹理的参数生成真正有效的Material类型数据
|
|
|
+ */
|
|
|
+const genMaterial = (mate: IMeshParams["material"]) => {
|
|
|
+ if (!mate) return undefined;
|
|
|
+ const multipleMaterial = Array.isArray(mate);
|
|
|
+ const material = multipleMaterial ? ([] as THREE.Material[]) : new THREE[mate.type as THREEMaterialType]();
|
|
|
+ //处理材质
|
|
|
+ //多个材质
|
|
|
+ if (multipleMaterial && Array.isArray(material)) {
|
|
|
+ console.log('有多个材质');
|
|
|
+
|
|
|
+ for (const m of mate) {
|
|
|
+ const im = new THREE[m.type as THREEMaterialType]();
|
|
|
+
|
|
|
+ material.push(im);
|
|
|
+ }
|
|
|
+ } else if (mate) {
|
|
|
+ //单个材质
|
|
|
+ Object.assign(material, mate);
|
|
|
+ for (const key in mate) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(mate, key)) {
|
|
|
+ const element = mate[key];
|
|
|
+ if(element && Object.prototype.toString.call(element === '[object Object]')) {
|
|
|
+ if( element.isTexture){
|
|
|
+ const texture = new THREE.Texture()
|
|
|
+ for (const k in texture) {
|
|
|
+ if(Object.prototype.toString.call(texture[k]) === '[object Object]'){
|
|
|
+ if(k==='center' || k==='offset' || k==='repeat'){
|
|
|
+ texture[k] = new THREE.Vector2()
|
|
|
+ }
|
|
|
+ if(k === 'matrix'){
|
|
|
+ texture[k] = new THREE.Matrix3()
|
|
|
+ }
|
|
|
+ if(k === 'source'){
|
|
|
+ texture[k] = new THREE.Source(null)
|
|
|
+ }
|
|
|
+ Object.assign(texture[k] , element[k]);
|
|
|
+ }else {
|
|
|
+ texture[k] = element[k]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ material[key] = texture
|
|
|
+ }
|
|
|
+ if(element.isColor){
|
|
|
+ const color = new THREE.Color()
|
|
|
+ Object.assign(color, element);
|
|
|
+ material[key] = color
|
|
|
+ }
|
|
|
+ if(key === 'normalScale'){
|
|
|
+ const vector2 = new THREE.Vector2()
|
|
|
+ Object.assign(vector2, element);
|
|
|
+ material[key] = vector2
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return material;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 处理基本属性转换(Object3D基类上的属性) matrix scale rotate translate position children animations
|
|
|
+ */
|
|
|
+const parseBaseParams = (params: IBaseProps, object: THREE.Object3D) => {
|
|
|
+ const matrix = new THREE.Matrix4();
|
|
|
+ matrix.elements = params.matrix.elements;
|
|
|
+ object.name = params.name;
|
|
|
+ object.matrix = matrix;
|
|
|
+ object.rotation.set(...params.rotation);
|
|
|
+ object.position.set(...params.position);
|
|
|
+ object.scale.set(...params.scale);
|
|
|
+ object.quaternion.set(...params.quaternion);
|
|
|
+ object.up.set(...params.up);
|
|
|
+ object.userData = params.userData;
|
|
|
+ object.visible = params.visible;
|
|
|
+
|
|
|
+ parseChildren(object, params.children);
|
|
|
+ genAnimations(object, params.animations);
|
|
|
+ // deleteObjectKeys(params, [
|
|
|
+ // "name",
|
|
|
+ // "matrix",
|
|
|
+ // "rotation",
|
|
|
+ // "position",
|
|
|
+ // "scale",
|
|
|
+ // "quaternion",
|
|
|
+ // "up",
|
|
|
+ // "userData",
|
|
|
+ // "visible",
|
|
|
+ // ]);
|
|
|
+ // object.scale.x += 0.3;
|
|
|
+ // object.scale.y += 0.3;
|
|
|
+ // object.scale.z += 0.3;
|
|
|
+};
|
|
|
+
|
|
|
+const parseMesh = (IMeshParams: IMeshParams) => {
|
|
|
+ const geometry = genGeometry(IMeshParams.geometry, IMeshParams);
|
|
|
+ const material = genMaterial(IMeshParams.material);
|
|
|
+
|
|
|
+ const mesh = new THREE.Mesh(geometry, material);
|
|
|
+ parseBaseParams(IMeshParams, mesh);
|
|
|
+ return mesh;
|
|
|
+};
|
|
|
+
|
|
|
+const parseGroup = (params: IGroupParams) => {
|
|
|
+ const group = new THREE.Group();
|
|
|
+ parseBaseParams(params, group);
|
|
|
+ return group;
|
|
|
+};
|
|
|
+
|
|
|
+const parsePointLight = (params: IPointLight) => {
|
|
|
+ const color = new THREE.Color();
|
|
|
+ // 色彩空间
|
|
|
+ // export type ColorSpace = NoColorSpace | SRGBColorSpace | LinearSRGBColorSpace;
|
|
|
+ // export type NoColorSpace = '';
|
|
|
+ // export type SRGBColorSpace = 'srgb';
|
|
|
+ // export type LinearSRGBColorSpace = 'srgb-linear';
|
|
|
+ //glb模型为了亮度恢复 使用srgb格式 所以颜色也使用同样格式 使其颜色模式一致
|
|
|
+ color.setRGB(params.color.r, params.color.g, params.color.b, "srgb-linear");
|
|
|
+
|
|
|
+ const pointLight = new THREE.PointLight(color, params.intensity, params.distance, params.decay);
|
|
|
+ parseBaseParams(params, pointLight);
|
|
|
+ return pointLight;
|
|
|
+};
|
|
|
+
|
|
|
+const parseObject3D = (params: IBaseProps) => {
|
|
|
+ const object = new THREE.Object3D();
|
|
|
+ parseBaseParams(params, object);
|
|
|
+ return object;
|
|
|
+};
|
|
|
+
|
|
|
+const parseChildren = (object3D: THREE.Object3D, children: IBaseProps[]) => {
|
|
|
+ if (!children.length) return;
|
|
|
+ const objectList: THREE.Object3D[] = [];
|
|
|
+ for (const child of children) {
|
|
|
+ const { type } = child;
|
|
|
+ if (type === "Mesh") {
|
|
|
+ objectList.push(parseMesh(child as IMeshParams));
|
|
|
+ } else if (type === "Group") {
|
|
|
+ objectList.push(parseGroup(child));
|
|
|
+ } else if (type === "PointLight") {
|
|
|
+ objectList.push(parsePointLight(child as IPointLight));
|
|
|
+ } else if (type === "Object3D") {
|
|
|
+ objectList.push(parseObject3D(child));
|
|
|
+ } else {
|
|
|
+ throw new Error("出现了未处理的类型:" + type);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ object3D.add(...objectList);
|
|
|
+};
|
|
|
+/**
|
|
|
+ * 生成动画
|
|
|
+ */
|
|
|
+const genAnimations = (object3D: THREE.Object3D, sceneAnimations: IGroupParams["sceneAnimations"]) => {
|
|
|
+ if (!sceneAnimations) return;
|
|
|
+ const animations: THREE.AnimationClip[] = [];
|
|
|
+
|
|
|
+ for (const animation of sceneAnimations!) {
|
|
|
+ const clip = new THREE.AnimationClip(animation.name, animation.duration, [], animation.blendMode);
|
|
|
+ //@ts-ignore
|
|
|
+ for (const { name, times, values, type } of animation.tracks) {
|
|
|
+ let nreTrack;
|
|
|
+ if(type){
|
|
|
+ if(type === 'vector') nreTrack = new THREE.VectorKeyframeTrack(name, times as any, values as any);
|
|
|
+ if(type === 'quaternion') nreTrack = new THREE.QuaternionKeyframeTrack(name, times as any, values as any);
|
|
|
+ }else {
|
|
|
+ nreTrack = new THREE.QuaternionKeyframeTrack(name, times as any, values as any);
|
|
|
+ }
|
|
|
+
|
|
|
+ clip.tracks.push(nreTrack);
|
|
|
+ }
|
|
|
+
|
|
|
+ animations.push(clip);
|
|
|
+ }
|
|
|
+
|
|
|
+ object3D.animations = animations;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 解析传入的模型参数生成有效的three.js物体
|
|
|
+ */
|
|
|
+export const parseModel = (params: IGroupParams) => {
|
|
|
+ const model = parseGroup(params);
|
|
|
+ // model.position.x += 10;
|
|
|
+ genAnimations(model, params.sceneAnimations);
|
|
|
+ // console.log("解析完:", model);
|
|
|
+ return model;
|
|
|
+};
|