未验证 提交 a62e7f1e 编写于 作者: M Mr.doob 提交者: GitHub

Merge pull request #20535 from Mcgode/edge-split-modifier

Examples: Add EdgeSplitModifier
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." );
THREE.EdgeSplitModifier = function () {
const A = new THREE.Vector3();
const B = new THREE.Vector3();
const C = new THREE.Vector3();
let positions, normals;
let indexes;
let pointToIndexMap, splitIndexes;
function computeNormals() {
normals = new Float32Array( indexes.length * 3 );
for ( let i = 0; i < indexes.length; i += 3 ) {
let index = indexes[ i ];
A.set(
positions[ 3 * index ],
positions[ 3 * index + 1 ],
positions[ 3 * index + 2 ] );
index = indexes[ i + 1 ];
B.set(
positions[ 3 * index ],
positions[ 3 * index + 1 ],
positions[ 3 * index + 2 ] );
index = indexes[ i + 2 ];
C.set(
positions[ 3 * index ],
positions[ 3 * index + 1 ],
positions[ 3 * index + 2 ] );
C.sub( B );
A.sub( B );
const normal = C.cross( A ).normalize();
for ( let j = 0; j < 3; j ++ ) {
normals[ 3 * ( i + j ) ] = normal.x;
normals[ 3 * ( i + j ) + 1 ] = normal.y;
normals[ 3 * ( i + j ) + 2 ] = normal.z;
}
}
}
function mapPositionsToIndexes() {
pointToIndexMap = Array( positions.length / 3 );
for ( let i = 0; i < indexes.length; i ++ ) {
const index = indexes[ i ];
if ( pointToIndexMap[ index ] == null )
pointToIndexMap[ index ] = [];
pointToIndexMap[ index ].push( i );
}
}
function edgeSplitToGroups( indexes, cutOff, firstIndex ) {
A.set( normals[ 3 * firstIndex ], normals[ 3 * firstIndex + 1 ], normals[ 3 * firstIndex + 2 ] )
.normalize();
const result = {
splitGroup: [],
currentGroup: [ firstIndex ]
};
for ( const j of indexes ) {
if ( j !== firstIndex ) {
B.set( normals[ 3 * j ], normals[ 3 * j + 1 ], normals[ 3 * j + 2 ] )
.normalize();
if ( B.dot( A ) < cutOff )
result.splitGroup.push( j );
else
result.currentGroup.push( j );
}
}
return result;
}
function edgeSplit( indexes, cutOff, original = null ) {
if ( indexes.length === 0 )
return;
const groupResults = [];
for ( const index of indexes )
groupResults.push( edgeSplitToGroups( indexes, cutOff, index ) );
let result = groupResults[ 0 ];
for ( const groupResult of groupResults )
if ( groupResult.currentGroup.length > result.currentGroup.length )
result = groupResult;
if ( original != null )
splitIndexes.push( {
original: original,
indexes: result.currentGroup
} );
if ( result.splitGroup.length )
edgeSplit( result.splitGroup, cutOff, original || result.currentGroup[ 0 ] );
}
this.modify = function ( geometry, cutOffAngle ) {
if ( ! geometry.isBufferGeometry ) {
geometry = new THREE.BufferGeometry().fromGeometry( geometry );
}
if ( geometry.index == null )
geometry = THREE.BufferGeometryUtils.mergeVertices( geometry );
indexes = geometry.index.array;
positions = geometry.getAttribute( "position" ).array;
computeNormals();
mapPositionsToIndexes();
splitIndexes = [];
for ( const vertexIndexes of pointToIndexMap )
edgeSplit( vertexIndexes, Math.cos( cutOffAngle ) - 0.001 );
const newPositions = new Float32Array( positions.length + 3 * splitIndexes.length );
newPositions.set( positions );
const offset = positions.length;
const newIndexes = new Uint32Array( indexes.length );
newIndexes.set( indexes );
for ( let i = 0; i < splitIndexes.length; i ++ ) {
const split = splitIndexes[ i ];
const index = indexes[ split.original ];
newPositions[ offset + 3 * i ] = positions[ 3 * index ];
newPositions[ offset + 3 * i + 1 ] = positions[ 3 * index + 1 ];
newPositions[ offset + 3 * i + 2 ] = positions[ 3 * index + 2 ];
for ( const j of split.indexes )
newIndexes[ j ] = offset / 3 + i;
}
geometry = new THREE.BufferGeometry();
geometry.setAttribute( 'position', new THREE.BufferAttribute( newPositions, 3, true ) );
geometry.setIndex( new THREE.BufferAttribute( newIndexes, 1 ) );
return geometry;
};
};
import { BufferGeometry, Geometry } from "../../../src/Three";
export class EdgeSplitModifier {
constructor();
modify( geometry: Geometry, cutOffPoint: number ): BufferGeometry;
}
import { BufferAttribute, BufferGeometry, Vector3 } from "../../../build/three.module.js";
import { BufferGeometryUtils } from "../utils/BufferGeometryUtils.js";
export function EdgeSplitModifier() {
const A = new Vector3();
const B = new Vector3();
const C = new Vector3();
let positions, normals;
let indexes;
let pointToIndexMap, splitIndexes;
function computeNormals() {
normals = new Float32Array( indexes.length * 3 );
for ( let i = 0; i < indexes.length; i += 3 ) {
let index = indexes[ i ];
A.set(
positions[ 3 * index ],
positions[ 3 * index + 1 ],
positions[ 3 * index + 2 ] );
index = indexes[ i + 1 ];
B.set(
positions[ 3 * index ],
positions[ 3 * index + 1 ],
positions[ 3 * index + 2 ] );
index = indexes[ i + 2 ];
C.set(
positions[ 3 * index ],
positions[ 3 * index + 1 ],
positions[ 3 * index + 2 ] );
C.sub( B );
A.sub( B );
const normal = C.cross( A ).normalize();
for ( let j = 0; j < 3; j ++ ) {
normals[ 3 * ( i + j ) ] = normal.x;
normals[ 3 * ( i + j ) + 1 ] = normal.y;
normals[ 3 * ( i + j ) + 2 ] = normal.z;
}
}
}
function mapPositionsToIndexes() {
pointToIndexMap = Array( positions.length / 3 );
for ( let i = 0; i < indexes.length; i ++ ) {
const index = indexes[ i ];
if ( pointToIndexMap[ index ] == null )
pointToIndexMap[ index ] = [];
pointToIndexMap[ index ].push( i );
}
}
function edgeSplitToGroups( indexes, cutOff, firstIndex ) {
A.set( normals[ 3 * firstIndex ], normals[ 3 * firstIndex + 1 ], normals[ 3 * firstIndex + 2 ] )
.normalize();
const result = {
splitGroup: [],
currentGroup: [ firstIndex ]
};
for ( const j of indexes ) {
if ( j !== firstIndex ) {
B.set( normals[ 3 * j ], normals[ 3 * j + 1 ], normals[ 3 * j + 2 ] )
.normalize();
if ( B.dot( A ) < cutOff )
result.splitGroup.push( j );
else
result.currentGroup.push( j );
}
}
return result;
}
function edgeSplit( indexes, cutOff, original = null ) {
if ( indexes.length === 0 )
return;
const groupResults = [];
for ( const index of indexes )
groupResults.push( edgeSplitToGroups( indexes, cutOff, index ) );
let result = groupResults[ 0 ];
for ( const groupResult of groupResults )
if ( groupResult.currentGroup.length > result.currentGroup.length )
result = groupResult;
if ( original != null )
splitIndexes.push( {
original: original,
indexes: result.currentGroup
} );
if ( result.splitGroup.length )
edgeSplit( result.splitGroup, cutOff, original || result.currentGroup[ 0 ] );
}
this.modify = function ( geometry, cutOffAngle ) {
if ( ! geometry.isBufferGeometry ) {
geometry = new BufferGeometry().fromGeometry( geometry );
}
if ( geometry.index == null )
geometry = BufferGeometryUtils.mergeVertices( geometry );
indexes = geometry.index.array;
positions = geometry.getAttribute( "position" ).array;
computeNormals();
mapPositionsToIndexes();
splitIndexes = [];
for ( const vertexIndexes of pointToIndexMap )
edgeSplit( vertexIndexes, Math.cos( cutOffAngle ) - 0.001 );
const newPositions = new Float32Array( positions.length + 3 * splitIndexes.length );
newPositions.set( positions );
const offset = positions.length;
const newIndexes = new Uint32Array( indexes.length );
newIndexes.set( indexes );
for ( let i = 0; i < splitIndexes.length; i ++ ) {
const split = splitIndexes[ i ];
const index = indexes[ split.original ];
newPositions[ offset + 3 * i ] = positions[ 3 * index ];
newPositions[ offset + 3 * i + 1 ] = positions[ 3 * index + 1 ];
newPositions[ offset + 3 * i + 2 ] = positions[ 3 * index + 2 ];
for ( const j of split.indexes )
newIndexes[ j ] = offset / 3 + i;
}
geometry = new BufferGeometry();
geometry.setAttribute( 'position', new BufferAttribute( newPositions, 3, true ) );
geometry.setIndex( new BufferAttribute( newIndexes, 1 ) );
return geometry;
};
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - modifier - Edge split modifier</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<script type="module">
import * as THREE from '../build/three.module.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { OBJLoader } from "./jsm/loaders/OBJLoader.js";
import { BufferGeometryUtils } from "./jsm/utils/BufferGeometryUtils.js";
import { EdgeSplitModifier } from "./jsm/modifiers/EdgeSplitModifier.js";
import { GUI } from "./jsm/libs/dat.gui.module.js";
let renderer, scene, camera;
let modifier, mesh, baseGeometry;
const params = {
smoothShading: false,
edgeSplit: false,
cutOffAngle: 20,
};
init();
function init() {
const info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = '<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - Indexed geometry edge splitting';
document.body.appendChild( info );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight );
const controls = new OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // use if there is no animation loop
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.rotateSpeed = 0.35;
controls.minZoom = 1;
camera.position.set( 0, 0, 4 );
scene.add( new THREE.HemisphereLight( 0xffffff, 0x444444 ) );
new OBJLoader().load(
'./models/obj/cerberus/cerberus.obj',
function ( group ) {
// Retrieve Cerberus vertex positions only
const modelGeometry = group.children[ 0 ].geometry;
modelGeometry.deleteAttribute( 'normal' );
modelGeometry.deleteAttribute( 'uv' );
modifier = new EdgeSplitModifier();
baseGeometry = modelGeometry;
console.log( "Loaded" );
mesh = new THREE.Mesh( getGeometry(), new THREE.MeshStandardMaterial() );
mesh.material.flatShading = ! params.smoothShading;
mesh.rotateY( - Math.PI / 2 );
mesh.scale.set( 3.5, 3.5, 3.5 );
mesh.translateZ( 1.5 );
scene.add( mesh );
render();
}
);
window.addEventListener( 'resize', onWindowResize, false );
const gui = new GUI( { name: "Edge split modifier parameters" } );
gui.add( params, "smoothShading" ).onFinishChange( updateMesh );
gui.add( params, "edgeSplit" ).onFinishChange( updateMesh );
gui.add( params, "cutOffAngle" ).min( 0 ).max( 180 ).onFinishChange( updateMesh );
}
function onWindowResize() {
renderer.setSize( window.innerWidth, window.innerHeight );
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
render();
}
function getGeometry() {
let g;
if ( params.edgeSplit )
g = modifier.modify( baseGeometry, params.cutOffAngle * Math.PI / 180 );
else
g = BufferGeometryUtils.mergeVertices( baseGeometry );
g.computeVertexNormals();
return g;
}
function updateMesh() {
mesh.geometry = getGeometry();
mesh.material.flatShading = ! params.smoothShading;
mesh.material.needsUpdate = true;
render();
}
function render() {
renderer.render( scene, camera );
}
</script>
</body>
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册