Selaa lähdekoodia

LDrawLoader: Check if a face edge is a sub segment of a long edge when smoothing normals (#23077)

* Add support for hard edges when they are a subsegment of a hard edge

* Track the number of materials per part

* Propagate face materials

* update comments
Garrett Johnson 3 vuotta sitten
vanhempi
commit
91e4d297e5
1 muutettua tiedostoa jossa 119 lisäystä ja 4 poistoa
  1. 119 4
      examples/jsm/loaders/LDrawLoader.js

+ 119 - 4
examples/jsm/loaders/LDrawLoader.js

@@ -13,7 +13,8 @@ import {
 	ShaderMaterial,
 	UniformsLib,
 	UniformsUtils,
-	Vector3
+	Vector3,
+	Ray
 } from '../../../build/three.module.js';
 
 // Special surface finish tag types.
@@ -187,7 +188,8 @@ function generateFaceNormals( faces ) {
 
 }
 
-function smoothNormals( faces, lineSegments ) {
+const _ray = new Ray();
+function smoothNormals( faces, lineSegments, checkSubSegments = false ) {
 
 	function hashVertex( v ) {
 
@@ -207,7 +209,27 @@ function smoothNormals( faces, lineSegments ) {
 
 	}
 
+	// converts the two vertices to a ray with a normalized direction and origin of 0, 0, 0 projected
+	// onto the original line.
+	function toNormalizedRay( v0, v1, targetRay ) {
+
+		targetRay.direction.subVectors( v1, v0 ).normalize();
+
+		const scalar = v0.dot( targetRay.direction );
+		targetRay.origin.copy( v0 ).addScaledVector( targetRay.direction, - scalar );
+
+		return targetRay;
+
+	}
+
+	function hashRay( ray ) {
+
+		return hashEdge( ray.origin, ray.direction );
+
+	}
+
 	const hardEdges = new Set();
+	const hardEdgeRays = new Map();
 	const halfEdgeList = {};
 	const normals = [];
 
@@ -221,6 +243,43 @@ function smoothNormals( faces, lineSegments ) {
 		hardEdges.add( hashEdge( v0, v1 ) );
 		hardEdges.add( hashEdge( v1, v0 ) );
 
+		// only generate the hard edge ray map if we're checking subsegments because it's more expensive to check
+		// and requires more memory.
+		if ( checkSubSegments ) {
+
+			// add both ray directions to the map
+			const ray = toNormalizedRay( v0, v1, new Ray() );
+			const rh1 = hashRay( ray );
+			if ( ! hardEdgeRays.has( rh1 ) ) {
+
+				toNormalizedRay( v1, v0, ray );
+				const rh2 = hashRay( ray );
+
+				const info = {
+					ray,
+					distances: [],
+				};
+
+				hardEdgeRays.set( rh1, info );
+				hardEdgeRays.set( rh2, info );
+
+			}
+
+			// store both segments ends in min, max order in the distances array to check if a face edge is a
+			// subsegment later.
+			const info = hardEdgeRays.get( rh1 );
+			let d0 = info.ray.direction.dot( v0 );
+			let d1 = info.ray.direction.dot( v1 );
+			if ( d0 > d1 ) {
+
+				[ d0, d1 ] = [ d1, d0 ];
+
+			}
+
+			info.distances.push( d0, d1 );
+
+		}
+
 	}
 
 	// track the half edges associated with each triangle
@@ -238,7 +297,53 @@ function smoothNormals( faces, lineSegments ) {
 			const hash = hashEdge( v0, v1 );
 
 			// don't add the triangle if the edge is supposed to be hard
-			if ( hardEdges.has( hash ) ) continue;
+			if ( hardEdges.has( hash ) ) {
+
+				continue;
+
+			}
+
+			// if checking subsegments then check to see if this edge lies on a hard edge ray and whether its within any ray bounds
+			if ( checkSubSegments ) {
+
+				toNormalizedRay( v0, v1, _ray );
+
+				const rayHash = hashRay( _ray );
+				if ( hardEdgeRays.has( rayHash ) ) {
+
+					const info = hardEdgeRays.get( rayHash );
+					const { ray, distances } = info;
+					let d0 = ray.direction.dot( v0 );
+					let d1 = ray.direction.dot( v1 );
+
+					if ( d0 > d1 ) {
+
+						[ d0, d1 ] = [ d1, d0 ];
+
+					}
+
+					// return early if the face edge is found to be a subsegment of a line edge meaning the edge will have "hard" normals
+					let found = false;
+					for ( let i = 0, l = distances.length; i < l; i += 2 ) {
+
+						if ( d0 >= distances[ i ] && d1 <= distances[ i + 1 ] ) {
+
+							found = true;
+							break;
+
+						}
+
+					}
+
+					if ( found ) {
+
+						continue;
+
+					}
+
+				}
+
+			}
 
 			const info = {
 				index: index,
@@ -1005,6 +1110,7 @@ class LDrawLoader extends Loader {
 			lineSegments: [],
 			conditionalSegments: [],
 			totalFaces: 0,
+			faceMaterials: new Set(),
 
 			// If true, this object is the start of a construction step
 			startingConstructionStep: false
@@ -1751,6 +1857,8 @@ class LDrawLoader extends Loader {
 
 					}
 
+					currentParseScope.faceMaterials.add( material );
+
 					break;
 
 					// Line type 4: Quadrilateral
@@ -1883,7 +1991,11 @@ class LDrawLoader extends Loader {
 		if ( this.smoothNormals && doSmooth ) {
 
 			generateFaceNormals( subobjectParseScope.faces );
-			smoothNormals( subobjectParseScope.faces, subobjectParseScope.lineSegments );
+
+			// only check subsetgments if we have multiple materials in a single part because this seems to be the case where it's needed most --
+			// there may be cases where a single edge line crosses over polygon edges that are broken up by multiple materials.
+			const checkSubSegments = subobjectParseScope.faceMaterials.size > 1;
+			smoothNormals( subobjectParseScope.faces, subobjectParseScope.lineSegments, checkSubSegments );
 
 		}
 
@@ -1927,10 +2039,12 @@ class LDrawLoader extends Loader {
 			const parentLineSegments = parentParseScope.lineSegments;
 			const parentConditionalSegments = parentParseScope.conditionalSegments;
 			const parentFaces = parentParseScope.faces;
+			const parentFaceMaterials = parentParseScope.faceMaterials;
 
 			const lineSegments = subobjectParseScope.lineSegments;
 			const conditionalSegments = subobjectParseScope.conditionalSegments;
 			const faces = subobjectParseScope.faces;
+			const faceMaterials = subobjectParseScope.faceMaterials;
 
 			for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
 
@@ -1987,6 +2101,7 @@ class LDrawLoader extends Loader {
 			}
 
 			parentParseScope.totalFaces += subobjectParseScope.totalFaces;
+			faceMaterials.forEach( material => parentFaceMaterials.add( material ) );
 
 		}