Skip to content

Commit 91f3114

Browse files
mrdoobclaude
andcommitted
Examples: Use MWSELR vertex normals for sculpt shading.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bbf1800 commit 91f3114

1 file changed

Lines changed: 40 additions & 5 deletions

File tree

examples/jsm/sculpt/SculptMesh.js

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -607,28 +607,63 @@ class SculptMesh {
607607
_updateVerticesNormal( iVerts ) {
608608

609609
const nAr = this._normalsXYZ;
610+
const vAr = this._verticesXYZ;
611+
const fAr = this._facesABCD;
610612
const faceNormals = this._faceNormals;
611613
const ringFaces = this._vertRingFace;
612614
const full = iVerts === undefined;
613615
const nbVerts = full ? this._nbVertices : iVerts.length;
614616

617+
// MWSELR: weight = sin(angle) / (|e1| * |e2|)
618+
// Since sin(angle) = |cross(e1,e2)| / (|e1|*|e2|),
619+
// weight = |cross(e1,e2)| / (|e1|^2 * |e2|^2)
620+
615621
for ( let i = 0; i < nbVerts; ++ i ) {
616622

617623
const ind = full ? i : iVerts[ i ];
618624
const vrf = ringFaces[ ind ];
625+
const ind3 = ind * 3;
626+
const vx = vAr[ ind3 ], vy = vAr[ ind3 + 1 ], vz = vAr[ ind3 + 2 ];
619627
let nx = 0, ny = 0, nz = 0;
628+
620629
for ( let j = 0, l = vrf.length; j < l; ++ j ) {
621630

622-
const id = vrf[ j ] * 3;
623-
nx += faceNormals[ id ];
624-
ny += faceNormals[ id + 1 ];
625-
nz += faceNormals[ id + 2 ];
631+
const fId = vrf[ j ] * 4;
632+
const iv1 = fAr[ fId ], iv2 = fAr[ fId + 1 ], iv3 = fAr[ fId + 2 ];
633+
634+
// Find the two other vertices of this face
635+
let ov1, ov2;
636+
if ( iv1 === ind ) { ov1 = iv2; ov2 = iv3; }
637+
else if ( iv2 === ind ) { ov1 = iv3; ov2 = iv1; }
638+
else { ov1 = iv1; ov2 = iv2; }
639+
640+
const o1 = ov1 * 3, o2 = ov2 * 3;
641+
const e1x = vAr[ o1 ] - vx, e1y = vAr[ o1 + 1 ] - vy, e1z = vAr[ o1 + 2 ] - vz;
642+
const e2x = vAr[ o2 ] - vx, e2y = vAr[ o2 + 1 ] - vy, e2z = vAr[ o2 + 2 ] - vz;
643+
644+
const len1sq = e1x * e1x + e1y * e1y + e1z * e1z;
645+
const len2sq = e2x * e2x + e2y * e2y + e2z * e2z;
646+
const denom = len1sq * len2sq;
647+
if ( denom === 0 ) continue;
648+
649+
// cross(e1, e2) magnitude = sin(angle) * |e1| * |e2|
650+
const cx = e1y * e2z - e1z * e2y;
651+
const cy = e1z * e2x - e1x * e2z;
652+
const cz = e1x * e2y - e1y * e2x;
653+
const sinArea = Math.sqrt( cx * cx + cy * cy + cz * cz );
654+
655+
// weight = sinArea / (|e1|^2 * |e2|^2) = sin(angle) / (|e1| * |e2|)
656+
const w = sinArea / denom;
657+
658+
const fnId = vrf[ j ] * 3;
659+
nx += faceNormals[ fnId ] * w;
660+
ny += faceNormals[ fnId + 1 ] * w;
661+
nz += faceNormals[ fnId + 2 ] * w;
626662

627663
}
628664

629665
let len = Math.sqrt( nx * nx + ny * ny + nz * nz );
630666
if ( len > 0 ) len = 1.0 / len;
631-
const ind3 = ind * 3;
632667
nAr[ ind3 ] = nx * len;
633668
nAr[ ind3 + 1 ] = ny * len;
634669
nAr[ ind3 + 2 ] = nz * len;

0 commit comments

Comments
 (0)