material-editor/src/common/MaterialEditor.ts

237 lines
7.7 KiB
TypeScript
Raw Normal View History

2025-04-10 16:37:20 +08:00
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<string, Geometry | BufferGeometry>;
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<string, Geometry | BufferGeometry>(
[
["球", 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<Texture>;
async LoadMetalEnv(): Promise<Texture>
{
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<Texture>;
exrTexture: Texture;
async LoadDefaultExr(): Promise<Texture>
{
if (this.exrTexture) return this.exrTexture;
if (this.exrPromise) return this.exrPromise;
this.exrPromise = new Promise<Texture>((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();
}
}