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();
|
||||
case 'KeyR':
|
||||
return api.tool.repair();
|
||||
case 'KeyN':
|
||||
case 'KeyE':
|
||||
return api.tool.clean();
|
||||
case 'KeyV':
|
||||
return selection.focus();
|
||||
case 'KeyN':
|
||||
return api.normals();
|
||||
case 'KeyW':
|
||||
return api.wireframe();
|
||||
case 'KeyG':
|
||||
|
|
|
|||
|
|
@ -128,7 +128,8 @@ let selection = {
|
|||
update() {
|
||||
for (let group of groups) {
|
||||
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
|
||||
let mgsel = selected.filter(s => s instanceof mesh.model).map(m => m.group);
|
||||
|
|
@ -491,12 +492,25 @@ let api = mesh.api = {
|
|||
} else {
|
||||
wire = state;
|
||||
}
|
||||
for (let m of api.model.list()) {
|
||||
for (let m of model.list()) {
|
||||
m.wireframe(wire, opt);
|
||||
}
|
||||
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,
|
||||
|
||||
modes,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,12 @@ mesh.group = class MeshGroup extends mesh.object {
|
|||
}
|
||||
}
|
||||
|
||||
normals() {
|
||||
for (let model of this.models) {
|
||||
model.normals(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
select() {
|
||||
for (let model of this.models) {
|
||||
model.select(...arguments);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,9 @@ let materials = mesh.material = {
|
|||
wireframe: new MeshBasicMaterial({
|
||||
side: DoubleSide,
|
||||
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 = {}) {
|
||||
let was = this._wire ? true : false;
|
||||
if (bool === undefined) {
|
||||
return this._wire ? {
|
||||
return was ? {
|
||||
enabled: true,
|
||||
opacity: this.opacity(),
|
||||
color: this._wire ? this._wire.material.color : undefined,
|
||||
color: was ? this._wire.material.color : undefined,
|
||||
} : {
|
||||
enabled: false
|
||||
};
|
||||
}
|
||||
if (bool.toggle) {
|
||||
return this.wireframe(this._wire ? false : true, opt);
|
||||
bool = !was;
|
||||
}
|
||||
let was = this._wire ? true : false;
|
||||
// no change
|
||||
if (was === bool) {
|
||||
return was;
|
||||
}
|
||||
if (this._wire) {
|
||||
if (was) {
|
||||
this.mesh.remove(this._wire);
|
||||
this._wire = undefined;
|
||||
this.opacity({restore: true});
|
||||
|
|
@ -279,6 +282,27 @@ mesh.model = class MeshModel extends mesh.object {
|
|||
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() {
|
||||
if (arguments.length === 0) {
|
||||
// direct call requires pass through group
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ gapp.register("mesh.util", [
|
|||
let mesh = self.mesh = self.mesh || {};
|
||||
if (mesh.util) return;
|
||||
|
||||
let { Matrix4, Vector3, Box3 } = THREE;
|
||||
let { Matrix4, Matrix3, Vector3, Box3 } = THREE;
|
||||
|
||||
let deferFn = [];
|
||||
let boundsCache = {};
|
||||
|
|
@ -156,7 +156,98 @@ let util = mesh.util = {
|
|||
avg[f] = (avg[f] || 0) / array.length;
|
||||
}
|
||||
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