<script>
	var conditionalLineVertShader = /* glsl */`
	attribute vec3 control0;
	attribute vec3 control1;
	attribute vec3 direction;
  attribute float collapse;
  attribute vec3 instPos;

	#include <common>
	#include <color_pars_vertex>
	#include <fog_pars_vertex>
	#include <logdepthbuf_pars_vertex>
	#include <clipping_planes_pars_vertex>
	void main() {
		#include <color_vertex>

		// Transform the line segment ends and control points into camera clip space
		vec4 c0 = projectionMatrix * modelViewMatrix * vec4( control0 + instPos, 1.0 );
		vec4 c1 = projectionMatrix * modelViewMatrix * vec4( control1 + instPos, 1.0 );
		vec4 p0 = projectionMatrix * modelViewMatrix * vec4( position + instPos, 1.0 );
		vec4 p1 = projectionMatrix * modelViewMatrix * vec4( position + instPos + direction, 1.0 );

		c0.xy /= c0.w;
		c1.xy /= c1.w;
		p0.xy /= p0.w;
		p1.xy /= p1.w;

		// Get the direction of the segment and an orthogonal vector
		vec2 dir = p1.xy - p0.xy;
		vec2 norm = vec2( -dir.y, dir.x );

		// Get control point directions from the line
		vec2 c0dir = c0.xy - p1.xy;
		vec2 c1dir = c1.xy - p1.xy;

		// If the vectors to the controls points are pointed in different directions away
		// from the line segment then the line should not be drawn.
		float d0 = dot( normalize( norm ), normalize( c0dir ) );
		float d1 = dot( normalize( norm ), normalize( c1dir ) );
		float discardFlag = float( sign( d0 ) != sign( d1 ) );
    
    vec3 p = position + instPos + ((discardFlag > 0.5) ? direction * collapse : vec3(0));    
    vec4 mvPosition = modelViewMatrix * vec4( p, 1.0 );
		gl_Position = projectionMatrix * mvPosition;

		#include <logdepthbuf_vertex>
		#include <clipping_planes_vertex>
		#include <fog_vertex>
	}
	`;

	var conditionalLineFragShader = /* glsl */`
	uniform vec3 diffuse;
	uniform float opacity;

	#include <common>
	#include <color_pars_fragment>
	#include <fog_pars_fragment>
	#include <logdepthbuf_pars_fragment>
	#include <clipping_planes_pars_fragment>
	void main() {
		#include <clipping_planes_fragment>
		vec3 outgoingLight = vec3( 0.0 );
		vec4 diffuseColor = vec4( diffuse, opacity );
		#include <logdepthbuf_fragment>
		#include <color_fragment>
		outgoingLight = diffuseColor.rgb; // simple shader
		gl_FragColor = vec4( outgoingLight, diffuseColor.a );
		#include <tonemapping_fragment>
		#include <encodings_fragment>
		#include <fog_fragment>
		#include <premultiplied_alpha_fragment>
	}
	`;
</script>
body{
  overflow: hidden;
  margin: 0;
}
import * as THREE from 'https://cdn.skypack.dev/[email protected]';
import {OrbitControls} from 'https://cdn.skypack.dev/[email protected]/examples/jsm/controls/OrbitControls.js';
import {GLTFLoader} from 'https://cdn.skypack.dev/[email protected]/examples/jsm/loaders/GLTFLoader.js';
import {GUI} from 'https://cdn.skypack.dev/[email protected]/examples/jsm/libs/dat.gui.module.js';
import * as BufferGeometryUtils  from 'https://cdn.skypack.dev/[email protected]/examples/jsm/utils/BufferGeometryUtils.js'


var conditionalLineVertShader = /* glsl */`
attribute vec3 control0;
attribute vec3 control1;
attribute vec3 direction;
attribute float collapse;
attribute vec3 instPos;

#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
void main() {
      #include <color_vertex>

      // Transform the line segment ends and control points into camera clip space
      vec4 c0 = projectionMatrix * modelViewMatrix * vec4( control0 + instPos, 1.0 );
      vec4 c1 = projectionMatrix * modelViewMatrix * vec4( control1 + instPos, 1.0 );
      vec4 p0 = projectionMatrix * modelViewMatrix * vec4( position + instPos, 1.0 );
      vec4 p1 = projectionMatrix * modelViewMatrix * vec4( position + instPos + direction, 1.0 );

      c0.xy /= c0.w;
      c1.xy /= c1.w;
      p0.xy /= p0.w;
      p1.xy /= p1.w;

      // Get the direction of the segment and an orthogonal vector
      vec2 dir = p1.xy - p0.xy;
      vec2 norm = vec2( -dir.y, dir.x );

      // Get control point directions from the line
      vec2 c0dir = c0.xy - p1.xy;
      vec2 c1dir = c1.xy - p1.xy;

      // If the vectors to the controls points are pointed in different directions away
      // from the line segment then the line should not be drawn.
      float d0 = dot( normalize( norm ), normalize( c0dir ) );
      float d1 = dot( normalize( norm ), normalize( c1dir ) );
      float discardFlag = float( sign( d0 ) != sign( d1 ) );

vec3 p = position + instPos + ((discardFlag > 0.5) ? direction * collapse : vec3(0));    
vec4 mvPosition = modelViewMatrix * vec4( p, 1.0 );
      gl_Position = projectionMatrix * mvPosition;

      #include <logdepthbuf_vertex>
      #include <clipping_planes_vertex>
      #include <fog_vertex>
}
`;

var conditionalLineFragShader = /* glsl */`
uniform vec3 diffuse;
uniform float opacity;

#include <common>
#include <color_pars_fragment>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
void main() {
      #include <clipping_planes_fragment>
      vec3 outgoingLight = vec3( 0.0 );
      vec4 diffuseColor = vec4( diffuse, opacity );
      #include <logdepthbuf_fragment>
      #include <color_fragment>
      outgoingLight = diffuseColor.rgb; // simple shader
      gl_FragColor = vec4( outgoingLight, diffuseColor.a );
      #include <tonemapping_fragment>
      #include <encodings_fragment>
      #include <fog_fragment>
      #include <premultiplied_alpha_fragment>
}
`;


function EdgesGeometry( geometry, thresholdAngle ) {

	let g = new THREE.InstancedBufferGeometry();

	g.type = 'EdgesGeometry';

	g.parameters = {
		thresholdAngle: thresholdAngle
	};

	thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1;

	// buffer

	const vertices = [];
  const control0 = [];
  const control1 = [];
  const direction = [];
  const collapse = [];

	// helper variables

      const thresholdDot = Math.cos( THREE.MathUtils.DEG2RAD * thresholdAngle );

	const edge = [ 0, 0 ], edges = {}, eArray = [];
	let edge1, edge2, key;
	const keys = [ 'a', 'b', 'c' ];

	// prepare source geometry

	let geometry2, geometry2a;

	if ( geometry.isBufferGeometry ) {

		geometry2a = geometry.clone();
		//geometry2.fromBufferGeometry( geometry );

	} else {

		geometry2a = geometry.clone();

  }
  //console.log(geometry2a.index.count / geometry2a.attributes.position.array.length);
  var ratio = (  geometry2a.attributes.position.array.length / geometry2a.index.count ) 
  geometry2 = BufferGeometryUtils.mergeVertices(geometry2a, ratio);
console.log(geometry2a);
	//geometry2.mergeVertices();
  geometry2.computeVertexNormals();


      const sourceVertices = geometry2.attributes.position;

      var sv = [];
      var normalss = []
      const faces = [];
      const vs = [];
      var ori = new THREE.Vector3()
      
       var a = new THREE.Vector3();
       var b = new THREE.Vector3()
       var c = new THREE.Vector3();
       var tri = new THREE.Triangle()


      for ( let s = 0; s < sourceVertices.array.length; s ++ ){
        sv.push(new THREE.Vector3(sourceVertices.array[s * 3 + 0], sourceVertices.array[s * 3 + 1], sourceVertices.array[s * 3 + 2]))
      }
      scene.updateMatrixWorld() 
      
var index = geometry2.index;  
var facess = index.count / 3;
var normIdx = []; 

      for (let i = 0; i < facess; i++) {
        var  triy = {};
        triy.a = index.array[i * 3 + 0];
        triy.b = index.array[i * 3 + 1];
        triy.c = index.array[i * 3 + 2];

        var dir = new THREE.Vector3();
        a.fromBufferAttribute(sourceVertices, index.array[i * 3 + 0]);
        b.fromBufferAttribute(sourceVertices, index.array[i * 3 + 1]);
        c.fromBufferAttribute(sourceVertices, index.array[i * 3 + 2]);
        tri.set(a, b, c);
        tri.getMidpoint(ori);
        tri.getNormal(dir);

          //triArr.push(triy)


        triy.normal = dir
        faces.push(triy)


        //console.log(tri1);
        //console.log(tri1);
      }



      


      for (let i = 0; i < faces.length; i++) {
      var face = faces[i]




		for ( let j = 0; j < 3; j ++ ) {
      
                  edge1 = face[ keys[ j ] ];
                  
                  
			edge2 = face[ keys[ ( j + 1 ) % 3 ] ];
			edge[ 0 ] = Math.min( edge1, edge2 );
			edge[ 1 ] = Math.max( edge1, edge2 );

			key = edge[ 0 ] + ',' + edge[ 1 ];

			if ( edges[ key ] === undefined ) {

				edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined };

			} else {

				edges[ key ].face2 = i;

                  }
                  
                  

		}

  }


	// generate vertices
  const v3 = new THREE.Vector3();
  const n = new THREE.Vector3();
  const n1 = new THREE.Vector3();
  const n2 = new THREE.Vector3();
  const d = new THREE.Vector3();



	for ( key in edges ) {

    //console.log( key);
            const e = edges[ key ];
            



		// an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree.
//console.log(faces);
if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) {
//console.log('fshg');
  let vertex1 = sv[ e.index1 ];
  let vertex2 = sv[ e.index2 ];
  //console.log(vertex1);
  
  vertices.push( vertex1.x, vertex1.y, vertex1.z );
  vertices.push( vertex2.x, vertex2.y, vertex2.z );


      //console.log(vertices);
      
      d.subVectors(vertex2, vertex1);
      collapse.push(0, 1);
      n.copy(d).normalize();
      direction.push(d.x, d.y, d.z);
      n1.copy(faces[ e.face1 ].normal);
      n1.crossVectors(n, n1);
      d.subVectors(vertex1, vertex2);
      n.copy(d).normalize();
      n2.copy(faces[ e.face2 ].normal);
      n2.crossVectors(n, n2);
      direction.push(d.x, d.y, d.z);
      
      v3.copy(vertex1).add(n1); // control0
      control0.push(v3.x, v3.y, v3.z);
      v3.copy(vertex1).add(n2); // control1
      control1.push(v3.x, v3.y, v3.z);
      
      v3.copy(vertex2).add(n1); // control0
      control0.push(v3.x, v3.y, v3.z);
      v3.copy(vertex2).add(n2); // control1
      control1.push(v3.x, v3.y, v3.z);
    }

	}

	// build geometry
      //g.setAttribute( 'position', new THREE.BufferAttribute (new Float32Array( vertices), 3)  );
	g.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
  g.setAttribute( 'control0', new THREE.Float32BufferAttribute( control0, 3 ) );
  g.setAttribute( 'control1', new THREE.Float32BufferAttribute( control1, 3 ) );
  g.setAttribute( 'direction', new THREE.Float32BufferAttribute( direction, 3 ) );
  g.setAttribute( 'collapse', new THREE.Float32BufferAttribute( collapse, 1 ) );
console.log(g);
  return g;
  
}


var raycaster = new THREE.Raycaster(), rayhelper, mesh, instanceObj;

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 10000);
camera.position.set(300, 80, 0);
camera.lookAt(scene.position);
let renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor(0x444444);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

let controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 80, 0);
controls.update();

var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.DirectionalLight(0xffffff, 0.5));

var loader = new GLTFLoader();
loader.load( "https://3dpk.co.uk/cube.glb", function ( gltf ) {
  
  let container = new THREE.Group();
  
  mesh = gltf.scene.children[ 0 ];
  mesh.material.color.set(0x222222);
  mesh.material.vertexColors = false;
  mesh.material.polygonOffset = true;
  mesh.material.polygonOffsetFactor = 1;
  //mesh.material.wireframe = true
  
  let outline = createOutlineSegments(mesh.geometry, 0xffffff);
  let instPos = [];
  
  instanceObj = new THREE.InstancedMesh( mesh.geometry, mesh.material, 20 )

const matrix = new THREE.Matrix4();


instanceObj.instanceMatrix.setUsage( THREE.DynamicDrawUsage );

for ( let bm = 0; bm < 20 ; bm ++ ) {

      randomizeMatrixA( matrix, instPos );
      instanceObj.setMatrixAt( bm, matrix );

}
   //console.log(instPos);
    outline.geometry.setAttribute("instPos", new THREE.InstancedBufferAttribute(new Float32Array(instPos), 3));
    //console.log(outline.geometry);
   container.add(instanceObj);
  container.add(outline);

  //container.add(outline);
  //container.add(mesh);
  
  scene.add(container);
	//console.log(mesh);
  
  let gui = new GUI();
  gui.add(instanceObj, "visible").name("mesh");
  gui.add(outline, "visible").name("outline");

  //calculateFaces()

} );

function randomizeMatrixA( matrix, instPos ) {
  
  var trashPosition = new THREE.Vector3();
  var scale = new THREE.Vector3()
const quaternion = new THREE.Quaternion();


            trashPosition.x = Math.random() * 800 - 400;
            trashPosition.y = Math.random() * 800 - 400;
            trashPosition.z = Math.random() * 800 - 400;
  
            instPos.push(trashPosition.x, trashPosition.y, trashPosition.z);
            scale.x = scale.y = scale.z = 1;

            matrix.compose( trashPosition, quaternion, scale );
            
     
}


function calculateFaces(){
  mesh.geometry.computeVertexNormals();
  var posy =  mesh.geometry.attributes.position; 

  var oriy = new THREE.Vector3();
  var diry = new THREE.Vector3();
  var ay = new THREE.Vector3(),
  by = new THREE.Vector3(),
  cy = new THREE.Vector3()
  var  triy = new THREE.Triangle();
 var triArr = []


  var indexy = mesh.geometry.index;   
  var normIdx = mesh.geometry.attributes.normal.array; 
  var facesy = indexy.count / 3;
  //scene.updateMatrixWorld()
  console.log(mesh);
  for (let i = 0; i < facesy; i++) {

    ay.fromBufferAttribute(posy, indexy.array[i * 3 + 0]);
    by.fromBufferAttribute(posy, indexy.array[i * 3 + 1]);
    cy.fromBufferAttribute(posy, indexy.array[i * 3 + 2]);
    triy.set(ay, by, cy);
    triy.getMidpoint(oriy);
    triy.getNormal(diry);
      triArr.push(triy)
      
      //raycaster.set(oriy, diry);    
      //intersects = raycaster.intersectObject(objsFinal, false);

      // if ( intersects.length > 0 ) {
      //     if(intersects[ 0 ].face){            
      //         cont.push(intersects[ 0 ].face)
      //         faceindex.push(intersects[ 0 ].faceIndex)            
      //     }        
      // }

      //rayhelper = new THREE.ArrowHelper( raycaster.ray.direction, raycaster.ray.origin, 5, Math.random() * 0xffffff )
      //scene.add(rayhelper);

  }

  console.log(triArr);

}

renderer.setAnimationLoop(()=>{
  renderer.render(scene, camera);
});

function createOutlineSegments(geometry, color){
  let eg = EdgesGeometry(geometry);
  let m = new THREE.ShaderMaterial({
    vertexShader: conditionalLineVertShader,
    fragmentShader: conditionalLineFragShader,
    uniforms: {
      diffuse: {
        value: new THREE.Color(color)
      },
      opacity: {
        value: 0
      }
    },
    transparent: false
  });
  let o = new THREE.LineSegments(eg, m);
    o.frustumCulled = false
  console.log(o);
  return o;
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.