add normals visualization
This commit is contained in:
parent
102af46969
commit
c3049ba546
5 changed files with 147 additions and 10 deletions
|
|
@ -175,10 +175,12 @@ function space_init(data) {
|
||||||
return api.tool.analyze();
|
return api.tool.analyze();
|
||||||
case 'KeyR':
|
case 'KeyR':
|
||||||
return api.tool.repair();
|
return api.tool.repair();
|
||||||
case 'KeyN':
|
case 'KeyE':
|
||||||
return api.tool.clean();
|
return api.tool.clean();
|
||||||
case 'KeyV':
|
case 'KeyV':
|
||||||
return selection.focus();
|
return selection.focus();
|
||||||
|
case 'KeyN':
|
||||||
|
return api.normals();
|
||||||
case 'KeyW':
|
case 'KeyW':
|
||||||
return api.wireframe();
|
return api.wireframe();
|
||||||
case 'KeyG':
|
case 'KeyG':
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,8 @@ let selection = {
|
||||||
update() {
|
update() {
|
||||||
for (let group of groups) {
|
for (let group of groups) {
|
||||||
group.select(false);
|
group.select(false);
|
||||||
group.wireframe(prefs.map.space.wire);
|
group.wireframe(prefs.map.space.wire || false);
|
||||||
|
group.normals(prefs.map.space.norm || false);
|
||||||
}
|
}
|
||||||
// prevent selection of model and its group
|
// prevent selection of model and its group
|
||||||
let mgsel = selected.filter(s => s instanceof mesh.model).map(m => m.group);
|
let mgsel = selected.filter(s => s instanceof mesh.model).map(m => m.group);
|
||||||
|
|
@ -491,12 +492,25 @@ let api = mesh.api = {
|
||||||
} else {
|
} else {
|
||||||
wire = state;
|
wire = state;
|
||||||
}
|
}
|
||||||
for (let m of api.model.list()) {
|
for (let m of model.list()) {
|
||||||
m.wireframe(wire, opt);
|
m.wireframe(wire, opt);
|
||||||
}
|
}
|
||||||
prefs.save( prefs.map.space.wire = wire );
|
prefs.save( prefs.map.space.wire = wire );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
normals(state = {toggle:true}) {
|
||||||
|
let norm = prefs.map.space.norm;
|
||||||
|
if (state.toggle) {
|
||||||
|
norm = !norm;
|
||||||
|
} else {
|
||||||
|
norm = state;
|
||||||
|
}
|
||||||
|
for (let m of model.list()) {
|
||||||
|
m.normals(norm);
|
||||||
|
}
|
||||||
|
prefs.save( prefs.map.space.norm = norm );
|
||||||
|
},
|
||||||
|
|
||||||
mode,
|
mode,
|
||||||
|
|
||||||
modes,
|
modes,
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,12 @@ mesh.group = class MeshGroup extends mesh.object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
normals() {
|
||||||
|
for (let model of this.models) {
|
||||||
|
model.normals(...arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
select() {
|
select() {
|
||||||
for (let model of this.models) {
|
for (let model of this.models) {
|
||||||
model.select(...arguments);
|
model.select(...arguments);
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,9 @@ let materials = mesh.material = {
|
||||||
wireframe: new MeshBasicMaterial({
|
wireframe: new MeshBasicMaterial({
|
||||||
side: DoubleSide,
|
side: DoubleSide,
|
||||||
wireframe: true,
|
wireframe: true,
|
||||||
color: 0x0
|
color: 0x0,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.5
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -249,23 +251,24 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
}
|
}
|
||||||
|
|
||||||
wireframe(bool, opt = {}) {
|
wireframe(bool, opt = {}) {
|
||||||
|
let was = this._wire ? true : false;
|
||||||
if (bool === undefined) {
|
if (bool === undefined) {
|
||||||
return this._wire ? {
|
return was ? {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
opacity: this.opacity(),
|
opacity: this.opacity(),
|
||||||
color: this._wire ? this._wire.material.color : undefined,
|
color: was ? this._wire.material.color : undefined,
|
||||||
} : {
|
} : {
|
||||||
enabled: false
|
enabled: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (bool.toggle) {
|
if (bool.toggle) {
|
||||||
return this.wireframe(this._wire ? false : true, opt);
|
bool = !was;
|
||||||
}
|
}
|
||||||
let was = this._wire ? true : false;
|
// no change
|
||||||
if (was === bool) {
|
if (was === bool) {
|
||||||
return was;
|
return was;
|
||||||
}
|
}
|
||||||
if (this._wire) {
|
if (was) {
|
||||||
this.mesh.remove(this._wire);
|
this.mesh.remove(this._wire);
|
||||||
this._wire = undefined;
|
this._wire = undefined;
|
||||||
this.opacity({restore: true});
|
this.opacity({restore: true});
|
||||||
|
|
@ -279,6 +282,27 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
return was;
|
return was;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
normals(bool) {
|
||||||
|
let was = this._norm ? true : false;
|
||||||
|
if (bool === undefined) {
|
||||||
|
return was;
|
||||||
|
}
|
||||||
|
if (bool.toggle) {
|
||||||
|
bool = !was;
|
||||||
|
}
|
||||||
|
// no change
|
||||||
|
if (was === bool) {
|
||||||
|
return was;
|
||||||
|
}
|
||||||
|
if (was) {
|
||||||
|
this.mesh.remove(this._norm);
|
||||||
|
this._norm = undefined;
|
||||||
|
}
|
||||||
|
if (bool) {
|
||||||
|
this.mesh.add(this._norm = mesh.util.faceNormals(this.mesh, { size: 0.5 }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
if (arguments.length === 0) {
|
if (arguments.length === 0) {
|
||||||
// direct call requires pass through group
|
// direct call requires pass through group
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ gapp.register("mesh.util", [
|
||||||
let mesh = self.mesh = self.mesh || {};
|
let mesh = self.mesh = self.mesh || {};
|
||||||
if (mesh.util) return;
|
if (mesh.util) return;
|
||||||
|
|
||||||
let { Matrix4, Vector3, Box3 } = THREE;
|
let { Matrix4, Matrix3, Vector3, Box3 } = THREE;
|
||||||
|
|
||||||
let deferFn = [];
|
let deferFn = [];
|
||||||
let boundsCache = {};
|
let boundsCache = {};
|
||||||
|
|
@ -156,7 +156,98 @@ let util = mesh.util = {
|
||||||
avg[f] = (avg[f] || 0) / array.length;
|
avg[f] = (avg[f] || 0) / array.length;
|
||||||
}
|
}
|
||||||
return avg;
|
return avg;
|
||||||
|
},
|
||||||
|
|
||||||
|
faceNormals(mesh, opt = { }) {
|
||||||
|
const _va = new THREE.Vector3();
|
||||||
|
const _vb = new THREE.Vector3();
|
||||||
|
const _vc = new THREE.Vector3();
|
||||||
|
const _v1 = new THREE.Vector3();
|
||||||
|
const _v2 = new THREE.Vector3();
|
||||||
|
const _normalMatrix = new THREE.Matrix3();
|
||||||
|
|
||||||
|
class FaceNormalsHelper extends THREE.LineSegments {
|
||||||
|
constructor(object, size = opt.size || 1, color = opt.color || 0xff0000) {
|
||||||
|
const objGeometry = object.geometry;
|
||||||
|
const nNormals = objGeometry.attributes.position.count / 3;
|
||||||
|
const geometry = new THREE.BufferGeometry();
|
||||||
|
const positions = new THREE.Float32BufferAttribute(nNormals * 3 * 2, 3);
|
||||||
|
geometry.setAttribute('position', positions);
|
||||||
|
super(geometry, new THREE.LineBasicMaterial({
|
||||||
|
color, toneMapped: false
|
||||||
|
}));
|
||||||
|
this.object = object;
|
||||||
|
this.size = size;
|
||||||
|
this.type = 'FaceNormalsHelper';
|
||||||
|
this.matrixAutoUpdate = false;
|
||||||
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.object.updateMatrixWorld(true);
|
||||||
|
const position = this.geometry.attributes.position;
|
||||||
|
const objGeometry = this.object.geometry;
|
||||||
|
const objArr = objGeometry.attributes.position.array;
|
||||||
|
let j=0;
|
||||||
|
for (let idx = 0, xj = 0, jl = objArr.length; j < jl; ) {
|
||||||
|
_va.set(objArr[j++], objArr[j++], objArr[j++]);
|
||||||
|
_vb.set(objArr[j++], objArr[j++], objArr[j++]);
|
||||||
|
_vc.set(objArr[j++], objArr[j++], objArr[j++]);
|
||||||
|
let vn = THREE.computeFaceNormal(_va, _vb, _vc);
|
||||||
|
let vc = _va.add(_vb).add(_vc).divideScalar(3);
|
||||||
|
_v2.copy(vc).add(vn.multiplyScalar(this.size));
|
||||||
|
position.setXYZ(idx++, vc.x, vc.y, vc.z);
|
||||||
|
position.setXYZ(idx++, _v2.x, _v2.y, _v2.z);
|
||||||
|
}
|
||||||
|
position.needsUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FaceNormalsHelper(mesh);
|
||||||
|
},
|
||||||
|
|
||||||
|
vertexNormals(mesh) {
|
||||||
|
const _v1 = new Vector3();
|
||||||
|
const _v2 = new Vector3();
|
||||||
|
const _normalMatrix = new Matrix3();
|
||||||
|
|
||||||
|
class VertexNormalsHelper extends THREE.LineSegments {
|
||||||
|
constructor(object, size = 1, color = 0xff0000) {
|
||||||
|
const objGeometry = object.geometry;
|
||||||
|
const nNormals = objGeometry.attributes.normal.count;
|
||||||
|
const geometry = new THREE.BufferGeometry();
|
||||||
|
const positions = new THREE.Float32BufferAttribute(nNormals * 2 * 3, 3);
|
||||||
|
geometry.setAttribute('position', positions);
|
||||||
|
super(geometry, new THREE.LineBasicMaterial({
|
||||||
|
color,toneMapped: false
|
||||||
|
}));
|
||||||
|
this.object = object;
|
||||||
|
this.size = size;
|
||||||
|
this.type = 'VertexNormalsHelper';
|
||||||
|
this.matrixAutoUpdate = false;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.object.updateMatrixWorld(true);
|
||||||
|
const position = this.geometry.attributes.position;
|
||||||
|
const objGeometry = this.object.geometry;
|
||||||
|
const objPos = objGeometry.attributes.position;
|
||||||
|
const objNorm = objGeometry.attributes.normal;
|
||||||
|
for (let idx = 0, j = 0, jl = objPos.count; j < jl; j++) {
|
||||||
|
_v1.set(objPos.getX(j), objPos.getY(j), objPos.getZ(j));
|
||||||
|
_v2.set(objNorm.getX(j), objNorm.getY(j), objNorm.getZ(j));
|
||||||
|
_v2.applyMatrix3(_normalMatrix).normalize().multiplyScalar(this.size).add(_v1);
|
||||||
|
position.setXYZ(idx++, _v1.x, _v1.y, _v1.z);
|
||||||
|
position.setXYZ(idx++, _v2.x, _v2.y, _v2.z);
|
||||||
|
}
|
||||||
|
position.needsUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new VertexNormalsHelper(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue