(Three.js) Custom Mesh UV Display Texture Properly - javascript

I have created a custom Geometry function for Three.js, based on the Plane Geometry.
Everything works fine, except that I don't know how to make the UV display correctly.
I use 4 triangles per Square, while the THREE.PlaneGeometry is only using 2 triangles per square.
The code for PlaneGeometry UV's looks like this:
var uva = new THREE.Vector2( ix / gridX, 1 - iz / gridZ );
var uvb = new THREE.Vector2( ix / gridX, 1 - ( iz + 1 ) / gridZ );
var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iz + 1 ) / gridZ );
var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iz / gridZ );
And my Code in the Source Code:
var uva = new THREE.Vector2( a );
var uvb = new THREE.Vector2( b );
var uvc = new THREE.Vector2( c );
var uvd = new THREE.Vector2( d );
var uve = new THREE.Vector2( e );
Obviously that is wrong. But I have tried to use the PlaneGeometry code, and I get odd distortions, and I am not sure how to calculate the proper positions.
This:
var uva = new THREE.Vector2( ix / gridX, 1 - iz / gridZ );
var uvb = new THREE.Vector2( ix / gridX, 1 - ( iz + 1 ) / gridZ );
var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iz + 1 ) / gridZ );
var uvd = new THREE.Vector2( (( ix ) / gridX) + gridX, 1 - iz / gridZ );
var uve = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iz / gridZ );
Gives me this: BAD UV
I got some sort of answer in the THREE.JS ChatRoom, but I did not understand it, and the person did not elaborate...
(Q) So.. are the Vector2 for UV's not supposed to be the same position as the verticies ?
(A) no an UV is a vector that maps into a texture if you
have a 512x512 texture, and an UV with [0.25, 0.75], it would map to
the pixel in the texture at 256, 768 each vertex has an uv
this means that this vertex maps into the texture like explained above
this is done for each face of a vertex, and all fragments in the
face are then interpolated using those three uvs
So that did not remove my confusion.. I did not understand the [0.25, 0.75] part. Or that every vertex has a texture. How can a point have a texture.
Could someone point me in the right direction please ?
All I need is to know how the UV is positioned. But an example would be great.
Here is the source if you would like to take a look:
THREE.DiamondGeometry = function ( width, height, widthSegments, heightSegments ) {
THREE.Geometry.call( this );
this.width = width;
this.height = height;
this.widthSegments = widthSegments || 1;
this.heightSegments = heightSegments || 2;
var long_row = this.widthSegments + 1;
var short_row = this.widthSegments;
// First Row is the long_row, the ternary statement will toggle this.
var current_row = short_row;
var gridY = 0;
var vX = width / 2, vY = height / 2;
var ix, iz;
var width_half = width / 2;
var height_half = height / 2;
var gridX = this.widthSegments;
var gridZ = this.heightSegments;
var gridX1 = gridX + 1;
var gridZ1 = gridZ + ( gridZ - 2) + 1;
var segment_width = this.width / gridX;
var segment_height = this.height / gridZ;
var normal = new THREE.Vector3( 0, 0, 1 );
// Height Segments Verticies
for ( iz = 0; iz < (gridZ1 + 1) * 2; iz ++ ) {
// Ternary Operator:
current_row === long_row ? (current_row = short_row, vX = width_half - (segment_width / 2) ) : (current_row = long_row, vX = width_half );
// Width Segment Verticies
for ( ix = 0; ix < current_row; ix ++ ) {
var x = ix * segment_width - vX ;
var y = (iz * segment_height - vY) / 2 - (vY / 2);
this.vertices.push( new THREE.Vector3( x, - y, 0 ) );
}
}
for ( iz = 0; iz < gridZ ; iz ++ ) {
for ( ix = 0; ix < gridX; ix ++ ) {
var a = ix + gridX * iz + (iz * gridX1) ;
var b = a + 1;
var c = a + gridX1;
var d = c + gridX;
var e = d + 1;
// THIS IS THE BAD PART THAT I NEED TO CALCULATE THE UV POSITIONS FOR:
var uva = new THREE.Vector2( a );
var uvb = new THREE.Vector2( b );
var uvc = new THREE.Vector2( c );
var uvd = new THREE.Vector2( d );
var uve = new THREE.Vector2( e );
// UP
var face = new THREE.Face3( c, b, a );
face.normal.copy( normal );
face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
this.faces.push( face );
this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvc ] );
// DOWN
face = new THREE.Face3( e, c, d );
face.normal.copy( normal );
face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
this.faces.push( face );
this.faceVertexUvs[ 0 ].push( [ uvd, uvc.clone(), uve ] );
// LEFT
face = new THREE.Face3( d, c, a );
face.normal.copy( normal );
face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
this.faces.push( face );
this.faceVertexUvs[ 0 ].push( [ uva.clone(), uvc.clone(), uvd.clone() ] );
// RIGHT
face = new THREE.Face3( e, b, c );
face.normal.copy( normal );
face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
this.faces.push( face );
this.faceVertexUvs[ 0 ].push( [ uvc.clone(), uvb.clone(), uve.clone() ] );
}
}
this.computeCentroids();
};
THREE.DiamondGeometry.prototype = Object.create( THREE.Geometry.prototype );

see this image:
http://cgkit.sourceforge.net/tutorials/_images/uvmap.png
As noted there, for 1 texture without texture repeat, UV coordinates are in the range from (0,0) to (1,1). If you have a quad-plane and assign the UVs to the vertices as in the image, the whole texture gets displayed on the quad. if you add edge loops/ tesselate your quad for example in 4 smaller quads but still want to have the texture compeltely displayed without repetition, you need to calculate values in between for where the vertices are in UV-space. for example the vertex in the middle (resulting from your tesselation) is now UV (0.5, 0.5)
and the one at the top in the middl is (0, 0.5). See how PlaneGeometry does it and try to learn from it^^

Related

Optimize hundreds of dynamic lines in Three.js

I'm new to Three.js, I'm just having fun with it. I'm trying to achieve a simple dynamic background fullscreen on a page, you get the example here:
function createHexagon( vertices, color ) {
var geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
var material = new THREE.LineBasicMaterial( { color: color, opacity: Math.min((Math.random() / 5), 0.1), transparent: true } );
var hexagon = new THREE.Line( geometry, material );
return hexagon;
}
function initMatrix() {
var color = defaultColor.getHex();
var vertices;
var x = ( width / -2 ) - 90;
var y = height / -2;
var deltaX = 120;
var deltaY = 60;
var time = 5.0;
while( y < height / 2 ) {
while( x < width / 2 ) {
vertices = new Float32Array([
0, 30, 0,
20, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
20, 0, 0,
60, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
60, 0, 0,
80, 30, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
x += deltaX;
}
x = ( width / -2 ) - 90;
y += deltaY;
}
x = ( width / -2 ) - 30;
y = ( height / -2 ) - 30;
deltaX = 120;
deltaY = 60;
while( y < height / 2 ) {
while( x < width / 2 ) {
vertices = new Float32Array([
0, 30, 0,
20, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
20, 0, 0,
60, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
60, 0, 0,
80, 30, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
x += deltaX;
}
x = ( width / -2 ) - 30;
y += deltaY;
}
}
Those are single bufferGeometry lines (as you can see in the above functions to create the background) that randomly rotate and change opacity on mouse hover with raycaster and TweenLite. It works pretty fine. What you can notice is that CPU usage goes to almost 100%.
I know that if I group lines into the same geometry it'll be better on performance, but then I'm not able to animate single lines with raycaster, especially the opacity.
I searched a lot of discussions and tried so many things. The best result is this way, rendering single lines separately. Can you suggest some tips about it?
(Posted on behalf of the question author).
I found a solution. I made a whole geometry and I indexed vertices and respective colors. The only problem is that I can't mange opacity this way, but it works fine and CPU is around 20%.
document.addEventListener( 'DOMContentLoaded', main );
var width = screen.width;
var height = screen.height;
var camera, scene, renderer, raycaster, mouse;
var rayColor = new THREE.Color( 0x0640C2 );
var rayColor2 = new THREE.Color( 0xCC311B );
var colors;
var linesPositions, originalPositions;
var linesMesh;
var geometry;
var intersects;
var stats;
function initMatrix() {
var AlinesPositions = [ ];
var x = ( width / - 2 ) - 80;
var y = ( height / - 2 ) - 60;
var deltaX = 120;
var deltaY = 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 ) - 80;
y += deltaY;
}
x = ( width / - 2 );
y = ( height / - 2 ) - 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 );
y += deltaY;
}
linesPositions = new Float32Array( AlinesPositions );
var Acolors = [ ];
for( var i = 0; i < AlinesPositions.length; i++ ) {
var fact = Math.random() * 20.0;
var ran = Math.min( Math.max( ( Math.random() / fact ), 0.01 ), 0.5 );
Acolors[i] = ran;
Acolors[i+1] = ran;
Acolors[i+2] = ran;
i += 2;
}
colors = new Float32Array( Acolors );
geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( linesPositions, 3 ).setDynamic( true ) );
geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).setDynamic( true ) );
var material = new THREE.LineBasicMaterial( {
vertexColors: THREE.VertexColors,
blending: THREE.AdditiveBlending,
transparent: true
} );
linesMesh = new THREE.LineSegments( geometry, material );
scene.add( linesMesh );
originalPositions = new THREE.BufferAttribute( linesPositions, 3 );
originalPositions.copy( linesMesh.geometry.attributes.position );
}
function init() {
scene = new THREE.Scene();
//camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 100 );
camera = new THREE.PerspectiveCamera( 120, width / height, 1, 1000 );
camera.position.set( 0, 0, 200 );
camera.lookAt( 0, 0, 0 );
// Create matrix
initMatrix();
raycaster = new THREE.Raycaster();
raycaster.linePrecision = 20;
mouse = new THREE.Vector2();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( width, height );
document.body.appendChild( renderer.domElement );
}
function main() {
init();
animate();
}
function animate() {
requestAnimationFrame( animate );
render();
}
var intersected = [];
function removeX( i ) { intersected.splice( i, 1 ); }
function alterate( index ) {
var randColor = ( Math.random() <= 0.49 ) ? rayColor : rayColor2;
// Change color
var currentC = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenC = TweenLite.to( currentC, 1.0, {
r: randColor.r,
g: randColor.g,
b: randColor.b,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentC.r, currentC.g, currentC.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentC ]
});
// Change coordinates
var currentXY = { x: originalPositions.getX( index ), y: originalPositions.getY( index ), z: originalPositions.getZ( index ) };
var goal = [ ( currentXY.x + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.y + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.z + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ) ];
var tweenXY = TweenLite.to( currentXY, 1.0, {
x: goal[ 0 ],
y: goal[ 1 ],
z: goal[ 2 ],
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXY.x, currentXY.y, currentXY.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXY ]
});
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObject( linesMesh );
if( intersects.length ) {
if( !intersected.includes( intersects[ 0 ].index ) ) {
for( var x = 0; x < intersects.length; x++ ) {
if( !intersected.includes( intersects[ x ].index ) ) {
var index = intersects[ x ].index;
// Save index
intersected.push( index );
alterate( index );
}
}
var present = 0;
for( var y = 0; y < intersected.length; y++ ) {
for( var j = 0; j < intersects.length; j++ ) {
if( intersects[ j ].index == intersected[ y ] ) { present = 1; break; }
}
if( !present ) {
( function( y ) {
var randC = Math.min( Math.max( ( Math.random() / ( Math.random() * 20.0 ) ), 0.01 ), 0.5 );
// Current item old coordinates
let index = intersected[ y ];
var indexX = originalPositions.getX( index );
var indexY = originalPositions.getY( index );
var indexZ = originalPositions.getZ( index );
removeX( y );
// Reset color
var currentCreset = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenCreset = TweenLite.to( currentCreset, 1.5, {
r: randC,
g: randC,
b: randC,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentCreset.r, currentCreset.g, currentCreset.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentCreset ],
onComplete: function() { }
} );
// Reset coordinates
var currentXYreset = { x: linesMesh.geometry.attributes.position.getX( index ), y: linesMesh.geometry.attributes.position.getY( index ), z: linesMesh.geometry.attributes.position.getZ( index ) };
var tweenXYreset = TweenLite.to( currentXYreset, 1.5, {
x: indexX,
y: indexY,
z: indexZ,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXYreset.x, currentXYreset.y, currentXYreset.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXYreset ]
});
}) ( y );
}
}
}
}
renderer.render( scene, camera );
}
function onMouseMove( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
var normX = event.clientX - ( window.innerWidth / 2 );
var normY = event.clientY - ( window.innerHeight / 2 );
camera.position.set( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 200 );
camera.lookAt( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 0 );
}
window.addEventListener( 'click', function() {
if( intersected.length ) {
for( var x = 0; x < intersected.length; x++ ) {
var index = intersected[ x ];
alterate( index );
}
}
});
window.addEventListener( 'mousemove', onMouseMove, false );
// Set new dimensions for scene when resize window
window.addEventListener( 'resize', function() {
var wWidth = window.innerWidth;
var wHeight = window.innerHeight;
/*camera.left = wWidth / -2;
camera.right = wWidth / 2;
camera.top = wHeight / 2;
camera.bottom = wHeight / -2;*/
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
raycaster.setFromCamera( mouse, camera );
renderer.setSize( window.innerWidth, window.innerHeight );
} );
Here is a Codepen link.
I found a solution. I made a whole geometry and I indexed vertices and respective colors. The only problem is that I can't mange opacity this way, but it works fine and CPU is around 20%.
document.addEventListener( 'DOMContentLoaded', main );
var width = screen.width;
var height = screen.height;
var camera, scene, renderer, raycaster, mouse;
var rayColor = new THREE.Color( 0x0640C2 );
var rayColor2 = new THREE.Color( 0xCC311B );
var colors;
var linesPositions, originalPositions;
var linesMesh;
var geometry;
var intersects;
var stats;
function initMatrix() {
var AlinesPositions = [ ];
var x = ( width / - 2 ) - 80;
var y = ( height / - 2 ) - 60;
var deltaX = 120;
var deltaY = 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 ) - 80;
y += deltaY;
}
x = ( width / - 2 );
y = ( height / - 2 ) - 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 );
y += deltaY;
}
linesPositions = new Float32Array( AlinesPositions );
var Acolors = [ ];
for( var i = 0; i < AlinesPositions.length; i++ ) {
var fact = Math.random() * 20.0;
var ran = Math.min( Math.max( ( Math.random() / fact ), 0.01 ), 0.5 );
Acolors[i] = ran;
Acolors[i+1] = ran;
Acolors[i+2] = ran;
i += 2;
}
colors = new Float32Array( Acolors );
geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( linesPositions, 3 ).setDynamic( true ) );
geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).setDynamic( true ) );
var material = new THREE.LineBasicMaterial( {
vertexColors: THREE.VertexColors,
blending: THREE.AdditiveBlending,
transparent: true
} );
linesMesh = new THREE.LineSegments( geometry, material );
scene.add( linesMesh );
originalPositions = new THREE.BufferAttribute( linesPositions, 3 );
originalPositions.copy( linesMesh.geometry.attributes.position );
}
function init() {
scene = new THREE.Scene();
//camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 100 );
camera = new THREE.PerspectiveCamera( 120, width / height, 1, 1000 );
camera.position.set( 0, 0, 200 );
camera.lookAt( 0, 0, 0 );
// Create matrix
initMatrix();
raycaster = new THREE.Raycaster();
raycaster.linePrecision = 20;
mouse = new THREE.Vector2();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( width, height );
document.body.appendChild( renderer.domElement );
}
function main() {
init();
animate();
}
function animate() {
requestAnimationFrame( animate );
render();
}
var intersected = [];
function removeX( i ) { intersected.splice( i, 1 ); }
function alterate( index ) {
var randColor = ( Math.random() <= 0.49 ) ? rayColor : rayColor2;
// Change color
var currentC = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenC = TweenLite.to( currentC, 1.0, {
r: randColor.r,
g: randColor.g,
b: randColor.b,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentC.r, currentC.g, currentC.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentC ]
});
// Change coordinates
var currentXY = { x: originalPositions.getX( index ), y: originalPositions.getY( index ), z: originalPositions.getZ( index ) };
var goal = [ ( currentXY.x + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.y + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.z + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ) ];
var tweenXY = TweenLite.to( currentXY, 1.0, {
x: goal[ 0 ],
y: goal[ 1 ],
z: goal[ 2 ],
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXY.x, currentXY.y, currentXY.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXY ]
});
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObject( linesMesh );
if( intersects.length ) {
if( !intersected.includes( intersects[ 0 ].index ) ) {
for( var x = 0; x < intersects.length; x++ ) {
if( !intersected.includes( intersects[ x ].index ) ) {
var index = intersects[ x ].index;
// Save index
intersected.push( index );
alterate( index );
}
}
var present = 0;
for( var y = 0; y < intersected.length; y++ ) {
for( var j = 0; j < intersects.length; j++ ) {
if( intersects[ j ].index == intersected[ y ] ) { present = 1; break; }
}
if( !present ) {
( function( y ) {
var randC = Math.min( Math.max( ( Math.random() / ( Math.random() * 20.0 ) ), 0.01 ), 0.5 );
// Current item old coordinates
let index = intersected[ y ];
var indexX = originalPositions.getX( index );
var indexY = originalPositions.getY( index );
var indexZ = originalPositions.getZ( index );
removeX( y );
// Reset color
var currentCreset = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenCreset = TweenLite.to( currentCreset, 1.5, {
r: randC,
g: randC,
b: randC,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentCreset.r, currentCreset.g, currentCreset.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentCreset ],
onComplete: function() { }
} );
// Reset coordinates
var currentXYreset = { x: linesMesh.geometry.attributes.position.getX( index ), y: linesMesh.geometry.attributes.position.getY( index ), z: linesMesh.geometry.attributes.position.getZ( index ) };
var tweenXYreset = TweenLite.to( currentXYreset, 1.5, {
x: indexX,
y: indexY,
z: indexZ,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXYreset.x, currentXYreset.y, currentXYreset.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXYreset ]
});
}) ( y );
}
}
}
}
renderer.render( scene, camera );
}
function onMouseMove( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
var normX = event.clientX - ( window.innerWidth / 2 );
var normY = event.clientY - ( window.innerHeight / 2 );
camera.position.set( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 200 );
camera.lookAt( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 0 );
}
window.addEventListener( 'click', function() {
if( intersected.length ) {
for( var x = 0; x < intersected.length; x++ ) {
var index = intersected[ x ];
alterate( index );
}
}
});
window.addEventListener( 'mousemove', onMouseMove, false );
// Set new dimensions for scene when resize window
window.addEventListener( 'resize', function() {
var wWidth = window.innerWidth;
var wHeight = window.innerHeight;
/*camera.left = wWidth / -2;
camera.right = wWidth / 2;
camera.top = wHeight / 2;
camera.bottom = wHeight / -2;*/
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
raycaster.setFromCamera( mouse, camera );
renderer.setSize( window.innerWidth, window.innerHeight );
} );
Here is a Codepen link: Example

threejs coincident vertices texture discontinued

I want to make a animated tube with hemisphere ends, the tubular segments is 200. I have the vertices from the first 60 segments to copy the position of a SphereGeometry's upper half part, and the last 60 segments lower half part.
The segments between the upper and lower hemisphere is all copied to the vertices around the sphere's equatorial.
The geometry looks fine, but the spherical environment mapping texture is discontinued at the sphere's equatorial.
I have my code as below, anyone know how to solve the problem?
var camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 28, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 50;
scene = new THREE.Scene();
var nMat = new THREE.MeshNormalMaterial({side:THREE.DoubleSide,});
var tube = new DSTube( 200, 30, 10, nMat);
scene.add( tube.mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r78/three.js"></script>
<script>
function DSTube( lengthSeg, radiusSeg, radius, material ) {
//for speed
this.framerate = 60;
//center
this.origin = new THREE.Vector3();
this.head = this.origin;
this.tail = this.origin;
//setup
this.lengthSeg = lengthSeg;
this.radiusSeg = radiusSeg;
this.radius = radius;
this.pathPoints = [];
for(var i=0; i<this.lengthSeg; i++) {
this.pathPoints.push( new THREE.Vector3( 0, 0, 0) );
}
// TubeGeometry(path, tubularSegments, radius, radiusSegments, closed)
this.geometry = new THREE.TubeGeometry( new THREE.CatmullRomCurve3(this.pathPoints), this.lengthSeg, this.radius, this.radiusSeg, false );
this.material = material;
this.mesh = new THREE.Mesh( this.geometry, this.material );
this.verticeCount = this.geometry.vertices.length;
//sphere part
//adjust height segment if needed
this.sphereHeightSegments = 60;
//SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength)
this.headSphere = new THREE.SphereGeometry( this.radius, this.radiusSeg, this.sphereHeightSegments );
this.tailSphere = new THREE.SphereGeometry( this.radius, this.radiusSeg, this.sphereHeightSegments );
this.sphereVerticeCount = this.headSphere.vertices.length;
//count for tube hemisphere
this.headHemisphereVerticeCount = ( this.sphereHeightSegments / 2 + 1 ) * this.radiusSeg;
//layer
this.tubeLayerCount = this.lengthSeg + 1;
this.headHemisphereLayerCount = this.tailHemisphereLayerCount = this.sphereHeightSegments / 2 + 1;
this.tailHemisphereStartLayer = this.tubeLayerCount - this.sphereHeightSegments / 2;
this.middleLayerCount = this.tubeLayerCount - this.headHemisphereLayerCount * 2;
this.initGeometry();
// return this.mesh;
}
DSTube.prototype.initGeometry = function() {
this.copyHeadSphere();
this.copyInitialTubePart();
this.copyTailSphere();
}
DSTube.prototype.copyHeadSphere = function() {
//copy head hemisphere vertice
for( var y = 0; y < this.headHemisphereLayerCount; y++ ) {
for( var x = 0; x < this.radiusSeg; x++ ) {
if( x == 0 ) {
var sphereVertex = this.headSphere.vertices[ y * (this.radiusSeg + 1) + x ];
this.geometry.vertices[ y * this.radiusSeg + x ].copy( sphereVertex );
} else {
var sphereVertex = this.headSphere.vertices[ y * (this.radiusSeg + 1) + ( this.radiusSeg - x ) ];
this.geometry.vertices[ y * this.radiusSeg + x ].copy( sphereVertex );
}
}
}
this.geometry.computeBoundingSphere();
this.geometry.computeFaceNormals();
this.geometry.computeVertexNormals();
}
DSTube.prototype.copyInitialTubePart = function() {
//copy head hemisphere vertice
for( var y = this.headHemisphereLayerCount - 1; y < this.tubeLayerCount ; y++ ) {
for( var x = 0; x < this.radiusSeg; x++ ) {
var vertex = this.geometry.vertices[ ( this.headHemisphereLayerCount - 1 ) * this.radiusSeg + x ];
this.geometry.vertices[ y * this.radiusSeg + x ].copy( vertex );
}
}
this.geometry.computeBoundingSphere();
this.geometry.computeFaceNormals();
this.geometry.computeVertexNormals();
}
DSTube.prototype.copyTailSphere = function() {
//copy tail hemisphere vertice
for( var y = this.tailHemisphereStartLayer; y < this.tubeLayerCount; y++ ) {
for( var x = 0; x < this.radiusSeg; x++ ) {
if( x == 0 ) {
var sphereVertex = this.tailSphere.vertices[ ( y - this.middleLayerCount - 1 ) * (this.radiusSeg + 1) + x ];
this.geometry.vertices[ y * this.radiusSeg + x ].copy( sphereVertex );
} else {
var sphereVertex = this.tailSphere.vertices[ ( y - this.middleLayerCount - 1 ) * (this.radiusSeg + 1) + ( this.radiusSeg - x ) ];
this.geometry.vertices[ y * this.radiusSeg + x ].copy( sphereVertex );
}
}
}
this.geometry.computeBoundingSphere();
this.geometry.computeFaceNormals();
this.geometry.computeVertexNormals();
}
</script>

Creating three tables with header

I'm trying to cycle through a table and create three tables of clickable divs with a header for each (ideally with a separate class to make them look different). I'm trying to use a while loop with three if statements but the headers are not displaying properly. All three still show up as normal divs instead of with different classes but the first one is covered with a second copy of the third column header with the correct header and the second header is covered with a second copy of the first header, again with the correct class.
JS Snippet
var table = [
"Column Title", "#", 2,2,
"Column Item", "website.com", 1, 3,
"Column Item 2", "website2.com", 2, 3,
// Etc.
];
var camera, scene, renderer;
var controls;
var objects = [];
var targets = { table: [], sphere: [], helix: [], grid: [] };
init();
animate();
function init() {
for ( var i = 0; i < table.length; i += 4 ) {
if(i === 0){
var courtsTitle = document.createElement('div');
courtsTitle.className = 'listTitle';
courtsTitle.innerHTML = "<p>Column Title</p>";
var object = new THREE.CSS3DObject( courtsTitle );
object.position.x = Math.random() * 4000 - 2000;
object.position.y = Math.random() * 4000 - 2000;
object.position.z = Math.random() * 4000 - 2000;
scene.add( object );
objects.push( object );
var object = new THREE.Object3D();
object.position.x = ( 6 * 480 ) - 2900;
object.position.y = - ( 2 * 340 ) + 1550;
targets.table.push( object );
}
if(i === 14){
var departmentsTitle = document.createElement('div');
departmentsTitle.className = 'listTitle';
departmentsTitle.innerHTML = "<p>Column Title2</p>";
var object = new THREE.CSS3DObject( departmentsTitle );
object.position.x = Math.random() * 4000 - 2000;
object.position.y = Math.random() * 4000 - 2000;
object.position.z = Math.random() * 4000 - 2000;
scene.add( object );
objects.push( object );
var object = new THREE.Object3D();
object.position.x = ( 10 * 480 ) - 2900;
object.position.y = - ( 2 * 340 ) + 1550;
targets.table.push( object );
}
if(i === 28){
var servicesTitle = document.createElement('div');
servicesTitle.className = 'listTitle';
servicesTitle.innerHTML = "<p>Column Title3</p>";
var object = new THREE.CSS3DObject( servicesTitle );
object.position.x = Math.random() * 4000 - 2000;
object.position.y = Math.random() * 4000 - 2000;
object.position.z = Math.random() * 4000 - 2000;
scene.add( object );
objects.push( object );
//
var object = new THREE.Object3D();
object.position.x = ( 2 * 480 ) - 2900;
object.position.y = - ( 2 * 340 ) + 1550;
targets.table.push( object );
} else {
var department = document.createElement('a');
department.className = 'department';
department.style.backgroundColor = 'rgba(0,127,127,' + ( Math.random() * 0.5 + 0.25 ) + ')';
department.setAttribute('href', table[i + 1]);
department.setAttribute('target', "_blank");
var link = document.createElement('div');
link.className = 'link';
link.innerHTML = table[i];
department.appendChild(link);
/*Random Starting point*/
var object = new THREE.CSS3DObject( department );
object.position.x = Math.random() * 4000 - 2000;
object.position.y = Math.random() * 4000 - 2000;
object.position.z = Math.random() * 4000 - 2000;
scene.add( object );
objects.push( object );
/*Final position*/
var object = new THREE.Object3D();
object.position.x = ( table[ i + 2 ] * 480 ) - 2900;
object.position.y = - ( table[ i + 3 ] * 340 ) + 1550;
targets.table.push( object );
}
}

Altering color of individual sprite in three.js

I just started playing around with three.js and I'm having some trouble modifying the color of individual sprites within an array.
I'm working with the example found here, from threejs.org.
I'm attempting to modify each sprite's color based on its scale value. In particular, I added the following line to the last function, from the source file.
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
camera.lookAt( scene.position );
var i = 0;
for ( var ix = 0; ix < AMOUNTX; ix ++ ) {
for ( var iy = 0; iy < AMOUNTY; iy ++ ) {
particle = particles[ i++ ];
particle.position.y = ( Math.sin( ( ix + count ) * 0.3 ) * 50 ) +
( Math.sin( ( iy + count ) * 0.5 ) * 50 );
particle.scale.x = particle.scale.y = ( Math.sin( ( ix + count ) * 0.3 ) + 1 ) * 4 +
( Math.sin( ( iy + count ) * 0.5 ) + 1 ) * 4;
// Added this line in an attempt to change color based on scale //
particle.material.color.setHSL(particle.scale.x * .1, .2, .2);
}
}
renderer.render( scene, camera );
count += 0.1;
}
However, the added line changes every particle to the same color values. I'd assumed that each element of the array would be accessed and modified within the loop, but it doesn't seem to be the case.
The material is being shared with all the particles,
var material = new THREE.SpriteCanvasMaterial( {
color: 0xffffff,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
}
} );
Move this above the loop where the sprites particles are created so they get a separate material each.
The answer about each sprite having its own material sent me in the right direction. I set material to an array and added an association between each material and sprite element.
var i = 0;
var j = 0;
for ( var ix = 0; ix < AMOUNTX; ix ++ ) {
for ( var iy = 0; iy < AMOUNTY; iy ++ ) {
material[ j ] = new THREE.SpriteCanvasMaterial( {
vertexColors: THREE.VertexColors,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
}
} );
particle = particles[ i ++ ] = new THREE.Sprite( material[ j ++ ] );
particle.position.x = ix * SEPARATION - ( ( AMOUNTX * SEPARATION ) / 2 );
particle.position.z = iy * SEPARATION - ( ( AMOUNTY * SEPARATION ) / 2 );
scene.add( particle );
}
}

Morphing Geometry - Three.Js

So I have this code that will generate the user's desired dimension and display it on a customized new THREE.Geometry() . This may be an off topic to most of you. But hey, I'm just new to Three.js
My problem is that:
I can't find a way to insert the geometry.morphTargets. Or simply, I don't know how to use it properly
So here's my code:
//custom Object height and width
customHeightWidth(customWidth, customHeight);
function customHeightWidth(width, height){
material = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture(uploadedFile),
side: THREE.DoubleSide,
overdraw: true,
wireframe: false
});
//objects
combined = new THREE.PlaneGeometry(width, height, 30, 10);
geometry = new THREE.Geometry();
geometry.name: "target1", vertices.push( new THREE.Vector3( -(width), height, 0 ) );
geometry.name: "target2", vertices.push( new THREE.Vector3( -(width), -(height), 0 ) );
geometry.name: "target3", vertices.push( new THREE.Vector3( width, -(height), 0 ) );
geometry.computeBoundingSphere();
geometry.faces.push( new THREE.Face3( 0, 1, 2 ) );
geometry1 = new THREE.Geometry();
geometry1.vertices.push( new THREE.Vector3( width, height, 0 ) );
geometry1.vertices.push( new THREE.Vector3( -(width), height, 0 ) );
geometry1.vertices.push( new THREE.Vector3( width, -(height), 0 ) );
geometry1.computeBoundingSphere();
geometry1.faces.push( new THREE.Face3( 0, 1, 2 ) );
// 1st box
var mesh1 = new THREE.Mesh(geometry);
var mesh2 = new THREE.Mesh(geometry1);
//activating the meshs
THREE.GeometryUtils.merge(combined, mesh1);
THREE.GeometryUtils.merge(combined, mesh2);
mesh = new THREE.Mesh(combined, material);
this.scene.add(mesh);
};
}
var animate = function() {
requestAnimationFrame(animate);
//mesh.rotation.x += 0.01;
//mesh.rotation.y -= 0.006;
renderer.render(scene, camera);
}
init();
animate();
I recommend reviewing how JSONLoader loads morph targets to the Geometry.morphTargets array in the Geometry class.
function parseMorphing( scale ) {
if ( json.morphTargets !== undefined ) {
var i, l, v, vl, dstVertices, srcVertices;
for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) {
geometry.morphTargets[ i ] = {};
geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;
geometry.morphTargets[ i ].vertices = [];
dstVertices = geometry.morphTargets[ i ].vertices;
srcVertices = json.morphTargets [ i ].vertices;
for( v = 0, vl = srcVertices.length; v < vl; v += 3 ) {
var vertex = new THREE.Vector3();
vertex.x = srcVertices[ v ] * scale;
vertex.y = srcVertices[ v + 1 ] * scale;
vertex.z = srcVertices[ v + 2 ] * scale;
dstVertices.push( vertex );
}
}
}
//Reading morphTargets
for(i=o;i<geometry.morphTargets.length;i++) {
morphTargets = geometry.morphTargets[i].vertices;
return morphTargets;
}
//loading morphTargets
loadMorphTargets = function (morphTargets) {
if (morphTargets !== undefined) {
var i, l, v, vl, dstVertices, srcVertices;
//for (i = 0, l = morphTargets.length; i < l; i++) {
geometry.morphTargets[i] = {};
geometry.morphTargets[i].name = morphTargets.name;
geometry.morphTargets[i].vertices = [];
var dstVertices = geometry.morphTargets[i].vertices;
var srcVertices = morphTargets.vertices;
for (v = 0, vl = srcVertices.length; v < vl; v += 3) {
var vertex = new THREE.Vector3();
vertex.x = srcVertices[ v ];
vertex.y = srcVertices[ v + 1 ];
vertex.z = srcVertices[ v + 2 ];
dstVertices.push(vertex);
}
}
};
I guess it will be useful to you.

Categories