DecalGeometry constructor

DecalGeometry(
  1. Mesh mesh,
  2. Vector3 position,
  3. Euler orientation, [
  4. Vector3? size,
])

mesh — Any mesh object.

position — Position of the decal projector.

orientation — Orientation of the decal projector.

size — Size of the decal projector.

Implementation

DecalGeometry(Mesh mesh, Vector3 position, Euler orientation,[ Vector3? size ]):super(){
  size ??= Vector3(1,1,1);

		final List<double> vertices = [];
		final List<double> normals = [];
		final List<double> uvs = [];

  final plane = Vector3();

  final normalMatrix = Matrix3.identity().getNormalMatrix( mesh.matrixWorld );

		// this matrix represents the transformation of the decal projector

		final projectorMatrix = Matrix4.identity();
		projectorMatrix.makeRotationFromEuler( orientation );
		projectorMatrix.setPosition( position.x,position.y,position.z );

		final projectorMatrixInverse = Matrix4.identity();
		projectorMatrixInverse.setFrom( projectorMatrix ).invert();

  void pushDecalVertex(List<DecalVertex> decalVertices,Vector3 vertex,[Vector3? normal]) {
    // transform the vertex to world space, then to projector space
    vertex.applyMatrix4( mesh.matrixWorld );
    vertex.applyMatrix4( projectorMatrixInverse );

			if ( normal != null) {
				normal.applyNormalMatrix(normalMatrix);
				decalVertices.add(DecalVertex(vertex.clone(), normal.clone()));
			}
    else {
				decalVertices.add(DecalVertex(vertex.clone()));
			}
  }

  DecalVertex clip(DecalVertex v0, DecalVertex v1, p, s ) {
    final d0 = v0.position.dot( p ) - s;
    final d1 = v1.position.dot( p ) - s;

    final s0 = d0 / ( d0 - d1 );

			final position = Vector3(
				v0.position.x + s0 * ( v1.position.x - v0.position.x ),
				v0.position.y + s0 * ( v1.position.y - v0.position.y ),
				v0.position.z + s0 * ( v1.position.z - v0.position.z )
			);

			Vector3? normal;

			if (v0.normal != null && v1.normal != null) {
				normal = Vector3(
					v0.normal!.x + s0 * ( v1.normal!.x - v0.normal!.x ),
					v0.normal!.y + s0 * ( v1.normal!.y - v0.normal!.y ),
					v0.normal!.z + s0 * ( v1.normal!.z - v0.normal!.z )
				);
			}

			final v = DecalVertex( position, normal );

			// need to clip more values (texture coordinates)? do it this way:
			// intersectpoint.value = a.value + s * ( b.value - a.value );

			return v;
  }

  List<DecalVertex> clipGeometry(List<DecalVertex> inVertices, plane ) {
    final List<DecalVertex> outVertices = [];
    final s = 0.5 * size!.dot( plane ).abs();

    // a single iteration clips one face,
    // which consists of three consecutive 'DecalVertex' objects

    for (int i = 0; i < inVertices.length; i += 3 ) {
      int total = 0;
      late DecalVertex nV1;
      late DecalVertex nV2;
      late DecalVertex nV3;
      late DecalVertex nV4;

      final d1 = inVertices[ i + 0 ].position.dot( plane ) - s;
      final d2 = inVertices[ i + 1 ].position.dot( plane ) - s;
      final d3 = inVertices[ i + 2 ].position.dot( plane ) - s;

      final v1Out = d1 > 0;
      final v2Out = d2 > 0;
      final v3Out = d3 > 0;

      // calculate, how many vertices of the face lie outside of the clipping plane

      total = ( v1Out ? 1 : 0 ) + ( v2Out ? 1 : 0 ) + ( v3Out ? 1 : 0 );

      switch ( total ) {
        case 0: {
          // the entire face lies inside of the plane, no clipping needed
          outVertices.add( inVertices[ i ] );
          outVertices.add( inVertices[ i + 1 ] );
          outVertices.add( inVertices[ i + 2 ] );
          break;
        }
        case 1: {
          // one vertex lies outside of the plane, perform clipping
          if ( v1Out ) {
            nV1 = inVertices[ i + 1 ];
            nV2 = inVertices[ i + 2 ];
            nV3 = clip( inVertices[ i ], nV1, plane, s );
            nV4 = clip( inVertices[ i ], nV2, plane, s );
          }

          if ( v2Out ) {
            nV1 = inVertices[ i ];
            nV2 = inVertices[ i + 2 ];
            nV3 = clip( inVertices[ i + 1 ], nV1, plane, s );
            nV4 = clip( inVertices[ i + 1 ], nV2, plane, s );

            outVertices.add( nV3 );
            outVertices.add( nV2.clone() );
            outVertices.add( nV1.clone() );

            outVertices.add( nV2.clone() );
            outVertices.add( nV3.clone() );
            outVertices.add( nV4 );
            break;
          }

          if ( v3Out ) {
            nV1 = inVertices[ i ];
            nV2 = inVertices[ i + 1 ];
            nV3 = clip( inVertices[ i + 2 ], nV1, plane, s );
            nV4 = clip( inVertices[ i + 2 ], nV2, plane, s );
          }

          outVertices.add( nV1.clone() );
          outVertices.add( nV2.clone() );
          outVertices.add( nV3 );

          outVertices.add( nV4 );
          outVertices.add( nV3.clone() );
          outVertices.add( nV2.clone() );

          break;
        }
        case 2: {
          // two vertices lies outside of the plane, perform clipping
          if ( ! v1Out ) {
            nV1 = inVertices[ i ].clone();
            nV2 = clip( nV1, inVertices[ i + 1 ], plane, s );
            nV3 = clip( nV1, inVertices[ i + 2 ], plane, s );
            outVertices.add( nV1 );
            outVertices.add( nV2 );
            outVertices.add( nV3 );
          }
          if ( ! v2Out ) {
            nV1 = inVertices[ i + 1 ].clone();
            nV2 = clip( nV1, inVertices[ i + 2 ], plane, s );
            nV3 = clip( nV1, inVertices[ i ], plane, s );
            outVertices.add( nV1 );
            outVertices.add( nV2 );
            outVertices.add( nV3 );
          }
          if ( ! v3Out ) {
            nV1 = inVertices[ i + 2 ].clone();
            nV2 = clip( nV1, inVertices[ i ], plane, s );
            nV3 = clip( nV1, inVertices[ i + 1 ], plane, s );
            outVertices.add( nV1 );
            outVertices.add( nV2 );
            outVertices.add( nV3 );

          }
          break;
        }
        case 3: {
          // the entire face lies outside of the plane, so let's discard the corresponding vertices
          break;
        }
      }
    }

    return outVertices;
  }

  void generate() {
    List<DecalVertex> decalVertices = [];

    final vertex = Vector3();
    final normal = Vector3();

    // handle different geometry types

    final geometry = mesh.geometry;

    final positionAttribute = geometry?.attributes['position'];
    final normalAttribute = geometry?.attributes['normal'];

    // first, create an array of 'DecalVertex' objects
    // three consecutive 'DecalVertex' objects represent a single face
    //
    // this data structure will be later used to perform the clipping

    if ( geometry?.index != null ) {
      // indexed BufferGeometry
      final index = geometry!.index;

      for (int i = 0; i < index!.count; i ++ ) {
        vertex.fromBuffer( positionAttribute, index.getX( i )!.toInt() );
        if ( normalAttribute != null) {
          normal.fromBuffer( normalAttribute, index.getX( i )!.toInt() );
          pushDecalVertex( decalVertices, vertex, normal );
        }
        else{
          pushDecalVertex( decalVertices, vertex );
        }
      }
    }
    else {
      // non-indexed BufferGeometry
      for (int i = 0; i < positionAttribute.count; i ++ ) {
        vertex.fromBuffer( positionAttribute, i );
					if ( normalAttribute != null) {
						normal.fromBuffer( normalAttribute, i );
						pushDecalVertex( decalVertices, vertex, normal );
					}
        else {
						pushDecalVertex( decalVertices, vertex );
					}
      }
    }

    // second, clip the geometry so that it doesn't extend out from the projector

    decalVertices = clipGeometry( decalVertices, plane.setValues( 1, 0, 0 ) );
    decalVertices = clipGeometry( decalVertices, plane.setValues( - 1, 0, 0 ) );
    decalVertices = clipGeometry( decalVertices, plane.setValues( 0, 1, 0 ) );
    decalVertices = clipGeometry( decalVertices, plane.setValues( 0, - 1, 0 ) );
    decalVertices = clipGeometry( decalVertices, plane.setValues( 0, 0, 1 ) );
    decalVertices = clipGeometry( decalVertices, plane.setValues( 0, 0, - 1 ) );

    // third, generate final vertices, normals and uvs

    for (int i = 0; i < decalVertices.length; i ++ ) {
      final decalVertex = decalVertices[ i ];

      // create texture coordinates (we are still in projector space)
      uvs.addAll([
        0.5 + ( decalVertex.position.x / size!.x ),
        0.5 + ( decalVertex.position.y / size.y )
      ]);

      // transform the vertex back to world space

      decalVertex.position.applyMatrix4( projectorMatrix );

      // now create vertex and normal buffer data

      vertices.addAll( [decalVertex.position.x, decalVertex.position.y, decalVertex.position.z] );
      if ( decalVertex.normal != null ) {
        normals.addAll( [decalVertex.normal!.x, decalVertex.normal!.y, decalVertex.normal!.z] );
      }
    }
  }
		// generate buffers
		generate();

		// build geometry
		setAttributeFromString( 'position', Float32BufferAttribute.fromList( vertices, 3 ) );
		setAttributeFromString( 'normal', Float32BufferAttribute.fromList( normals, 3 ) );
		setAttributeFromString( 'uv', Float32BufferAttribute.fromList( uvs, 2 ) );
}