import { AmbientLight, BoxBufferGeometry, BufferGeometry, ConeBufferGeometry, CubeRefractionMapping, CubeTextureLoader, Geometry, Mesh, MeshPhysicalMaterial, Object3D, SphereBufferGeometry, sRGBEncoding, Texture, TorusBufferGeometry, TorusKnotBufferGeometry } from 'three'; import { Singleton } from './Singleton'; import { Viewer } from './Viewer'; import { PMREMGenerator3 } from './PMREMGenerator2'; import type { PhysicalMaterialRecord, TextureTableRecord } from 'webcad_ue4_api'; import { MaterialEditorCamerControl } from './MaterialMouseControl'; import { ref } from 'vue'; async function textureRenderUpdate(textureRecord:TextureTableRecord){ const texture = textureRecord['texture'] as Texture; texture.wrapS = textureRecord.WrapS; texture.wrapT = textureRecord.WrapT; texture.anisotropy = 16; texture.rotation = textureRecord.rotation; texture.repeat.set(textureRecord.repeatX, textureRecord.repeatY); texture.offset.set(textureRecord.moveX, textureRecord.moveY); texture.needsUpdate = true; // for (let f of this.waits) // f(); // this.waits.length = 0; // // this.AsyncUpdated(); } /** * 材质编辑器 */ export class MaterialEditor extends Singleton { Geometrys: Map; CurGeometryName = ref("球"); Canvas: HTMLElement; ShowObject: Object3D; ShowMesh: Mesh; Viewer: Viewer; private _MeshMaterial: MeshPhysicalMaterial = new MeshPhysicalMaterial({}); //构造 private constructor() { super(); this.initGeometrys(); this.LoadDefaultExr(); this.LoadMetalEnv(); } initGeometrys() { this.Geometrys = new Map( [ ["球", new SphereBufferGeometry(1000, 32, 32)], ["圆环", new TorusBufferGeometry(0.8 * 1e3, 0.4 * 1e3, 32, 64)], ["立方体", new BoxBufferGeometry(1e3, 1e3, 1e3, 1, 1, 1)], ["环面纽结", new TorusKnotBufferGeometry(0.7 * 1e3, 0.3 * 1e3, 128, 64)], ["圆锥体", new ConeBufferGeometry(1 * 1e3, 2 * 1e3, 32)], // ["球(多面)", new SphereBufferGeometry(1 * 1e3, 64, 64)], // ["立方体(多面)", new BoxBufferGeometry(1 * 1e3, 1 * 1e3, 1 * 1e3, 128, 128, 128)] ] ); } initViewer() { if (!this.Viewer) { this.Viewer = new Viewer(this.Canvas); // this.Viewer.PreViewer.Cursor.CursorObject.visible = false; // this.Viewer.CameraCtrl.CameraType = CameraType.PerspectiveCamera; // this.Viewer.UsePass = false; this.initScene(); new MaterialEditorCamerControl(this.Viewer); } else { this.Canvas.appendChild(this.Viewer.Renderer.domElement); } } SetViewer(canvas: HTMLElement) { this.Canvas = canvas; this.initViewer(); this.CurGeometryName.value = "球"; } initScene() { let scene = this.Viewer.Scene; this.ShowObject = new Object3D(); let geo = this.Geometrys.get(this.CurGeometryName.value); this.ShowMesh = new Mesh(geo, this._MeshMaterial); this.ShowMesh.scale.set(1000, 1000, 1000); this.ShowObject.add(this.ShowMesh); scene.add(this.ShowObject); // let remove = autorun(() => // { // let geo = this.Geometrys.get(this.CurGeometryName.get()); // if (geo) // { // this.ShowMesh.geometry = geo; // this.Viewer.UpdateRender(); // } // }); // end(this as MaterialEditor, this.dispose, remove); //环境光 let ambient = new AmbientLight(); ambient.intensity = 0.7; scene.add(ambient); } metaTexture: Texture; metaPromise: Promise; async LoadMetalEnv(): Promise { if (this.metaTexture) return this.metaTexture; if (this.metaPromise) return this.metaPromise; return new Promise((res, rej) => { let urls = ['right.webp', 'left.webp', 'top.webp', 'bottom.webp', 'front.webp', 'back.webp']; new CubeTextureLoader().setPath('https://cdn.cfcad.cn/t/house/') .load(urls, (t) => { t.encoding = sRGBEncoding; t.mapping = CubeRefractionMapping; let pmremGenerator = new PMREMGenerator3(this.Viewer.Renderer); let ldrCubeRenderTarget = pmremGenerator.fromCubemap(t); this.metaTexture = ldrCubeRenderTarget.texture; res(this.metaTexture); this.Viewer.Scene.background = this.metaTexture; // this.Viewer.Scene.background = new Color(255,0,0); this.Viewer.UpdateRender(); }); }); } exrPromise: Promise; exrTexture: Texture; async LoadDefaultExr(): Promise { if (this.exrTexture) return this.exrTexture; if (this.exrPromise) return this.exrPromise; this.exrPromise = new Promise((res, rej) => { let urls = ['right.webp', 'left.webp', 'top.webp', 'bottom.webp', 'front.webp', 'back.webp']; new CubeTextureLoader().setPath('https://cdn.cfcad.cn/t/house_gray/') .load(urls, (t) => { t.encoding = sRGBEncoding; t.mapping = CubeRefractionMapping; let pmremGenerator = new PMREMGenerator3(this.Viewer.Renderer); let target = pmremGenerator.fromCubemap(t); this.exrTexture = target.texture; res(this.exrTexture); this.Viewer.UpdateRender(); }); }); return this.exrPromise; } private Material: PhysicalMaterialRecord; setMaterial(mat: PhysicalMaterialRecord) { this.Material = mat; this._MeshMaterial.copy(mat.Material); let mtl = this._MeshMaterial; if (mtl.metalness > 0.8) this.LoadMetalEnv().then(env => { mtl.envMap = env; mtl.needsUpdate = true; }); else this.LoadDefaultExr().then(exr => { mtl.envMap = exr; mtl.needsUpdate = true; }); this.Update(); } dispose() { } async Update() { let mat = this.ShowMesh.material as MeshPhysicalMaterial; if (this.Material.map && this.Material.map.Object && this.Material.useMap) { let texture = this.Material.map.Object as TextureTableRecord; await textureRenderUpdate(texture); } if (this.Material.bumpMap && this.Material.bumpMap.Object) { let texture = this.Material.bumpMap.Object as TextureTableRecord; await textureRenderUpdate(texture); } if (this.Material.roughnessMap && this.Material.roughnessMap.Object) { let texture = this.Material.roughnessMap.Object as TextureTableRecord; await textureRenderUpdate(texture); } mat.needsUpdate = true; this._MeshMaterial.copy(this.Material.Material); let mtl = this._MeshMaterial; if (mtl.metalness > 0.8) this.LoadMetalEnv().then(env => { mtl.envMap = env; mtl.needsUpdate = true; }); else this.LoadDefaultExr().then(exr => { mtl.envMap = exr; mtl.needsUpdate = true; }); this.Viewer.UpdateRender(); } }