add simple mesh/group list
This commit is contained in:
parent
eb2fbf2a09
commit
930e591c44
7 changed files with 149 additions and 29 deletions
|
|
@ -5,6 +5,7 @@
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
let broker = gapp.broker;
|
let broker = gapp.broker;
|
||||||
|
let call = broker.send;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
let moto = self.moto,
|
let moto = self.moto,
|
||||||
|
|
@ -36,22 +37,78 @@ function init() {
|
||||||
});
|
});
|
||||||
space.view.setZoom(zoomrev, zoomspd);
|
space.view.setZoom(zoomrev, zoomspd);
|
||||||
|
|
||||||
// trigger space event binding
|
|
||||||
broker.send.space_init({ space, platform });
|
|
||||||
|
|
||||||
// start worker
|
// start worker
|
||||||
moto.client.start(`/code/mesh_work?${gapp.version}`);
|
moto.client.start(`/code/mesh_work?${gapp.version}`);
|
||||||
|
|
||||||
// set app version
|
// trigger space event binding
|
||||||
$h('app-name', "Mesh:Tool");
|
call.space_init({ space, platform });
|
||||||
$h('app-vers', gapp.version);
|
|
||||||
|
// trigger ui building
|
||||||
|
call.ui_build();
|
||||||
|
|
||||||
// hide loading curtain
|
// hide loading curtain
|
||||||
$d('curtain','none');
|
$d('curtain','none');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ui_build() {
|
||||||
|
// set app version
|
||||||
|
$h('app-name', "Mesh:Tool");
|
||||||
|
$h('app-vers', gapp.version);
|
||||||
|
|
||||||
|
let bound = h.bind($('app-body'), [
|
||||||
|
h.div({id: 'grouplist'})
|
||||||
|
]);
|
||||||
|
|
||||||
|
let { grouplist } = bound;
|
||||||
|
let api = mesh.api;
|
||||||
|
|
||||||
|
let maker = {
|
||||||
|
update() {
|
||||||
|
clearTimeout(maker.timer);
|
||||||
|
maker.timer = setTimeout(maker.do, 10);
|
||||||
|
},
|
||||||
|
do() {
|
||||||
|
let groups = api.group.list()
|
||||||
|
// map groups to divs
|
||||||
|
.map(g => h.div([
|
||||||
|
h.button({
|
||||||
|
_: g.id,
|
||||||
|
class: [
|
||||||
|
"group",
|
||||||
|
api.selection.contains(g) ? 'selected' : undefined
|
||||||
|
],
|
||||||
|
onclick() { api.selection.toggle(g); }
|
||||||
|
}),
|
||||||
|
h.div({ class: "vsep" }),
|
||||||
|
h.div({ class: "models"},
|
||||||
|
// map models to buttons
|
||||||
|
g.models.map(m => h.button({
|
||||||
|
_: m.id,
|
||||||
|
class: api.selection.contains(m) ? [ 'selected' ] : [],
|
||||||
|
onclick() { api.selection.toggle(m); }
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
]));
|
||||||
|
h.bind(grouplist, groups);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function update(object, topic) {
|
||||||
|
console.log({update: object, topic});
|
||||||
|
}
|
||||||
|
|
||||||
|
// listen for api calls
|
||||||
|
broker.listeners({
|
||||||
|
model_add: maker.update,
|
||||||
|
group_add: maker.update,
|
||||||
|
model_remove: maker.update,
|
||||||
|
group_remove: maker.update,
|
||||||
|
selection_update: maker.update,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// add space event bindings
|
// add space event bindings
|
||||||
broker.subscribe('space_init', data => {
|
function space_init(data) {
|
||||||
let { space, platform } = data;
|
let { space, platform } = data;
|
||||||
let platcolor = 0x00ff00;
|
let platcolor = 0x00ff00;
|
||||||
let api = mesh.api;
|
let api = mesh.api;
|
||||||
|
|
@ -64,7 +121,7 @@ broker.subscribe('space_init', data => {
|
||||||
platform.setColor(platcolor);
|
platform.setColor(platcolor);
|
||||||
load.File.load([...evt.dataTransfer.files])
|
load.File.load([...evt.dataTransfer.files])
|
||||||
.then(data => {
|
.then(data => {
|
||||||
broker.send.space_load(data);
|
call.space_load(data);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
dbug.error(error);
|
dbug.error(error);
|
||||||
|
|
@ -127,7 +184,7 @@ broker.subscribe('space_init', data => {
|
||||||
m.debug();
|
m.debug();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
broker.send.space_debug();
|
call.space_debug();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'Escape':
|
case 'Escape':
|
||||||
|
|
@ -199,20 +256,19 @@ broker.subscribe('space_init', data => {
|
||||||
return api.objects().length > 0;
|
return api.objects().length > 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// add object loader
|
// add object loader
|
||||||
broker.subscribe('space_load', data => {
|
function space_load(data) {
|
||||||
mesh.api.group.new(data.flat().map(el => new mesh.model(el)))
|
mesh.api.group.new(data.flat().map(el => new mesh.model(el)))
|
||||||
.centerModels()
|
.centerModels()
|
||||||
.centerXY()
|
.centerXY()
|
||||||
.floor()
|
.floor()
|
||||||
.focus();
|
.focus();
|
||||||
});
|
}
|
||||||
|
|
||||||
// on debug key press
|
// on debug key press
|
||||||
broker.subscribe('space_debug', () => {
|
function space_debug() {
|
||||||
for (let g of mesh.api.group.list()) {
|
for (let g of mesh.api.group.list()) {
|
||||||
let { center, size } = g.bounds();
|
let { center, size } = g.bounds();
|
||||||
console.group(g.id);
|
console.group(g.id);
|
||||||
|
|
@ -230,6 +286,14 @@ broker.subscribe('space_debug', () => {
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
}
|
}
|
||||||
moto.client.fn.debug();
|
moto.client.fn.debug();
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind functions to topics
|
||||||
|
broker.listeners({
|
||||||
|
ui_build,
|
||||||
|
space_init,
|
||||||
|
space_load,
|
||||||
|
space_debug,
|
||||||
});
|
});
|
||||||
|
|
||||||
// remove version cache bust from url
|
// remove version cache bust from url
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,10 @@ let selection = {
|
||||||
return mdl;
|
return mdl;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
contains(object) {
|
||||||
|
return selected.contains(object);
|
||||||
|
},
|
||||||
|
|
||||||
// @param group {MeshObject[]}
|
// @param group {MeshObject[]}
|
||||||
set(objects) {
|
set(objects) {
|
||||||
selected = objects;
|
selected = objects;
|
||||||
|
|
@ -82,6 +86,9 @@ let selection = {
|
||||||
for (let group of groups) {
|
for (let group of groups) {
|
||||||
group.material(mesh.material.unselected);
|
group.material(mesh.material.unselected);
|
||||||
}
|
}
|
||||||
|
// prevent selection of model and its group
|
||||||
|
let mgsel = selected.filter(s => s instanceof mesh.model).map(m => m.group);
|
||||||
|
selected = selected.filter(sel => !mgsel.contains(sel));
|
||||||
for (let object of selected) {
|
for (let object of selected) {
|
||||||
object.material(mesh.material.selected);
|
object.material(mesh.material.selected);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ gapp.register("mesh.group", [
|
||||||
"add.array", // dep: add.array
|
"add.array", // dep: add.array
|
||||||
"add.three", // dep: add.three
|
"add.three", // dep: add.three
|
||||||
"moto.license", // dep: moto.license
|
"moto.license", // dep: moto.license
|
||||||
|
"moto.broker", // dep: moto.broker
|
||||||
"mesh.object", // dep: mesh.object
|
"mesh.object", // dep: mesh.object
|
||||||
"mesh.model", // dep: mesh.model
|
"mesh.model", // dep: mesh.model
|
||||||
"mesh.api", // dep: mesh.api
|
"mesh.api", // dep: mesh.api
|
||||||
|
|
@ -16,12 +17,15 @@ gapp.register("mesh.group", [
|
||||||
let mesh = self.mesh = self.mesh || {};
|
let mesh = self.mesh = self.mesh || {};
|
||||||
if (mesh.group) return;
|
if (mesh.group) return;
|
||||||
|
|
||||||
|
let broker = gapp.broker;
|
||||||
|
let call = broker.send;
|
||||||
|
|
||||||
let space = moto.Space;
|
let space = moto.Space;
|
||||||
let worker = moto.client.fn;
|
let worker = moto.client.fn;
|
||||||
|
|
||||||
mesh.group = class MeshGroup extends mesh.object {
|
mesh.group = class MeshGroup extends mesh.object {
|
||||||
|
|
||||||
// @param group {mesh.model[]}
|
// @param group {MeshModel[]}
|
||||||
constructor(models = []) {
|
constructor(models = []) {
|
||||||
super();
|
super();
|
||||||
this.group = new THREE.Group();
|
this.group = new THREE.Group();
|
||||||
|
|
@ -45,6 +49,7 @@ mesh.group = class MeshGroup extends mesh.object {
|
||||||
this.models.addOnce(model);
|
this.models.addOnce(model);
|
||||||
this.group.add(model.mesh);
|
this.group.add(model.mesh);
|
||||||
space.update();
|
space.update();
|
||||||
|
call.model_add({model, group:this});
|
||||||
worker.group_add({id: this.id, model:model.id});
|
worker.group_add({id: this.id, model:model.id});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
@ -61,6 +66,8 @@ mesh.group = class MeshGroup extends mesh.object {
|
||||||
model.group = undefined;
|
model.group = undefined;
|
||||||
this.models.remove(model);
|
this.models.remove(model);
|
||||||
this.group.remove(model.mesh);
|
this.group.remove(model.mesh);
|
||||||
|
// create message for listeners
|
||||||
|
call.model_remove({model, group:this});
|
||||||
// trigger sync with worker
|
// trigger sync with worker
|
||||||
worker.group_remove({id: this.id, model:model.id});
|
worker.group_remove({id: this.id, model:model.id});
|
||||||
model.remove(true);
|
model.remove(true);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ gapp.finalize("mesh.work", [
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// compensation for space/world/platform rotation
|
// compensation for space/world/platform rotation
|
||||||
let space_rotation = new THREE.Matrix4().makeRotationX(Math.PI / 2);
|
let core_matrix = new THREE.Matrix4().makeRotationX(Math.PI / 2);
|
||||||
|
|
||||||
let { client, worker } = moto;
|
let { client, worker } = moto;
|
||||||
let cache = {};
|
let cache = {};
|
||||||
|
|
@ -38,14 +38,14 @@ let model = {
|
||||||
let geo = new THREE.BufferGeometry();
|
let geo = new THREE.BufferGeometry();
|
||||||
geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
|
geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
|
||||||
geo.computeVertexNormals();
|
geo.computeVertexNormals();
|
||||||
cacheUpdate(id, { name, geo });
|
cacheUpdate(id, { name, geo, matrix: core_matrix.clone() });
|
||||||
},
|
},
|
||||||
|
|
||||||
debug(data) {
|
debug(data) {
|
||||||
let { matrix, id } = data;
|
let { matrix, id } = data;
|
||||||
let rec = cache[id];
|
let rec = cache[id];
|
||||||
let geo = rec.geo.clone();
|
let geo = rec.geo.clone();
|
||||||
let m4 = space_rotation.clone().multiply( new THREE.Matrix4().fromArray(matrix) );
|
let m4 = core_matrix.clone().multiply( new THREE.Matrix4().fromArray(matrix) );
|
||||||
geo.applyMatrix4(m4);
|
geo.applyMatrix4(m4);
|
||||||
// for debugging state / matrix ops
|
// for debugging state / matrix ops
|
||||||
return geo.attributes.position.array;
|
return geo.attributes.position.array;
|
||||||
|
|
@ -59,11 +59,6 @@ let group = {
|
||||||
|
|
||||||
remove(data) {
|
remove(data) {
|
||||||
let { id, model } = data;
|
let { id, model } = data;
|
||||||
},
|
|
||||||
|
|
||||||
create(data) {
|
|
||||||
let { id, type } = data;
|
|
||||||
cache[id] = { id, type };
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -78,23 +73,23 @@ let object = {
|
||||||
},
|
},
|
||||||
|
|
||||||
move(data) {
|
move(data) {
|
||||||
let { id, type, x, y, z } = data;
|
let { id, x, y, z } = data;
|
||||||
},
|
},
|
||||||
|
|
||||||
position(data) {
|
position(data) {
|
||||||
let { id, type, x, y, z } = data;
|
let { id, x, y, z } = data;
|
||||||
},
|
},
|
||||||
|
|
||||||
rotate(data) {
|
rotate(data) {
|
||||||
let { id, type, x, y, z } = data;
|
let { id, x, y, z } = data;
|
||||||
},
|
},
|
||||||
|
|
||||||
rotation(data) {
|
rotation(data) {
|
||||||
let { id, type, x, y, z } = data;
|
let { id, x, y, z } = data;
|
||||||
},
|
},
|
||||||
|
|
||||||
qrotation(data) {
|
qrotation(data) {
|
||||||
let { id, type, w, x, y, z } = data;
|
let { id, w, x, y, z } = data;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,20 @@ gapp.broker = new class Broker {
|
||||||
return Object.keys(this.topics);
|
return Object.keys(this.topics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create subscriptions for all functions in an object
|
||||||
|
// a complement wrapObject on the subscription side
|
||||||
|
listeners(object, root) {
|
||||||
|
for (let [key, fn] of Object.entries(object)) {
|
||||||
|
if (typeof fn === 'function') {
|
||||||
|
key = root ? `${root}_${key}` : key;
|
||||||
|
this.subscribe(key, fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attach function to a topic
|
||||||
|
// creates topic if new topic
|
||||||
|
// creates a send function if new topic
|
||||||
subscribe(topic, listener) {
|
subscribe(topic, listener) {
|
||||||
if (Array.isArray(topic)) {
|
if (Array.isArray(topic)) {
|
||||||
for (let t of topic) {
|
for (let t of topic) {
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,9 @@ function build(data, context) {
|
||||||
func[key] = val;
|
func[key] = val;
|
||||||
elid = elid || `_${nextid++}`;
|
elid = elid || `_${nextid++}`;
|
||||||
} else if (tov === 'object') {
|
} else if (tov === 'object') {
|
||||||
html.push(` ${key}="${val.join(' ')}"`);
|
if (val.length)
|
||||||
} else {
|
html.push(` ${key}="${val.filter(v => v !== undefined).join(' ')}"`);
|
||||||
|
} else if (tov !== 'undefined') {
|
||||||
html.push(` ${key}="${val}"`);
|
html.push(` ${key}="${val}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,3 +140,35 @@ body, div {
|
||||||
margin-right: -14px !important;
|
margin-right: -14px !important;
|
||||||
margin-bottom: -14px !important;
|
margin-bottom: -14px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#grouplist {
|
||||||
|
position: absolute;
|
||||||
|
margin: 3px 3px 0 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#grouplist, #grouplist div {
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#grouplist > div {
|
||||||
|
border: 1px solid gray;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#grouplist .models button {
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#grouplist .selected {
|
||||||
|
background-color: rgba(0,255,0,0.75)
|
||||||
|
}
|
||||||
|
|
||||||
|
#grouplist .vsep {
|
||||||
|
height: 1px;
|
||||||
|
margin: 2px 0 1px 0;
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue