checkpoint
This commit is contained in:
parent
19e50b2635
commit
7ff172f5f3
7 changed files with 234 additions and 199 deletions
|
|
@ -126,12 +126,10 @@ async function restore_space() {
|
||||||
return { id, md: cached[id] }
|
return { id, md: cached[id] }
|
||||||
})
|
})
|
||||||
.filter(r => r.md) // filter cache misses
|
.filter(r => r.md) // filter cache misses
|
||||||
.map(r => new mesh.model(r.md, r.id)
|
.map(r => new mesh.model(r.md, r.id).applyMeta(mcache[r.id]))
|
||||||
.applyMatrix(mcache[r.id]?.matrix)
|
|
||||||
.visible(mcache[r.id]?.visible ?? true));
|
|
||||||
if (models.length) {
|
if (models.length) {
|
||||||
log(`restored ${models.length} model(s)`);
|
log(`restored ${models.length} model(s)`);
|
||||||
mesh.api.group.new(models, id).applyMatrix(mcache[id]?.matrix)
|
mesh.api.group.new(models, id).applyMeta(mcache[id])
|
||||||
} else {
|
} else {
|
||||||
log(`removed empty group ${id}`);
|
log(`removed empty group ${id}`);
|
||||||
db_space.remove(id);
|
db_space.remove(id);
|
||||||
|
|
@ -237,15 +235,12 @@ function space_init(data) {
|
||||||
case 'KeyA':
|
case 'KeyA':
|
||||||
if (api.mode.is([ api.modes.edge ])) return mesh.edges.add();
|
if (api.mode.is([ api.modes.edge ])) return mesh.edges.add();
|
||||||
return shiftKey && api.tool.analyze();
|
return shiftKey && api.tool.analyze();
|
||||||
// case 'KeyR':
|
|
||||||
// return shiftKey ? api.tool.rebuild() : api.tool.repair();
|
|
||||||
case 'KeyE':
|
case 'KeyE':
|
||||||
if (api.mode.is([ api.modes.sketch ])) {
|
if (api.mode.is([ api.modes.sketch ])) {
|
||||||
estop(evt);
|
estop(evt);
|
||||||
return api.sketch.extrude();
|
return api.sketch.extrude();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
// return api.tool.clean();
|
|
||||||
case 'KeyV':
|
case 'KeyV':
|
||||||
return shiftKey ? selection.show() : selection.focus();
|
return shiftKey ? selection.show() : selection.focus();
|
||||||
case 'KeyN':
|
case 'KeyN':
|
||||||
|
|
@ -335,8 +330,7 @@ function space_init(data) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (rot && floor) {
|
if (rot && floor) {
|
||||||
// todo future pref to auto-floor or not
|
rot.floor();
|
||||||
rot.floor(mesh.group);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
@ -396,7 +390,7 @@ function space_init(data) {
|
||||||
}
|
}
|
||||||
} else if (ctrlKey) {
|
} else if (ctrlKey) {
|
||||||
// rotate selected face towawrd z "floor"
|
// rotate selected face towawrd z "floor"
|
||||||
group.faceDown(int.face.normal);
|
group.rotateTowardZ(int.face.normal);
|
||||||
selection.update();
|
selection.update();
|
||||||
} else {
|
} else {
|
||||||
const { modes } = api;
|
const { modes } = api;
|
||||||
|
|
@ -610,9 +604,9 @@ function object_visible(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache model matrices for page restores
|
// cache model matrices for page restores
|
||||||
function object_matrix(data) {
|
function object_meta(data) {
|
||||||
let { id, matrix } = data;
|
let { id, meta } = data;
|
||||||
update_meta(id, { matrix: matrix.elements });
|
update_meta(id, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
function object_destroy(id) {
|
function object_destroy(id) {
|
||||||
|
|
@ -706,7 +700,7 @@ function set_snap_value(snap) {
|
||||||
// bind functions to topics
|
// bind functions to topics
|
||||||
broker.listeners({
|
broker.listeners({
|
||||||
load_files,
|
load_files,
|
||||||
object_matrix,
|
object_meta,
|
||||||
object_destroy,
|
object_destroy,
|
||||||
object_visible,
|
object_visible,
|
||||||
space_init,
|
space_init,
|
||||||
|
|
|
||||||
|
|
@ -253,14 +253,14 @@ const selection = {
|
||||||
|
|
||||||
floor() {
|
floor() {
|
||||||
for (let s of selection.groups()) {
|
for (let s of selection.groups()) {
|
||||||
s.floor(...arguments);
|
s.floor();
|
||||||
}
|
}
|
||||||
return selection;
|
return selection;
|
||||||
},
|
},
|
||||||
|
|
||||||
centerXY() {
|
centerXY() {
|
||||||
for (let s of [...selection.groups(), ...selection.sketches()]) {
|
for (let s of [...selection.groups(), ...selection.sketches()]) {
|
||||||
s.centerXY(...arguments);
|
s.centerXY();
|
||||||
}
|
}
|
||||||
return selection;
|
return selection;
|
||||||
},
|
},
|
||||||
|
|
@ -1096,17 +1096,10 @@ const tool = {
|
||||||
|
|
||||||
regroup(models) {
|
regroup(models) {
|
||||||
models = fallback(models, true);
|
models = fallback(models, true);
|
||||||
if (models.length === 0) {
|
if (models.length) {
|
||||||
return;
|
log(`regrouping ${models.length} model(s)`);
|
||||||
|
return mesh.api.group.new(models.map(m => m.ungroup()));
|
||||||
}
|
}
|
||||||
log(`regrouping ${models.length} model(s)`);
|
|
||||||
let bounds = util.bounds(models);
|
|
||||||
let { mid } = bounds;
|
|
||||||
return Promise.all(models.map(m => m.ungroup())).then(() => {
|
|
||||||
return mesh.api.group.new(models)
|
|
||||||
.centerModels()
|
|
||||||
.position(mid.x, mid.y, mid.z);;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
analyze(models, opt = { compound: true }) {
|
analyze(models, opt = { compound: true }) {
|
||||||
|
|
@ -1410,8 +1403,8 @@ const api = exports({
|
||||||
let left, up;
|
let left, up;
|
||||||
if (normal) {
|
if (normal) {
|
||||||
let { x, y, z } = normal;
|
let { x, y, z } = normal;
|
||||||
left = new Vector3(x,y,z).angleTo(new Vector3(0,-1,0));
|
left = new Vector3(x,y,0).angleTo(new Vector3(0,-1,0));
|
||||||
up = new Vector3(x,y,z).angleTo(new Vector3(0,0,1));
|
up = new Vector3(0,y,z).angleTo(new Vector3(0,0,1));
|
||||||
if (x < 0) left = -left;
|
if (x < 0) left = -left;
|
||||||
}
|
}
|
||||||
// sets "home" views (front, back, home, reset)
|
// sets "home" views (front, back, home, reset)
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,12 @@
|
||||||
gapp.register("mesh.group", [], (root, exports) => {
|
gapp.register("mesh.group", [], (root, exports) => {
|
||||||
|
|
||||||
const { mesh, moto } = root;
|
const { mesh, moto } = root;
|
||||||
const { Quaternion, Group, Vector3 } = THREE;
|
const { Group, Box3 } = THREE;
|
||||||
const { broker } = gapp;
|
const { broker } = gapp;
|
||||||
const { space } = moto;
|
const { space } = moto;
|
||||||
|
|
||||||
const call = broker.send;
|
const call = broker.send;
|
||||||
const worker = moto.client.fn;
|
const worker = moto.client.fn;
|
||||||
const lookUp = new Vector3(0,0,-1);
|
|
||||||
|
|
||||||
mesh.group = class MeshGroup extends mesh.object {
|
mesh.group = class MeshGroup extends mesh.object {
|
||||||
|
|
||||||
|
|
@ -42,6 +41,14 @@ mesh.group = class MeshGroup extends mesh.object {
|
||||||
return this.group3;
|
return this.group3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get bounds() {
|
||||||
|
let box3 = new Box3();
|
||||||
|
for (let model of this.models) {
|
||||||
|
box3.union(model.bounds.translate(model.position()));
|
||||||
|
}
|
||||||
|
return box3;
|
||||||
|
}
|
||||||
|
|
||||||
// @param model {MeshModel}
|
// @param model {MeshModel}
|
||||||
add(model) {
|
add(model) {
|
||||||
model.group = this;
|
model.group = this;
|
||||||
|
|
@ -58,7 +65,6 @@ mesh.group = class MeshGroup extends mesh.object {
|
||||||
// @param model {MeshModel}
|
// @param model {MeshModel}
|
||||||
// @param free {boolean} prevent cleanup of model so it can be re-used
|
// @param free {boolean} prevent cleanup of model so it can be re-used
|
||||||
remove(model, opt = { free: true }) {
|
remove(model, opt = { free: true }) {
|
||||||
// console.log('group',this,'remove',model);
|
|
||||||
// remove all models and group
|
// remove all models and group
|
||||||
if (arguments.length === 0) {
|
if (arguments.length === 0) {
|
||||||
for (let m of this.models.slice()) {
|
for (let m of this.models.slice()) {
|
||||||
|
|
@ -87,13 +93,6 @@ mesh.group = class MeshGroup extends mesh.object {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// rotate model so face normal points toward floor
|
|
||||||
faceDown(normal) {
|
|
||||||
let q = new Quaternion().setFromUnitVectors(normal, lookUp);
|
|
||||||
this.qrotation(q);
|
|
||||||
this.floor();
|
|
||||||
}
|
|
||||||
|
|
||||||
promote() {
|
promote() {
|
||||||
this.centerModels();
|
this.centerModels();
|
||||||
let { dim, mid } = this.bounds;
|
let { dim, mid } = this.bounds;
|
||||||
|
|
@ -110,34 +109,48 @@ mesh.group = class MeshGroup extends mesh.object {
|
||||||
}
|
}
|
||||||
|
|
||||||
rotate(x = 0, y = 0, z = 0) {
|
rotate(x = 0, y = 0, z = 0) {
|
||||||
console.log('group rotate', ...arguments);
|
this.log('group-rotate', ...arguments);
|
||||||
for (let model of this.models) {
|
for (let model of this.models) {
|
||||||
model.rotate(...arguments);
|
model.rotate(...arguments);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
qrotate(quaternion) {
|
||||||
|
this.log('group-rotate', quaternion.toArray());
|
||||||
|
for (let model of this.models) {
|
||||||
|
model.qrotate(quaternion);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
scale(x = 1, y = 1, z = 1) {
|
scale(x = 1, y = 1, z = 1) {
|
||||||
|
this.log('group-scale', ...arguments);
|
||||||
for (let m of this.models) {
|
for (let m of this.models) {
|
||||||
m.scale(x, y, z);
|
m.scale(x, y, z);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// center objects to group bounds
|
move(x = 0, y = 0, z = 0) {
|
||||||
// dependent on first being added to world/scene
|
this.log('group-move', ...arguments);
|
||||||
centerModels() {
|
if (!(x || y || z)) return;
|
||||||
let bounds = this.bounds;
|
|
||||||
for (let model of this.models) {
|
for (let model of this.models) {
|
||||||
model.center(bounds);
|
model.move(x, y, z);
|
||||||
}
|
|
||||||
let { center } = mesh.api.prefs.map.space;
|
|
||||||
if (center === false) {
|
|
||||||
let { mid } = bounds;
|
|
||||||
this.move(mid.x, mid.y, mid.z);
|
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
center() {
|
||||||
|
let b = this.bounds;
|
||||||
|
return this.move(-b.mid.x, -b.mid.y, -b.mid.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
centerXY() {
|
||||||
|
let b = this.bounds;
|
||||||
|
return this.move(-b.mid.x, -b.mid.y, 0);
|
||||||
|
}
|
||||||
|
|
||||||
opacity() {
|
opacity() {
|
||||||
for (let model of this.models) {
|
for (let model of this.models) {
|
||||||
model.opacity(...arguments);
|
model.opacity(...arguments);
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,15 @@
|
||||||
gapp.register("mesh.model", [], (root, exports) => {
|
gapp.register("mesh.model", [], (root, exports) => {
|
||||||
|
|
||||||
const { MeshPhongMaterial, MeshBasicMaterial, LineBasicMaterial } = THREE;
|
const { MeshPhongMaterial, MeshBasicMaterial, LineBasicMaterial } = THREE;
|
||||||
const { BufferGeometry, BufferAttribute, DoubleSide, Mesh } = THREE;
|
const { BufferGeometry, BufferAttribute, DoubleSide, Mesh} = THREE;
|
||||||
|
const { Quaternion, Box3, Vector3 } = THREE;
|
||||||
const { mesh, moto } = root;
|
const { mesh, moto } = root;
|
||||||
const { space } = moto;
|
const { space } = moto;
|
||||||
const { api } = mesh;
|
const { api } = mesh;
|
||||||
|
|
||||||
const mapp = mesh;
|
const mapp = mesh;
|
||||||
const worker = moto.client.fn;
|
const worker = moto.client.fn;
|
||||||
|
const zero = new Vector3(0,0,0);
|
||||||
|
|
||||||
/** default materials **/
|
/** default materials **/
|
||||||
let materials = mesh.material = {
|
let materials = mesh.material = {
|
||||||
|
|
@ -122,6 +124,10 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
return this.mesh;
|
return this.mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get bounds() {
|
||||||
|
return this.geometry.boundingBox.clone();
|
||||||
|
}
|
||||||
|
|
||||||
get matrix() {
|
get matrix() {
|
||||||
return this.mesh.matrixWorld.elements;
|
return this.mesh.matrixWorld.elements;
|
||||||
}
|
}
|
||||||
|
|
@ -166,40 +172,82 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rotate(x = 0, y = 0, z = 0) {
|
qrotate(quaternion) {
|
||||||
console.log('model rotate', ...arguments);
|
this.log('model-rotate', quaternion.toArray());
|
||||||
worker.model_rotate({ id: this.id, x, y, z }).then(() => {
|
this.geometry.applyQuaternion(quaternion);
|
||||||
this.attributes.position.needsUpdate = true;
|
this.updateBounds();
|
||||||
moto.space.update();
|
this.persist();
|
||||||
});
|
moto.space.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// override and translate mesh
|
scale(x = 1, y = 1, z = 1) {
|
||||||
xmove(x = 0, y = 0, z = 0) {
|
this.log('model-scale', ...arguments, this.bounds);
|
||||||
let attr = this.attributes;
|
if (x === 1 && y === 1 && z === 1) return;
|
||||||
let arr = attr.position.array;
|
this.geometry.scale(x, y, z);
|
||||||
for (let i=0, l=arr.length; i<l; ) {
|
this.updateBounds();
|
||||||
arr[i] = arr[i++] + x;
|
this.persist();
|
||||||
arr[i] = arr[i++] + y;
|
moto.space.update();
|
||||||
arr[i] = arr[i++] + z;
|
}
|
||||||
|
|
||||||
|
translate(x = 0, y = 0, z = 0) {
|
||||||
|
this.log('model-translate', ...arguments);
|
||||||
|
if (!(x || y || z)) return;
|
||||||
|
this.geometry.translate(x, y, z);
|
||||||
|
this.updateBounds();
|
||||||
|
this.persist();
|
||||||
|
}
|
||||||
|
|
||||||
|
move(x = 0, y = 0, z = 0) {
|
||||||
|
this.log('model-move', ...arguments);
|
||||||
|
if (!(x || y || z)) return;
|
||||||
|
let pos = this.position();
|
||||||
|
return this.position(pos.x + x, pos.y + y, pos.z + z);
|
||||||
|
}
|
||||||
|
|
||||||
|
position() {
|
||||||
|
let pos = this.object.position;
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
return pos;
|
||||||
}
|
}
|
||||||
this.reload(arr, attr.index ? attr.index.array : undefined);
|
pos.set(...arguments);
|
||||||
|
this.metaChanged();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// override and translate mesh
|
// preserves world location while updating mesh for rotation and scaling
|
||||||
xscale(x = 1, y = 1, z = 1) {
|
// moves mesh center to 0,0,0 via translation then
|
||||||
let attr = this.attributes;
|
// moves mesh object to former bounds center
|
||||||
let arr = attr.position.array;
|
reCenter() {
|
||||||
for (let i=0, l=arr.length; i<l; ) {
|
this.log('model-center');
|
||||||
arr[i] = arr[i++] *= x;
|
let pos = this.position();
|
||||||
arr[i] = arr[i++] *= y;
|
let { mid } = this.bounds;
|
||||||
arr[i] = arr[i++] *= z;
|
let moveTo = mid.clone().add(pos);
|
||||||
}
|
this.translate(-mid.x, -mid.y, -mid.z);
|
||||||
this.reload(arr, attr.index ? attr.index.array : undefined);
|
this.position(moveTo.x, moveTo.y, moveTo.z);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// preserves world location while updating mesh for rotation and scaling
|
||||||
|
// moves mesh center to x,y,z via translation then
|
||||||
|
// moves mesh object to former bounds center
|
||||||
|
centerTo(pos) {
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBounds() {
|
||||||
|
if (this._wire)
|
||||||
|
this._wire.geometry.attributes.position.needsUpdate = true;
|
||||||
|
this.attributes.position.needsUpdate = true;
|
||||||
|
this.geometry.computeBoundingBox();
|
||||||
|
this.geometry.computeBoundingSphere();
|
||||||
|
this.log('update-bounds', this.geometry.boundingBox);
|
||||||
|
this.updateBoundsBox();
|
||||||
|
moto.space.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBoundsBox() {
|
||||||
|
this.group?.updateBoundsBox();
|
||||||
|
}
|
||||||
|
|
||||||
mirror() {
|
mirror() {
|
||||||
return this.duplicate({ mirror: true });
|
return this.duplicate({ mirror: true });
|
||||||
}
|
}
|
||||||
|
|
@ -209,27 +257,24 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
// defaults to returning a new model in a new group
|
// defaults to returning a new model in a new group
|
||||||
// options to mirror, re-use a group, or update model in-place
|
// options to mirror, re-use a group, or update model in-place
|
||||||
duplicate(opt = { select: true }) {
|
duplicate(opt = { select: true }) {
|
||||||
return worker.model_duplicate({
|
let pos = this.position();
|
||||||
matrix: this.matrix,
|
let data = this.attributes.position.clone().array;
|
||||||
id: this.id,
|
if (opt.append) data = [...data, ...opt.append].toFloat32();
|
||||||
opt: { mirror: opt.mirror }
|
if (opt.x) return this.reload(data);
|
||||||
}).then(data => {
|
let model = new mesh.model({ file: `${this.file}`, mesh: data });
|
||||||
if (opt.append) data = [...data, ...opt.append].toFloat32();
|
let group = opt.group || mesh.api.group.new();
|
||||||
if (opt.x) return this.reload(data);
|
group.add(model);
|
||||||
let model = new mesh.model({ file: `${this.file}`, mesh: data });
|
model.position(pos.x, pos.y, pos.z);
|
||||||
let group = opt.group || mesh.api.group.new();
|
model.wireframe(this.wireframe());
|
||||||
group.add(model);
|
if (opt.select) {
|
||||||
model.wireframe(this.wireframe());
|
api.selection.add(model);
|
||||||
if (opt.select) {
|
}
|
||||||
api.selection.add(model);
|
if (opt.mirror) {
|
||||||
}
|
group.move(0, 0, group.bounds.dim.z);
|
||||||
if (opt.mirror) {
|
} else if (opt.shift) {
|
||||||
group.move(0, 0, group.bounds.dim.z);
|
group.move(group.bounds.dim.x, 0, 0);
|
||||||
} else if (opt.shift) {
|
}
|
||||||
group.move(group.bounds.dim.x, 0, 0);
|
return model;
|
||||||
}
|
|
||||||
return model;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rebuild(opt = {}) {
|
rebuild(opt = {}) {
|
||||||
|
|
@ -259,11 +304,9 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
}
|
}
|
||||||
|
|
||||||
load(vertices) {
|
load(vertices) {
|
||||||
let shared = mesh.util.toSharedF32(vertices);
|
this.log('load');
|
||||||
let local = mesh.util.toLocal32(vertices);
|
|
||||||
console.log('load', { vertices, local, shared });
|
|
||||||
let geo = new BufferGeometry();
|
let geo = new BufferGeometry();
|
||||||
geo.setAttribute('position', new BufferAttribute(shared, 3));
|
geo.setAttribute('position', new BufferAttribute(vertices, 3));
|
||||||
let meh = this.mesh = new Mesh(geo, [
|
let meh = this.mesh = new Mesh(geo, [
|
||||||
this.mats.normal,
|
this.mats.normal,
|
||||||
this.mats.face
|
this.mats.face
|
||||||
|
|
@ -277,39 +320,49 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
// this ref allows clicks to be traced to models and groups
|
// this ref allows clicks to be traced to models and groups
|
||||||
meh.model = this;
|
meh.model = this;
|
||||||
// persist in db so it can be restored on page load
|
// persist in db so it can be restored on page load
|
||||||
mapp.db.space.put(this.id, { file: this.file, mesh: local });
|
this.persist();
|
||||||
// sync data to worker
|
// sync data to worker
|
||||||
worker.model_load({ id: this.id, name: this.file, vertices: shared });
|
worker.model_load({ id: this.id, name: this.file, vertices });
|
||||||
|
// update bounds
|
||||||
|
this.updateBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
reload(vertices) {
|
reload(vertices) {
|
||||||
let local = mesh.util.toLocal32(vertices);
|
this.log('reload');
|
||||||
let shared = mesh.util.toSharedF32(vertices);
|
|
||||||
console.trace('reload', { vertices, local, shared });
|
|
||||||
let was = this.wireframe(false);
|
let was = this.wireframe(false);
|
||||||
let geo = this.mesh.geometry;
|
let geo = this.mesh.geometry;
|
||||||
geo.setAttribute('position', new BufferAttribute(shared, 3));
|
geo.setAttribute('position', new BufferAttribute(vertices, 3));
|
||||||
// signal util.box3expand that geometry changed
|
// signal util.box3expand that geometry changed
|
||||||
geo._model_invalid = true;
|
geo._model_invalid = true;
|
||||||
geo.computeVertexNormals();
|
geo.computeVertexNormals();
|
||||||
// allows raycasting to work
|
// allows raycasting to work
|
||||||
geo.computeBoundingSphere();
|
// geo.computeBoundingSphere();
|
||||||
// persist in db so it can be restored on page load
|
// persist in db so it can be restored on page load
|
||||||
mapp.db.space.put(this.id, { file: this.file, mesh: local });
|
this.persist();
|
||||||
// sync data to worker
|
// sync data to worker
|
||||||
worker.model_load({id: this.id, name: this.name, vertices: shared });
|
worker.model_load({id: this.id, name: this.name, vertices });
|
||||||
// restore wireframe state
|
// restore wireframe state
|
||||||
this.wireframe(was);
|
this.wireframe(was);
|
||||||
// fixup normals
|
// fixup normals
|
||||||
this.normals({refresh: true});
|
this.normals({refresh: true});
|
||||||
// re-gen face index in surface mode
|
// re-gen face index in surface mode
|
||||||
mesh.api.mode.check();
|
mesh.api.mode.check();
|
||||||
|
// allows raycasting to work
|
||||||
|
// update bounds
|
||||||
|
this.updateBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
rename(file) {
|
rename(file) {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
|
this.persist();
|
||||||
|
}
|
||||||
|
|
||||||
|
persist() {
|
||||||
// persist in db so it can be restored on page load
|
// persist in db so it can be restored on page load
|
||||||
mapp.db.space.put(this.id, { file, mesh: this.attributes.position.array });
|
mapp.db.space.put(this.id, {
|
||||||
|
file: this.file,
|
||||||
|
mesh: this.attributes.position.array
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get group() {
|
get group() {
|
||||||
|
|
@ -323,8 +376,12 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
this._group = gv;
|
this._group = gv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get geometry() {
|
||||||
|
return this.mesh.geometry;
|
||||||
|
}
|
||||||
|
|
||||||
get attributes() {
|
get attributes() {
|
||||||
return this.mesh.geometry.attributes;
|
return this.geometry.attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
get vertices() {
|
get vertices() {
|
||||||
|
|
@ -460,6 +517,10 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
// invert normals for entire mesh or selected faces depending on mod
|
// invert normals for entire mesh or selected faces depending on mod
|
||||||
invert(mode) {
|
invert(mode) {
|
||||||
let { modes } = mesh.api;
|
let { modes } = mesh.api;
|
||||||
|
|
@ -529,13 +590,7 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
this.group.remove(this, { free: false });
|
this.group.remove(this, { free: false });
|
||||||
this.group = undefined;
|
this.group = undefined;
|
||||||
}
|
}
|
||||||
return worker.model_duplicate({
|
return this;
|
||||||
matrix: this.matrix,
|
|
||||||
id: this.id,
|
|
||||||
opt: {}
|
|
||||||
}).then(data => {
|
|
||||||
this.reload(data);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove model from group and space
|
// remove model from group and space
|
||||||
|
|
@ -553,12 +608,6 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBoundsBox() {
|
|
||||||
if (this.group) {
|
|
||||||
mesh.util.defer(this.group.deferUBB);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearSelections() {
|
clearSelections() {
|
||||||
// clear face selections (since they've been deleted);
|
// clear face selections (since they've been deleted);
|
||||||
this.sel.faces = [];
|
this.sel.faces = [];
|
||||||
|
|
@ -597,13 +646,6 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
// clear face selections (since they've been deleted);
|
// clear face selections (since they've been deleted);
|
||||||
this.sel.faces = [];
|
this.sel.faces = [];
|
||||||
this.updateSelections();
|
this.updateSelections();
|
||||||
|
|
||||||
// let { modes } = mesh.api;
|
|
||||||
// switch (mode) {
|
|
||||||
// case modes.face:
|
|
||||||
// console.log('delete selected faces');
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSelections() {
|
updateSelections() {
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,11 @@ const { mesh, moto } = root;
|
||||||
const { space } = moto;
|
const { space } = moto;
|
||||||
|
|
||||||
const worker = moto.client.fn;
|
const worker = moto.client.fn;
|
||||||
|
const lookUp = new Vector3(0,0,-1);
|
||||||
|
|
||||||
// broker updates generated by objects
|
// broker updates generated by objects
|
||||||
let publish = {
|
let publish = {
|
||||||
matrix: gapp.broker.bind("object_matrix"),
|
meta: gapp.broker.bind("object_meta"),
|
||||||
destroy: gapp.broker.bind("object_destroy"),
|
destroy: gapp.broker.bind("object_destroy"),
|
||||||
visible: gapp.broker.bind("object_visible"),
|
visible: gapp.broker.bind("object_visible"),
|
||||||
};
|
};
|
||||||
|
|
@ -26,8 +27,14 @@ mesh.object = class MeshObject {
|
||||||
|
|
||||||
constructor(id) {
|
constructor(id) {
|
||||||
this.id = id || mesh.util.uuid();
|
this.id = id || mesh.util.uuid();
|
||||||
this.deferUBB = () => { this.updateBoundsBox() };
|
|
||||||
worker.object_create({id: this.id, type: this.type});
|
worker.object_create({id: this.id, type: this.type});
|
||||||
|
this.log('NEW');
|
||||||
|
// storage location for meta data like position, scale, rotation
|
||||||
|
this.meta = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
log() {
|
||||||
|
console.log(this.id, this.type, ...arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
get type() {
|
get type() {
|
||||||
|
|
@ -40,7 +47,7 @@ mesh.object = class MeshObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
get bounds() {
|
get bounds() {
|
||||||
return mesh.util.bounds(this.object);
|
throw "bounds() requires implementation";
|
||||||
}
|
}
|
||||||
|
|
||||||
// manage lifecycle with worker, mesh app caches, etc
|
// manage lifecycle with worker, mesh app caches, etc
|
||||||
|
|
@ -53,16 +60,26 @@ mesh.object = class MeshObject {
|
||||||
publish.destroy(this.id);
|
publish.destroy(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// used during state restoration
|
||||||
|
applyMeta(meta = {}) {
|
||||||
|
this.log('apply-meta', meta);
|
||||||
|
this.applyMatrix(meta.matrix);
|
||||||
|
this.visible(meta.visible ?? true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// used during state restoration
|
||||||
applyMatrix(elements) {
|
applyMatrix(elements) {
|
||||||
if (elements) {
|
if (elements) {
|
||||||
console.log('matrix', this.type, this.id);
|
this.log('apply-matrix');
|
||||||
this.object.applyMatrix4(new Matrix4().fromArray(elements));
|
this.object.applyMatrix4(new Matrix4().fromArray(elements));
|
||||||
return this.matrixChanged();
|
return this.metaChanged();
|
||||||
} else {
|
} else {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// used during model splitting
|
||||||
applyMatrix4(matrix) {
|
applyMatrix4(matrix) {
|
||||||
return this.applyMatrix(matrix.elements);
|
return this.applyMatrix(matrix.elements);
|
||||||
}
|
}
|
||||||
|
|
@ -93,73 +110,58 @@ mesh.object = class MeshObject {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
floor(clazz) {
|
floor() {
|
||||||
if (!clazz || this instanceof clazz) {
|
let b = this.bounds;
|
||||||
let b = this.bounds;
|
return this.move(0, 0, -b.min.z);
|
||||||
this.move(0, 0, -b.min.z);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
center(bounds) {
|
center() {
|
||||||
let b = bounds || this.bounds;
|
throw "center() requires implementation";
|
||||||
this.move(-b.mid.x, -b.mid.y, -b.mid.z);
|
let b = this.bounds;
|
||||||
return this;
|
return this.move(-b.mid.x, -b.mid.y, -b.mid.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
centerXY(bounds) {
|
centerXY() {
|
||||||
let b = bounds || this.bounds;
|
throw "centerXY() requires implementation";
|
||||||
this.move(-b.mid.x, -b.mid.y, 0);
|
let b = this.bounds;
|
||||||
return this;
|
return this.move(-b.mid.x, -b.mid.y, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
move(x = 0, y = 0, z = 0) {
|
move() {
|
||||||
let pos = this.position();
|
throw "move() requires implementation";
|
||||||
return this.position(pos.x + x, pos.y + y, pos.z + z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scale() {
|
scale() {
|
||||||
let scale = this.object.scale;
|
throw "scale() requires implementation";
|
||||||
if (arguments.length === 0) {
|
}
|
||||||
return scale;
|
|
||||||
}
|
qrotate(quaternion) {
|
||||||
scale.set(...arguments);
|
throw "qrotate() requires implementation";
|
||||||
this.matrixChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rotate(x = 0, y = 0, z = 0) {
|
rotate(x = 0, y = 0, z = 0) {
|
||||||
throw "rotate() requires implementation";
|
this.log('object-rotate', ...arguments);
|
||||||
console.log('object rotate', ...arguments);
|
let m = new Matrix4();
|
||||||
if (x) this.object.rotateOnWorldAxis(new Vector3(1,0,0), x);
|
if (x) m.multiply(new Matrix4().makeRotationX(x));
|
||||||
if (y) this.object.rotateOnWorldAxis(new Vector3(0,1,0), y);
|
if (y) m.multiply(new Matrix4().makeRotationY(y));
|
||||||
if (z) this.object.rotateOnWorldAxis(new Vector3(0,0,1), z);
|
if (z) m.multiply(new Matrix4().makeRotationZ(z));
|
||||||
this.matrixChanged();
|
this.qrotate(new Quaternion().setFromRotationMatrix(m));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
rotation() {
|
rotation() {
|
||||||
throw "rotate() requires implementation";
|
throw "rotation() requires implementation";
|
||||||
let rotation = this.object.rotation;
|
|
||||||
if (arguments.length === 0) {
|
|
||||||
return rotation;
|
|
||||||
}
|
|
||||||
return this.qrotation(new Quaternion().setFromEuler(new Euler(...arguments)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qrotation(quaternion) {
|
// rotate object in the directon of normal (place face on Z plane)
|
||||||
this.object.setRotationFromQuaternion(quaternion);
|
rotateTowardZ(normal) {
|
||||||
this.matrixChanged();
|
let q = new Quaternion().setFromUnitVectors(normal, lookUp);
|
||||||
return this;
|
this.qrotate(q);
|
||||||
|
this.floor();
|
||||||
}
|
}
|
||||||
|
|
||||||
position() {
|
position() {
|
||||||
let pos = this.object.position;
|
throw "position() requires implementation";
|
||||||
if (arguments.length === 0) {
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
pos.set(...arguments);
|
|
||||||
this.matrixChanged();
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showBounds(bool) {
|
showBounds(bool) {
|
||||||
|
|
@ -175,6 +177,7 @@ mesh.object = class MeshObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBoundsBox() {
|
updateBoundsBox() {
|
||||||
|
this.log('update-bounds-box', this._showBounds);
|
||||||
let helper = this._boundsBox;
|
let helper = this._boundsBox;
|
||||||
let world = space.world;
|
let world = space.world;
|
||||||
if (helper) {
|
if (helper) {
|
||||||
|
|
@ -192,12 +195,12 @@ mesh.object = class MeshObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
matrixChanged() {
|
metaChanged() {
|
||||||
if (!this.updateBoundsBox()) {
|
this.object.updateMatrix();
|
||||||
this.object.updateMatrix();
|
this.updateBoundsBox();
|
||||||
}
|
|
||||||
space.update();
|
space.update();
|
||||||
publish.matrix({ id: this.id, matrix: this.object.matrix });
|
this.meta.matrix = this.object.matrix.elements;
|
||||||
|
publish.meta({ id: this.id, meta: this.meta });
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -549,7 +549,7 @@ mesh.sketch = class MeshSketch extends mesh.object {
|
||||||
const euler = this.group.rotation;
|
const euler = this.group.rotation;
|
||||||
const quaternion = new THREE.Quaternion();
|
const quaternion = new THREE.Quaternion();
|
||||||
quaternion.setFromEuler(euler);
|
quaternion.setFromEuler(euler);
|
||||||
ngrp.qrotation(quaternion);
|
ngrp.qrotate(quaternion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ function log(msg) {
|
||||||
mesh.log = log;
|
mesh.log = log;
|
||||||
|
|
||||||
function cacheUpdate(id, data) {
|
function cacheUpdate(id, data) {
|
||||||
Object.assign(cache[id], data);
|
return Object.assign(cache[id], data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// translate original mesh vertices into UI world view (PI/2 rotation on X)
|
// translate original mesh vertices into UI world view (PI/2 rotation on X)
|
||||||
|
|
@ -98,15 +98,6 @@ function indexFaces(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let model = {
|
let model = {
|
||||||
rotate(data) {
|
|
||||||
let { id, x, y, z } = data;
|
|
||||||
let rec = cache[id];
|
|
||||||
let { geo } = rec;
|
|
||||||
if (x) geo.applyQuaternion(new Quaternion().setFromAxisAngle(new Vector3(1,0,0), x));
|
|
||||||
if (y) geo.applyQuaternion(new Quaternion().setFromAxisAngle(new Vector3(0,1,0), y));
|
|
||||||
if (z) geo.applyQuaternion(new Quaternion().setFromAxisAngle(new Vector3(0,0,1), z));
|
|
||||||
},
|
|
||||||
|
|
||||||
load(data) {
|
load(data) {
|
||||||
let { vertices, name, id } = data;
|
let { vertices, name, id } = data;
|
||||||
let geo = new BufferGeometry();
|
let geo = new BufferGeometry();
|
||||||
|
|
@ -114,7 +105,6 @@ let model = {
|
||||||
cacheUpdate(id, {
|
cacheUpdate(id, {
|
||||||
name,
|
name,
|
||||||
geo,
|
geo,
|
||||||
xmatrix: core_matrix.clone(),
|
|
||||||
trans: undefined,
|
trans: undefined,
|
||||||
tool: undefined
|
tool: undefined
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue