<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://raw.githack.com/mrmola/arrow_assets/main/three.js"></script>
    <script type="text/javascript" src="https://raw.githack.com/mrmola/arrow_assets/main/OBJLoader.js"></script>
    <script type="text/javascript" src="https://raw.githack.com/mrmola/arrow_assets/main/MTLLoader.js"></script>
    <script type="text/javascript" src="https://unpkg.com/three@0.117.0/examples/js/controls/FlyControls.js"></script>
    <meta name="viewport" content="width=device-width">
    <style>body {
        background-color: black;
        margin: 0px;
    }
    div#interface {
        position:absolute;
        width: 100%;
        z-index:100;
    }
    div#interface {
        position:absolute;
        width: 100%;
        z-index:100;
    }
    </style>
    <link href="style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <div style="position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px; visibility: visible;" id="viewport"> </div>
  </body>
</html>
var renderer = new THREE.WebGLRenderer({ antialias: true, alpha:true, preserveDrawingBuffer:true });
var ambientLight = new THREE.AmbientLight(0x999999);
renderer.setSize( window.innerWidth, window.innerHeight );
let gl = renderer.context;
let camera = new THREE.PerspectiveCamera(
	75,
	window.innerWidth / window.innerHeight,
	0.1,
	1000
);
renderer.autoClear = false;
renderer.autoClearColor = false;
renderer.autoClearDepth = false;
renderer.autoClearStencil = false;
renderer.context.getExtension("EXT_frag_depth");
gl.getExtension("WEBGL_depth_texture");
var ext = gl.getExtension('WEBGL_draw_buffers');
document.getElementById("viewport").appendChild( renderer.domElement );
controls = new THREE.FlyControls( camera, renderer.domElement );
controls.movementSpeed = 10;
controls.domElement = renderer.domElement;
controls.rollSpeed = Math.PI / 6;
controls.autoForward = false;
controls.dragToLook = true;
var ctx = renderer.domElement.getContext('2d');
var scene = new THREE.Scene;
const dividingChar = "•";
scene.add(ambientLight);
ambientLight.layers.enable(5);
camera.position.set( 0, 0, 10 );
scene.add( camera );
camera.lookAt(0,0,0)
camera.rotation.order = "YXZ"; 
	this.updateRotationVector = function () {
		this.rotationVector.y = ( - this.moveState.yawRight + this.moveState.yawLeft );
		this.rotationVector.z = ( - this.moveState.rollRight + this.moveState.rollLeft );


};
let colorBuffer = new THREE.WebGLRenderTarget( renderer.context.canvas.width, renderer.context.canvas.width, {stencilBuffer:false, depthBuffer:true,
  magFilter: THREE.LinearFilter,
  minFilter: THREE.LinearFilter,
  anisotropy: 0,
  generateMipmaps: false});
let normalBuffer = new THREE.WebGLRenderTarget( renderer.context.canvas.width, renderer.context.canvas.width, {stencilBuffer:false, depthBuffer:true,
  magFilter: THREE.LinearFilter,
  minFilter: THREE.LinearFilter,
  anisotropy: 0,
  generateMipmaps: false});
const vertexDensity = 0.05;
const brushTexture = new THREE.TextureLoader().load( 'https://raw.githubusercontent.com/mrmola/arrow_assets/main/brush_stroke.png' );
brushTexture.magFilter = THREE.NearestFilter;
brushTexture.minFilter = THREE.NearestFilter;
let pointMaterial = new THREE.RawShaderMaterial( {
	uniforms: {
    iResolution: { get value(){ return new THREE.Vector2(renderer.context.canvas.width, renderer.context.canvas.height) }},
    //strokeTexture is what is sampled from when checking the brush shape size thing
    u_strokeTexture: { value:brushTexture },
    u_color: { value: colorBuffer.texture },
    u_normal: { value: normalBuffer.texture },
    //pointSize is the size of the point
    pointSize: {value:1/vertexDensity}
	},
  //the arrow shaders
  vertexShader: `
precision mediump float; precision mediump int;
const float strokeSize = 256.;
attribute vec2 position; 
uniform float pointSize;
uniform sampler2D u_normal;
uniform vec2 iResolution; 
//the stroke position on the stroke texture texture
varying vec2 strokePosition;
//the stroke's rotation
varying vec2 strokeRotation;
//the strokes dimensions
varying vec2 strokeDimensions;
//the color
varying vec3 v_color;
float unmapVector(float vector) {
  return (vector*2.)-1.;
}
vec2 unmapVector(vec2 vector) {
  return (vector*2.)-vec2(1.);
}
vec3 unmapVector(vec3 vector) {
  return (vector*2.)-vec3(1.);
}
vec4 unmapVector(vec4 vector) {
  return (vector*2.)-vec4(1.);
}
void main() {
  vec2 endPosition = position;
  vec4 osize = texture2D(u_normal, endPosition.xy);
  vec4 tempOsize = unmapVector(osize);
  strokeRotation = normalize(vec2(tempOsize.y, tempOsize.x));
  gl_Position = vec4((endPosition.xy)*2.-vec2(1,1), 0., 1.);
  strokePosition = vec2(1,1);
  strokeDimensions = vec2(0.75,0.5);
  mat2 rotationMatrix = mat2(
    strokeRotation.x,-strokeRotation.y,  
    strokeRotation.y,strokeRotation.x
  ); 
  mat2 scaleMatrix = mat2(
    strokeDimensions.x, 0,
    0, strokeDimensions.y
  );
  vec2 pointA = vec2(1, -1);
  vec2 pointB = vec2(1, 1);
  pointA = abs(pointSize*scaleMatrix*rotationMatrix*pointA);
  pointB = abs(pointSize*scaleMatrix*rotationMatrix*pointB);
  gl_PointSize = max(pointA.x, pointB.x);
  gl_PointSize = max(gl_PointSize, pointB.y);
  gl_PointSize = max(gl_PointSize, pointA.y);
  gl_PointSize = max(gl_PointSize, pointSize);
}`,
  fragmentShader: `
#extension GL_EXT_frag_depth : enable
#define TEST 8
precision mediump float;
precision mediump int;
//do not change
const float strokeSize = 256.;
uniform sampler2D u_strokeTexture;
uniform sampler2D u_normal;
uniform sampler2D u_color;
uniform vec2 iResolution;
varying vec2 strokeRotation;
varying vec2 strokeDimensions;
varying vec2 strokePosition;
varying vec3 v_color;
void main(){
  float singleStrokeSize = strokeSize/4.;
  mat2 rotationMatrix = mat2(
    strokeRotation.x,-strokeRotation.y,
    strokeRotation.y,strokeRotation.x
  );
  mat2 scaleMatrix = mat2(
    strokeDimensions.x*2., 0,
    0, strokeDimensions.y*2.
  );
  vec2 basedCoords = gl_PointCoord-vec2(0.5,0.5);
  vec2 offset = (scaleMatrix*rotationMatrix*basedCoords+vec2(0.5,0.5))*singleStrokeSize;
  vec4 strokeSample = texture2D(u_strokeTexture,(strokePosition*singleStrokeSize+offset)/strokeSize);
  //yes, i know i can replace this if statement with a mix thing. I haven't optimized
  if(
    (strokeSample.r == strokeSample.g && strokeSample.g == strokeSample.b) ||
    (offset.x > singleStrokeSize || offset.x < 0. || offset.y < 0. || offset.y > singleStrokeSize) 
  ) {
    gl_FragDepthEXT = 100.;
    vec4 color = texture2D(u_color, gl_FragCoord.xy/iResolution);
    gl_FragColor = color;
  } else {
    gl_FragColor = texture2D(u_normal, gl_FragCoord.xy/iResolution);
  }
}`
} );
let orientationMaterial= new THREE.MeshPhongMaterial();
orientationMaterial.extensions = {
   derivatives: true
};
orientationMaterial.onBeforeCompile = function(shader) {
  shader.uniforms.iResolution = { get value(){ return new THREE.Vector2(renderer.context.canvas.width, renderer.context.canvas.height) }};
  shader.uniforms.viewNormal =  {get value() {  
            let rot = vec2(camera.rotation.x,camera.rotation.y);
            return vec3(
              Math.cos(rot.x)*Math.cos(additionalAngle+rot.y),
              Math.sin(-rot.x),
              Math.cos(rot.x)*Math.sin(additionalAngle+rot.y)
            ).normalize();
          }}; 
          let tfs = shader.fragmentShader;
          let tvs = shader.vertexShader;
          tvs = tvs.split('\n');
          tfs = tfs.split('\n');
          tfs.splice(0,0, `uniform vec3 viewNormal;`);
           //the orientation map output
          tfs.splice(tfs.length-1, 0, `
gl_FragColor = vec4(normalize(vec2(
    dFdx(vUv.y),
    dFdy(vUv.y)
  )
)/2.+0.5,0,0);
`);
          //changes go here.
          tvs = tvs.join('\n');
          tfs = tfs.join('\n');
  console.log(tfs);
          shader.fragmentShader = tfs;
          shader.vertexShader = tvs;
}
let strokeMesh = new THREE.Points(new THREE.BufferGeometry(), pointMaterial);
strokeMesh.layers.enable(4);
strokeMesh.layers.disable(0);
strokeMesh.layers.disable(1);
makePointArray(new THREE.Vector2(renderer.context.canvas.width, renderer.context.canvas.height));
/** Fix the stroke array so that it does the thing
 * 
 * @param {Vector2} newSize Size of the screen in pixels
 */
function makePointArray(newSize) {
  let pointGeometry = new THREE.BufferGeometry();
  const positions = new Float32Array( Math.ceil(2*(Math.ceil(newSize.x*vertexDensity+1)*Math.ceil(newSize.y*vertexDensity)))); // 3 vertices per point
  let bp = 0;//base position
  for(let i = 0; i < newSize.x*vertexDensity+1; i++) {
    for(let j = 0; j < newSize.y*vertexDensity; j++) {
      positions[bp] = i/(newSize.x*vertexDensity);
      positions[bp+1] = j/(newSize.y*vertexDensity);
      bp+=2;
    }
  }
  pointGeometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 2 ) );
  //positions.length/2 will always be a whole number
  const strokeIDs = new Int8Array(positions.length/2);
  for(let i = 0; i < positions.length/2; i++) {
    strokeIDs[i] = Math.floor(Math.random()*16);
  }
  const strokeDepth = new Float32Array(positions.length/2);
  for(let i = 0; i < positions.length/2; i++) {
    strokeDepth[i] = Math.random();
  }
  let oldGeometry = strokeMesh.geometry;
  strokeMesh.geometry = pointGeometry;
  oldGeometry.dispose();
}
scene.add(strokeMesh);
let clock = new THREE.Clock();
var render = function(b) {
camera.rotation.z = 0;
  const delta = clock.getDelta();
  controls.update(delta);
  renderer.clear(true, true, true);
  camera.layers.enable(0);
  renderer.setRenderTarget(colorBuffer);
  renderer.clear(true, true, true);
  renderer.render(scene, camera);
  
  renderer.setRenderTarget(normalBuffer);
  scene.overrideMaterial = orientationMaterial;
  renderer.clear(true, true, true);
  renderer.render(scene, camera);
  scene.overrideMaterial = null;
  camera.layers.disable(0);
  
  camera.layers.enable(4);
  renderer.setRenderTarget(null);
  renderer.render(scene, camera);
  camera.layers.disable(4);
  
  requestAnimationFrame( render );
};
var mtlLoader = new THREE.MTLLoader();
mtlLoader.path = "https://raw.githubusercontent.com/mrmola/arrow_assets/main/";
let model = null;
mtlLoader.load("obj.mtl", function(e) {
  console.log("we got him x4");
  let OBJLoader = new THREE.OBJLoader();
  OBJLoader.setMaterials(e);
  OBJLoader.path = "https://raw.githubusercontent.com/mrmola/arrow_assets/main/";
  e.preload();
  OBJLoader.load("obj.obj",function(obj) {
    console.log("we got him x5");
    let deleteMe = obj;
    model = deleteMe.children[0];
    deleteMe.children.forEach(element => {
      scene.add(element);
      orientationMaterial.map = element.material.map;
      console.log(element);
render(true);
    console.log(model);
    });
  })
}, function(e){ 
  console.log("problem");
  console.log(e);
}, function(e){ 
  console.log("ERROR: ", e);
});
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.