105 lines
3.3 KiB
JavaScript
105 lines
3.3 KiB
JavaScript
import { Mesh } from '../../core/Mesh.js';
|
|
import { Program } from '../../core/Program.js';
|
|
import { Geometry } from '../../core/Geometry.js';
|
|
import { Vec3 } from '../../math/Vec3.js';
|
|
import { Mat3 } from '../../math/Mat3.js';
|
|
|
|
const vA = /* @__PURE__ */ new Vec3();
|
|
const vB = /* @__PURE__ */ new Vec3();
|
|
const vC = /* @__PURE__ */ new Vec3();
|
|
const vCenter = /* @__PURE__ */ new Vec3();
|
|
const vNormal = /* @__PURE__ */ new Vec3();
|
|
|
|
export class FaceNormalsHelper extends Mesh {
|
|
constructor(object, { size = 0.1, color = new Vec3(0.15, 0.86, 0.86), ...meshProps } = {}) {
|
|
const gl = object.gl;
|
|
|
|
const positionData = object.geometry.attributes.position.data;
|
|
const sizeData = new Float32Array([0, size]);
|
|
|
|
const indexAttr = object.geometry.attributes.index;
|
|
const getIndex = indexAttr ? (i) => indexAttr.data[i] : (i) => i;
|
|
const numVertices = indexAttr ? indexAttr.data.length : Math.floor(positionData.length / 3);
|
|
|
|
const nNormals = Math.floor(numVertices / 3);
|
|
const positionsArray = new Float32Array(nNormals * 2 * 3);
|
|
const normalsArray = new Float32Array(nNormals * 2 * 3);
|
|
const sizeArray = new Float32Array(nNormals * 2);
|
|
|
|
for (let i = 0; i < numVertices; i += 3) {
|
|
vA.fromArray(positionData, getIndex(i + 0) * 3);
|
|
vB.fromArray(positionData, getIndex(i + 1) * 3);
|
|
vC.fromArray(positionData, getIndex(i + 2) * 3);
|
|
|
|
vCenter
|
|
.add(vA, vB)
|
|
.add(vC)
|
|
.multiply(1 / 3);
|
|
|
|
vA.sub(vA, vB);
|
|
vC.sub(vC, vB);
|
|
vNormal.cross(vC, vA).normalize();
|
|
|
|
// duplicate position and normal for line start and end point
|
|
const i2 = i * 2;
|
|
positionsArray.set(vCenter, i2);
|
|
positionsArray.set(vCenter, i2 + 3);
|
|
|
|
normalsArray.set(vNormal, i2);
|
|
normalsArray.set(vNormal, i2 + 3);
|
|
sizeArray.set(sizeData, (i / 3) * 2);
|
|
}
|
|
|
|
const geometry = new Geometry(gl, {
|
|
position: { size: 3, data: positionsArray },
|
|
normal: { size: 3, data: normalsArray },
|
|
size: { size: 1, data: sizeArray },
|
|
});
|
|
|
|
const program = new Program(gl, {
|
|
vertex,
|
|
fragment,
|
|
uniforms: {
|
|
color: { value: color },
|
|
worldNormalMatrix: { value: new Mat3() },
|
|
objectWorldMatrix: { value: object.worldMatrix },
|
|
},
|
|
});
|
|
|
|
super(gl, { ...meshProps, mode: gl.LINES, geometry, program });
|
|
|
|
this.object = object;
|
|
}
|
|
|
|
draw(arg) {
|
|
this.program.uniforms.worldNormalMatrix.value.getNormalMatrix(this.object.worldMatrix);
|
|
super.draw(arg);
|
|
}
|
|
}
|
|
|
|
const vertex = /* glsl */ `
|
|
attribute vec3 position;
|
|
attribute vec3 normal;
|
|
attribute float size;
|
|
|
|
uniform mat4 viewMatrix;
|
|
uniform mat4 projectionMatrix;
|
|
uniform mat4 objectWorldMatrix;
|
|
uniform mat3 worldNormalMatrix;
|
|
|
|
void main() {
|
|
vec3 n = normalize(worldNormalMatrix * normal) * size;
|
|
vec3 p = (objectWorldMatrix * vec4(position, 1.0)).xyz;
|
|
gl_Position = projectionMatrix * viewMatrix * vec4(p + n, 1.0);
|
|
}
|
|
`;
|
|
|
|
const fragment = /* glsl */ `
|
|
precision highp float;
|
|
uniform vec3 color;
|
|
|
|
void main() {
|
|
gl_FragColor = vec4(color, 1.0);
|
|
}
|
|
`;
|