grid-apps-cmms/src/mesh/model.js

258 lines
7 KiB
JavaScript
Raw Normal View History

/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
"use strict";
(function() {
gapp.register("mesh.model", [
"add.array", // dep: add.array
"add.three", // dep: add.three
"moto.license", // dep: moto.license
"mesh.object", // dep: mesh.object
2021-12-30 22:59:46 -05:00
"mesh.group", // dep: mesh.group
2022-01-11 20:02:29 -05:00
"mesh.util", // dep: mesh.util
"mesh.api", // dep: mesh.api
]);
let mesh = self.mesh = self.mesh || {};
if (mesh.model) return;
let mapp = mesh;
let space = moto.Space;
let worker = moto.client.fn;
2022-01-12 11:46:10 -05:00
let { MeshPhongMaterial, MeshBasicMaterial } = THREE;
let { BufferGeometry, BufferAttribute, DoubleSide, Mesh } = THREE;
/** default materials **/
let materials = mesh.material = {
2022-01-12 11:46:10 -05:00
unselected: new MeshPhongMaterial({
side: DoubleSide,
2021-12-29 17:20:17 -05:00
transparent: true,
shininess: 100,
specular: 0x202020,
2021-12-29 17:20:17 -05:00
color: 0xffff00,
opacity: 1
}),
2022-01-12 11:46:10 -05:00
selected: new MeshPhongMaterial({
side: DoubleSide,
transparent: true,
shininess: 100,
specular: 0x202020,
2022-01-06 23:51:35 -05:00
color: 0x00ee00,
opacity: 1
}),
2022-01-12 11:46:10 -05:00
wireframe: new MeshBasicMaterial({
side: DoubleSide,
wireframe: true,
color: 0x0
}),
2021-12-29 17:20:17 -05:00
};
/** 3D model rendered on plaform **/
mesh.model = class MeshModel extends mesh.object {
constructor(data, id) {
super(id);
2022-01-05 09:29:27 -05:00
let { file, mesh, vertices, indices, normals } = data;
if (!mesh) {
dbug.error(`'${file}' missing mesh data`);
return;
}
// remove file name extensions
let text = file || '';
let dot = text.lastIndexOf('.');
if (dot > 0) file = text.substring(0, dot);
this.file = file || 'unnamed';
2022-01-05 09:29:27 -05:00
this.load(mesh || vertices, indices, normals);
// persist in db so it can be restored on page load
mapp.db.space.put(this.id, { file, mesh });
2021-12-29 17:20:17 -05:00
}
get type() {
return "model";
}
get object() {
return this.mesh;
2021-12-30 22:59:46 -05:00
}
2022-01-08 21:00:19 -05:00
get matrix() {
return this.mesh.matrixWorld.elements;
}
2022-01-11 14:41:49 -05:00
// return new group containing just this model in world coordinates
duplicate() {
worker.model_duplicate({
2022-01-08 21:00:19 -05:00
matrix: this.matrix,
2022-01-01 22:39:49 -05:00
id: this.id
2021-12-31 22:11:20 -05:00
}).then(data => {
mesh.api.group.new([new mesh.model({
2022-01-11 14:41:49 -05:00
file: `${this.file}-dup`,
2022-01-07 14:39:32 -05:00
mesh: data
})]);
2021-12-31 22:11:20 -05:00
});
}
2022-01-05 09:29:27 -05:00
load(vertices, indices, normals) {
2022-01-12 11:46:10 -05:00
let geo = new BufferGeometry();
geo.setAttribute('position', new BufferAttribute(vertices, 3));
if (indices) geo.setIndex(new BufferAttribute(indices, 1));
2022-01-05 09:29:27 -05:00
if (!normals) geo.computeVertexNormals();
2022-01-12 11:46:10 -05:00
let meh = this.mesh = new Mesh(geo, materials.unselected);
2021-12-29 17:20:17 -05:00
meh.receiveShadow = true;
meh.castShadow = true;
meh.renderOrder = 1;
2022-01-06 09:38:04 -05:00
// sets fallback opacity for wireframe toggle
this.opacity(1);
// this ref allows clicks to be traced to models and groups
meh.model = this;
// sync data to worker
worker.model_load({id: this.id, name: this.file, vertices, indices});
}
2022-01-05 09:29:27 -05:00
reload(vertices, indices, normals) {
2022-01-11 14:41:49 -05:00
let was = this.wireframe(false);
let geo = this.mesh.geometry;
2022-01-12 11:46:10 -05:00
geo.setAttribute('position', new BufferAttribute(vertices, 3));
geo.setAttribute('normal', undefined);
2022-01-12 11:46:10 -05:00
if (indices) geo.setIndex(new BufferAttribute(indices, 1));
geo.attributes.position.needsUpdate = true;
2022-01-05 09:29:27 -05:00
if (!normals) geo.computeVertexNormals();
// sync data to worker
worker.model_load({id: this.id, name: this.name, vertices, indices});
2022-01-11 14:41:49 -05:00
// restore wireframe state
this.wireframe(was);
}
2022-01-04 01:11:13 -05:00
get group() {
return this._group;
}
set group(gv) {
2021-12-30 22:59:46 -05:00
if (gv && this._group && this._group !== gv) {
throw "models can only belong to one group";
}
this._group = gv;
}
2021-12-30 22:59:46 -05:00
get attributes() {
return this.mesh.geometry.attributes;
}
get vertices() {
return this.attributes.position.count;
}
get faces() {
return this.vertices / 3;
}
2022-01-11 14:41:49 -05:00
// todo -- put model into its own group
ungroup() {
// get current world coordinates
let { mid } = meh.getBoundingBox();
// transform mesh into world coordinates
// move mesh to origin
// create and add to group
// move group center back to original center
geo.moveMesh(-mid.x, -mid.y, -mid.z);
geo.computeBoundingBox();
this.move(mid.x, mid.y, mid.z);
}
2022-01-06 09:38:04 -05:00
visible(bool) {
if (bool === undefined) {
return this.mesh.visible;
}
if (bool.toggle) {
return this.visible(!this.mesh.visible);
}
this.mesh.visible = bool;
}
material(mat) {
let op = this.opacity();
this.mesh.material = mat = mat.clone();
mat.opacity = op;
if (op === 1) this.wireframe(false);
}
opacity(ov, opt = {}) {
2021-12-30 23:23:45 -05:00
let mat = this.mesh.material;
if (ov === undefined) {
return mat.opacity;
}
2022-01-06 09:38:04 -05:00
if (ov.restore) {
ov = this._op;
} else if (ov.temp !== undefined) {
ov = ov.temp;
} else {
this._op = ov;
}
2021-12-30 23:23:45 -05:00
if (ov <= 0.0) {
mat.transparent = false;
mat.opacity = 1;
mat.visible = false;
} else {
mat.transparent = true;
2021-12-30 23:23:45 -05:00
mat.opacity = ov;
mat.visible = true;
}
space.update();
2021-12-30 23:23:45 -05:00
}
wireframe(bool, opt = {}) {
2021-12-30 23:28:36 -05:00
if (bool === undefined) {
return this._wire ? {
enabled: true,
2022-01-06 09:38:04 -05:00
opacity: this.opacity(),
2021-12-30 23:28:36 -05:00
color: this._wire ? this._wire.material.color : undefined,
} : {
enabled: false
};
}
if (bool.toggle) {
return this.wireframe(this._wire ? false : true, opt);
}
2022-01-11 14:41:49 -05:00
let was = this._wire ? true : false;
if (was === bool) {
return was;
}
2021-12-30 23:23:45 -05:00
if (this._wire) {
this.mesh.remove(this._wire);
this._wire = undefined;
2022-01-06 09:38:04 -05:00
this.opacity({restore: true});
2021-12-30 23:23:45 -05:00
}
if (bool) {
2022-01-12 11:46:10 -05:00
this._wire = new Mesh(this.mesh.geometry.shallowClone(), materials.wireframe);
2021-12-30 23:23:45 -05:00
this.mesh.add(this._wire);
2022-01-06 09:38:04 -05:00
this.opacity({temp: opt.opacity || 0});
2021-12-30 23:23:45 -05:00
}
space.update();
2022-01-11 14:41:49 -05:00
return was;
2021-12-30 23:23:45 -05:00
}
2021-12-30 22:59:46 -05:00
remove() {
if (arguments.length === 0) {
// direct call requires pass through group
this.group.remove(this);
this.group = undefined;
this.removed = 'pending';
} else {
// manage lifecycle with worker, mesh app caches, etc
this.destroy();
// tag removed for debugging
this.removed = 'complete';
}
2021-12-30 22:59:46 -05:00
}
updateBoundsBox() {
if (this.group)
mesh.util.defer(this.group.deferUBB);
}
};
})();