Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shshaw/878807cb460d2c6ec246bba0319239ed to your computer and use it in GitHub Desktop.
Save shshaw/878807cb460d2c6ec246bba0319239ed to your computer and use it in GitHub Desktop.
3D Mesh Manipulation, push & pull (#3December - Day 2)
console.clear();
let opts = {
variance: { x: 3, y: 3, z: 10 },
planeSize: 30,
power: 0.4,
brushSize: 40,
subtract: false,
speed: 2,
rotate: false,
image: 'http://brokensquare.com/Code/assets/face.png',
randomImage(){
this.image = 'https://unsplash.it/512/512?image=' + Math.floor(Math.random() * 1100);
loadTexture(this.image);
},
easing: Elastic.easeOut//Linear.easeNone
};
/*////////////////////////////////////////*/
let gui = new dat.GUI();
gui.closed = window.innerWidth < 600;
let image = gui.add(opts, 'image', {
Face: 'http://brokensquare.com/Code/assets/face.png',
Lion: 'https://unsplash.it/400/400?image=1074',
Water: 'https://unsplash.it/400/400?image=1053',
YellowCurtain: 'https://unsplash.it/400/400?image=855',
Tunnel: 'https://unsplash.it/400/400?image=137'
});
image.onChange(loadTexture);
let random = gui.add(opts, 'randomImage');
/*////////////////////////////////////////*/
gui.add(opts, 'subtract').listen();
gui.add(opts, 'power', 0.1, 1).step(0.1);
/*////////////////////////////////////////*/
let brush = gui.add(opts, 'brushSize', 5, 100).step(1).listen();
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
canvas.className = 'brush';
document.body.appendChild(canvas);
function drawBrush(){
let s = opts.brushSize;
canvas.width = canvas.height = s;
ctx.clearRect(0,0, s, s);
ctx.beginPath();
ctx.arc( s/2, s/2 ,s/2.1, 0, 2*Math.PI);
ctx.strokeStyle = '#AAA';
ctx.lineWidth = 1;
ctx.stroke();
}
brush.onChange(drawBrush);
drawBrush();
let mouseX = 0;
let mouseY = 0;
let frame;
function updateBrush(){
canvas.style.transform = 'translate(' + ( mouseX - opts.brushSize/2) + 'px,'+ ( mouseY - opts.brushSize/2) + 'px)';
frame = false;
}
document.addEventListener('mousemove',(e)=>{
mouseX = e.pageX;
mouseY = e.pageY;
frame = frame || requestAnimationFrame(updateBrush);
});
gui.add(opts, 'rotate');
/*////////////////////////////////////////*/
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1200 );
camera.position.z = 500;
camera.position.x = 100;
camera.rotation.y = -100;
let renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x000000, 1 );
document.body.appendChild( renderer.domElement );
window.addEventListener( 'resize', function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
});
/*////////////////////////////////////////*/
let orbit = new THREE.OrbitControls( camera, renderer.domElement );
orbit.enableZoom = false;
/*////////////////////////////////////////*/
let lights = [];
lights[ 0 ] = new THREE.PointLight( 0xffffff, 1, 0 );
lights[ 1 ] = new THREE.PointLight( 0xffffff, 1, 0 );
lights[ 0 ].position.set( 0, 200, 0 );
lights[ 1 ].position.set( 100, 200, 100 );
scene.add.apply( scene, lights );
/*////////////////////////////////////////*/
let plane;
let geometry;
let defaultVertices;
function loadTexture(image){
document.body.className = 'loading';
let loader = new THREE.TextureLoader();
loader.load(
image,//https://unsplash.it/512/512?image=1053',
function ( texture ) {
document.body.className = '';
geometry = new THREE.PlaneGeometry( texture.image.width, texture.image.height, opts.planeSize, opts.planeSize );
let material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide });
if ( plane ) { scene.remove(plane); }
plane = new THREE.Mesh( geometry, material );
scene.add( plane );
defaultVertices = plane.geometry.clone().vertices;
render();
});
}
loadTexture('http://brokensquare.com/Code/assets/face.png');
function render() {
requestAnimationFrame( render );
if ( plane ) {
plane.geometry.verticesNeedUpdate = true;
plane.geometry.normalsNeedUpdate = true;
plane.geometry.computeFaceNormals();
if ( opts.rotate ) {
plane.rotation.y -= 0.001;
}
}
renderer.render( scene, camera );
};
const PI2 = Math.PI * 2;
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
let particleMaterial = new THREE.SpriteCanvasMaterial( {
color: 0x000000,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
}
});
function distance(first,second){
let dx = first.x - second.x;
let dy = first.y - second.y;
let dz = 0;
if ( first.z && second.z ) { dz = first.z - second.z }
return Math.sqrt(dx * dx + dy * dy + dz * dz)
}
/*////////////////////////////////////////*/
let mousedown = false;
let lastMouse = mouse.clone();
document.addEventListener( 'mousedown', (event)=>{
mousedown = mouseIntersects().length > 0;
if ( mousedown ) {
event.preventDefault();
lastMouse.x = 0; lastMouse.y = 0;
setMouse(event);
adjustMesh();
}
});
document.addEventListener( 'mouseup', mouseEnd );
document.addEventListener( 'mouseleave', mouseEnd );
function mouseEnd(){
mousedown = false;
orbit.enabled = true;
lastMouse.x = 0; lastMouse.y = 0;
}
function setMouse(event){
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
}
function mouseIntersects(target){
raycaster.setFromCamera( mouse, camera );
return raycaster.intersectObjects( target || scene.children );
}
function adjustMesh(intersects){
intersects = intersects || mouseIntersects();
if ( intersects.length > 0 ) {
orbit.enabled = false;
let INTERSECTED = intersects[0];
let object = intersects[0].object;
let mousePoint = intersects[0].point;
var geometry = INTERSECTED.object.geometry;
var face = INTERSECTED.face;
var vertices = geometry.vertices;
var v1 = vertices[ face.a ];
var v2 = vertices[ face.b ];
var v3 = vertices[ face.c ];
// calculate the centroid
var position = new THREE.Vector3();
position.x = ( v1.x + v2.x + v3.x ) / 3;
position.y = ( v1.y + v2.y + v3.y ) / 3;
position.z = ( v1.z + v2.z + v3.z ) / 3;
plane.geometry.vertices.forEach((point,i)=>{
let dist = distance(point, position);
if ( dist < opts.brushSize ) {
//console.log(dist);
point.z += opts.power
/ ( (dist+1) / opts.brushSize )
* (opts.subtract ? -1 : 1 );
}
});
}
}
document.addEventListener( 'mousemove', onDocumentMouseMove );
function onDocumentMouseMove( event ) {
setMouse(event);
let intersects = mouseIntersects( scene.children );
let dist = distance( mouse, lastMouse );
if ( !mousedown || dist < 0.03 ) { return; }
lastMouse = mouse.clone();
adjustMesh(intersects);
event.preventDefault();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/renderers/CanvasRenderer.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.1/dat.gui.min.js"></script>
.brush {
pointer-events: none;
position: absolute;
top: 0;
left: 0;
z-index: 2;
mix-blend-mode: color-dodge;
}
.loading:before {
position: absolute;
top: 0;
left: 0;
padding: 1em;
content: 'Loading...';
color: #aaa;
font-family: monospace;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment