Skip to content

Commit a62e7f1

Browse files
authored
Merge pull request #20535 from Mcgode/edge-split-modifier
Examples: Add EdgeSplitModifier
2 parents 3926b03 + 4dcfd4d commit a62e7f1

File tree

4 files changed

+537
-0
lines changed

4 files changed

+537
-0
lines changed
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
console.warn( "THREE.EdgeSplitModifier: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs.org/docs/#manual/en/introduction/Installation." );
2+
3+
4+
THREE.EdgeSplitModifier = function () {
5+
6+
const A = new THREE.Vector3();
7+
const B = new THREE.Vector3();
8+
const C = new THREE.Vector3();
9+
10+
let positions, normals;
11+
let indexes;
12+
let pointToIndexMap, splitIndexes;
13+
14+
15+
function computeNormals() {
16+
17+
normals = new Float32Array( indexes.length * 3 );
18+
19+
for ( let i = 0; i < indexes.length; i += 3 ) {
20+
21+
let index = indexes[ i ];
22+
A.set(
23+
positions[ 3 * index ],
24+
positions[ 3 * index + 1 ],
25+
positions[ 3 * index + 2 ] );
26+
27+
index = indexes[ i + 1 ];
28+
B.set(
29+
positions[ 3 * index ],
30+
positions[ 3 * index + 1 ],
31+
positions[ 3 * index + 2 ] );
32+
33+
index = indexes[ i + 2 ];
34+
C.set(
35+
positions[ 3 * index ],
36+
positions[ 3 * index + 1 ],
37+
positions[ 3 * index + 2 ] );
38+
39+
C.sub( B );
40+
A.sub( B );
41+
42+
const normal = C.cross( A ).normalize();
43+
44+
for ( let j = 0; j < 3; j ++ ) {
45+
46+
normals[ 3 * ( i + j ) ] = normal.x;
47+
normals[ 3 * ( i + j ) + 1 ] = normal.y;
48+
normals[ 3 * ( i + j ) + 2 ] = normal.z;
49+
50+
}
51+
52+
}
53+
54+
}
55+
56+
57+
function mapPositionsToIndexes() {
58+
59+
pointToIndexMap = Array( positions.length / 3 );
60+
61+
for ( let i = 0; i < indexes.length; i ++ ) {
62+
63+
const index = indexes[ i ];
64+
65+
if ( pointToIndexMap[ index ] == null )
66+
pointToIndexMap[ index ] = [];
67+
68+
pointToIndexMap[ index ].push( i );
69+
70+
}
71+
72+
}
73+
74+
75+
function edgeSplitToGroups( indexes, cutOff, firstIndex ) {
76+
77+
A.set( normals[ 3 * firstIndex ], normals[ 3 * firstIndex + 1 ], normals[ 3 * firstIndex + 2 ] )
78+
.normalize();
79+
80+
const result = {
81+
splitGroup: [],
82+
currentGroup: [ firstIndex ]
83+
};
84+
85+
for ( const j of indexes ) {
86+
87+
if ( j !== firstIndex ) {
88+
89+
B.set( normals[ 3 * j ], normals[ 3 * j + 1 ], normals[ 3 * j + 2 ] )
90+
.normalize();
91+
92+
if ( B.dot( A ) < cutOff )
93+
result.splitGroup.push( j );
94+
95+
else
96+
result.currentGroup.push( j );
97+
98+
}
99+
100+
}
101+
102+
return result;
103+
104+
}
105+
106+
107+
function edgeSplit( indexes, cutOff, original = null ) {
108+
109+
if ( indexes.length === 0 )
110+
return;
111+
112+
const groupResults = [];
113+
for ( const index of indexes )
114+
groupResults.push( edgeSplitToGroups( indexes, cutOff, index ) );
115+
116+
let result = groupResults[ 0 ];
117+
for ( const groupResult of groupResults )
118+
if ( groupResult.currentGroup.length > result.currentGroup.length )
119+
result = groupResult;
120+
121+
if ( original != null )
122+
splitIndexes.push( {
123+
original: original,
124+
indexes: result.currentGroup
125+
} );
126+
127+
if ( result.splitGroup.length )
128+
edgeSplit( result.splitGroup, cutOff, original || result.currentGroup[ 0 ] );
129+
130+
}
131+
132+
133+
this.modify = function ( geometry, cutOffAngle ) {
134+
135+
if ( ! geometry.isBufferGeometry ) {
136+
137+
geometry = new THREE.BufferGeometry().fromGeometry( geometry );
138+
139+
}
140+
141+
142+
if ( geometry.index == null )
143+
geometry = THREE.BufferGeometryUtils.mergeVertices( geometry );
144+
145+
146+
indexes = geometry.index.array;
147+
positions = geometry.getAttribute( "position" ).array;
148+
149+
computeNormals();
150+
151+
152+
mapPositionsToIndexes();
153+
154+
155+
splitIndexes = [];
156+
157+
for ( const vertexIndexes of pointToIndexMap )
158+
edgeSplit( vertexIndexes, Math.cos( cutOffAngle ) - 0.001 );
159+
160+
161+
const newPositions = new Float32Array( positions.length + 3 * splitIndexes.length );
162+
newPositions.set( positions );
163+
const offset = positions.length;
164+
165+
const newIndexes = new Uint32Array( indexes.length );
166+
newIndexes.set( indexes );
167+
168+
for ( let i = 0; i < splitIndexes.length; i ++ ) {
169+
170+
const split = splitIndexes[ i ];
171+
const index = indexes[ split.original ];
172+
173+
newPositions[ offset + 3 * i ] = positions[ 3 * index ];
174+
newPositions[ offset + 3 * i + 1 ] = positions[ 3 * index + 1 ];
175+
newPositions[ offset + 3 * i + 2 ] = positions[ 3 * index + 2 ];
176+
177+
for ( const j of split.indexes )
178+
newIndexes[ j ] = offset / 3 + i;
179+
180+
}
181+
182+
geometry = new THREE.BufferGeometry();
183+
geometry.setAttribute( 'position', new THREE.BufferAttribute( newPositions, 3, true ) );
184+
geometry.setIndex( new THREE.BufferAttribute( newIndexes, 1 ) );
185+
186+
return geometry;
187+
188+
};
189+
190+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { BufferGeometry, Geometry } from "../../../src/Three";
2+
3+
export class EdgeSplitModifier {
4+
5+
constructor();
6+
modify( geometry: Geometry, cutOffPoint: number ): BufferGeometry;
7+
8+
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import { BufferAttribute, BufferGeometry, Vector3 } from "../../../build/three.module.js";
2+
import { BufferGeometryUtils } from "../utils/BufferGeometryUtils.js";
3+
4+
5+
export function EdgeSplitModifier() {
6+
7+
const A = new Vector3();
8+
const B = new Vector3();
9+
const C = new Vector3();
10+
11+
let positions, normals;
12+
let indexes;
13+
let pointToIndexMap, splitIndexes;
14+
15+
16+
function computeNormals() {
17+
18+
normals = new Float32Array( indexes.length * 3 );
19+
20+
for ( let i = 0; i < indexes.length; i += 3 ) {
21+
22+
let index = indexes[ i ];
23+
A.set(
24+
positions[ 3 * index ],
25+
positions[ 3 * index + 1 ],
26+
positions[ 3 * index + 2 ] );
27+
28+
index = indexes[ i + 1 ];
29+
B.set(
30+
positions[ 3 * index ],
31+
positions[ 3 * index + 1 ],
32+
positions[ 3 * index + 2 ] );
33+
34+
index = indexes[ i + 2 ];
35+
C.set(
36+
positions[ 3 * index ],
37+
positions[ 3 * index + 1 ],
38+
positions[ 3 * index + 2 ] );
39+
40+
C.sub( B );
41+
A.sub( B );
42+
43+
const normal = C.cross( A ).normalize();
44+
45+
for ( let j = 0; j < 3; j ++ ) {
46+
47+
normals[ 3 * ( i + j ) ] = normal.x;
48+
normals[ 3 * ( i + j ) + 1 ] = normal.y;
49+
normals[ 3 * ( i + j ) + 2 ] = normal.z;
50+
51+
}
52+
53+
}
54+
55+
}
56+
57+
58+
function mapPositionsToIndexes() {
59+
60+
pointToIndexMap = Array( positions.length / 3 );
61+
62+
for ( let i = 0; i < indexes.length; i ++ ) {
63+
64+
const index = indexes[ i ];
65+
66+
if ( pointToIndexMap[ index ] == null )
67+
pointToIndexMap[ index ] = [];
68+
69+
pointToIndexMap[ index ].push( i );
70+
71+
}
72+
73+
}
74+
75+
76+
function edgeSplitToGroups( indexes, cutOff, firstIndex ) {
77+
78+
A.set( normals[ 3 * firstIndex ], normals[ 3 * firstIndex + 1 ], normals[ 3 * firstIndex + 2 ] )
79+
.normalize();
80+
81+
const result = {
82+
splitGroup: [],
83+
currentGroup: [ firstIndex ]
84+
};
85+
86+
for ( const j of indexes ) {
87+
88+
if ( j !== firstIndex ) {
89+
90+
B.set( normals[ 3 * j ], normals[ 3 * j + 1 ], normals[ 3 * j + 2 ] )
91+
.normalize();
92+
93+
if ( B.dot( A ) < cutOff )
94+
result.splitGroup.push( j );
95+
96+
else
97+
result.currentGroup.push( j );
98+
99+
}
100+
101+
}
102+
103+
return result;
104+
105+
}
106+
107+
108+
function edgeSplit( indexes, cutOff, original = null ) {
109+
110+
if ( indexes.length === 0 )
111+
return;
112+
113+
const groupResults = [];
114+
for ( const index of indexes )
115+
groupResults.push( edgeSplitToGroups( indexes, cutOff, index ) );
116+
117+
let result = groupResults[ 0 ];
118+
for ( const groupResult of groupResults )
119+
if ( groupResult.currentGroup.length > result.currentGroup.length )
120+
result = groupResult;
121+
122+
if ( original != null )
123+
splitIndexes.push( {
124+
original: original,
125+
indexes: result.currentGroup
126+
} );
127+
128+
if ( result.splitGroup.length )
129+
edgeSplit( result.splitGroup, cutOff, original || result.currentGroup[ 0 ] );
130+
131+
}
132+
133+
134+
this.modify = function ( geometry, cutOffAngle ) {
135+
136+
if ( ! geometry.isBufferGeometry ) {
137+
138+
geometry = new BufferGeometry().fromGeometry( geometry );
139+
140+
}
141+
142+
143+
if ( geometry.index == null )
144+
geometry = BufferGeometryUtils.mergeVertices( geometry );
145+
146+
147+
indexes = geometry.index.array;
148+
positions = geometry.getAttribute( "position" ).array;
149+
150+
computeNormals();
151+
152+
153+
mapPositionsToIndexes();
154+
155+
156+
splitIndexes = [];
157+
158+
for ( const vertexIndexes of pointToIndexMap )
159+
edgeSplit( vertexIndexes, Math.cos( cutOffAngle ) - 0.001 );
160+
161+
162+
const newPositions = new Float32Array( positions.length + 3 * splitIndexes.length );
163+
newPositions.set( positions );
164+
const offset = positions.length;
165+
166+
const newIndexes = new Uint32Array( indexes.length );
167+
newIndexes.set( indexes );
168+
169+
for ( let i = 0; i < splitIndexes.length; i ++ ) {
170+
171+
const split = splitIndexes[ i ];
172+
const index = indexes[ split.original ];
173+
174+
newPositions[ offset + 3 * i ] = positions[ 3 * index ];
175+
newPositions[ offset + 3 * i + 1 ] = positions[ 3 * index + 1 ];
176+
newPositions[ offset + 3 * i + 2 ] = positions[ 3 * index + 2 ];
177+
178+
for ( const j of split.indexes )
179+
newIndexes[ j ] = offset / 3 + i;
180+
181+
}
182+
183+
geometry = new BufferGeometry();
184+
geometry.setAttribute( 'position', new BufferAttribute( newPositions, 3, true ) );
185+
geometry.setIndex( new BufferAttribute( newIndexes, 1 ) );
186+
187+
return geometry;
188+
189+
};
190+
191+
}

0 commit comments

Comments
 (0)