apply two textures on a plane in Three.js - three.js

Is it possible to add two textures at the same time on a plane? Or a cube?
I would like to apply one texture at the top half of a plane and a different one at the bottom half.
Something like this:
this is what I set so far
var geometry = new THREE.PlaneGeometry( 2, 2 );
var loader = new THREE.TextureLoader();
var texture = loader.load( 'textures/stone.png');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 4, 4 );
var material = new THREE.MeshBasicMaterial( { map: texture } );
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

You'll want to segment your geometry, and use multi-materials.
This means you'll use the widthSegments and heightSegments parameters of PlaneGeometry to define a plane with more than just two faces. This is necessary because each face can only have one material (unless you're doing some seriously fancy blending).
Once you have a segmented plane, you can assign the materialIndex of each face to point to a separate material.
You'll also define your mesh using an array of materials, rather than just one--each face's materialIndex references an index in the materials array.
The last thing to do, which I'll leave up to you, is to adjust the face UVs and texture repeating/wrapping to achieve the effect you're seeking.
If you have further questions on this topic, leave a comment, and I'll try to address them.
// https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.js
// https://threejs.org/examples/js/controls/TrackballControls.js
/**********************/
/* Initialization */
/**********************/
var renderer = new THREE.WebGLRenderer( {
canvas: document.getElementById( "view" ),
antialias: true,
alpha: true
} );
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 28, 1, 1, 1000 );
camera.position.z = 50;
camera.add( new THREE.PointLight( 0xffffff, 1, Infinity ) );
scene.add( camera );
var controls = new THREE.TrackballControls( camera, renderer.domElement );
controls.addEventListener( "change", render );
/**********************/
/* Populate the Scene */
/**********************/
var texLoader = new THREE.TextureLoader();
var tex1 = texLoader.load( "https://upload.wikimedia.org/wikipedia/commons/2/2b/WallRGB.png", render ); // bricks
var tex2 = texLoader.load( "https://upload.wikimedia.org/wikipedia/commons/6/68/NGC772_-_SDSS_DR14.jpg", render ); // space
var mat1 = new THREE.MeshLambertMaterial( { map: tex1 } );
var mat2 = new THREE.MeshLambertMaterial( { map: tex2 } );
// First, segment your geometry however you need it.
// This one is segmented into 1 horizontal segment with 2 vertical segments.
var planeGeo = new THREE.PlaneGeometry( 10, 10, 1, 2 );
// Next you need to set up your faces to use specific materials.
planeGeo.faces[ 0 ].materialIndex = 0;
planeGeo.faces[ 1 ].materialIndex = 0;
planeGeo.faces[ 2 ].materialIndex = 1;
planeGeo.faces[ 3 ].materialIndex = 1;
// You can create a mesh using an array of materials, referenced by materialIndex.
var mesh = new THREE.Mesh( planeGeo, [ mat1, mat2 ] );
scene.add( mesh );
/**********************/
/* Render Function */
/**********************/
function render () {
renderer.render( scene, camera );
}
render();
/**********************/
/* Animation Loop */
/**********************/
function animate () {
requestAnimationFrame( animate );
controls.update();
}
animate();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1,IE=edge">
<title>TEST</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
</head>
<body>
<canvas id="view" width="500" height="500" style="border: 1px black solid;"></canvas>
<script src="test.js"></script>
</body>
</html>

Complementing #TheJim01's answer, the code below will, adjust the UV coordinates on the floor geometry so that they will tile:
var tileSize = 24;
var textureSize = 256;
var floorScale = tileSize/textureSize;
floorGeometry.faceVertexUvs.forEach(layer => layer.forEach((face, i) => {
const base = layer[i % 2];
if (i % 2) {
face[0].x = 0;
face[0].y = floorScale;
face[1].x = floorScale;
face[1].y = floorScale;
face[2].x = floorScale;
face[2].y = 0;
} else {
face[0].x = 0;
face[0].y = 0;
face[1].x = 0;
face[1].y = floorScale;
face[2].x = floorScale;
face[2].y = 0;
}
}));

Related

In three.js how to position image texture similar to 'contain' in css?

My image texture is positioned relative to the center of 3d space instead of mesh and I don't quite understand what determines its size.
Here is example showing how the same image is positioned on different meshes:
https://imgur.com/glHE97L
I'd like the image be in the center of the mesh and it's size set similar as 'contain' in css.
The mesh is flat plane created using ShapeBufferGeometry:
const shape = new THREE.Shape( edgePoints );
const geometry = new THREE.ShapeBufferGeometry( shape );
To see any image I have to set:
texture.repeat.set(0.001, 0.001);
Not sure if that matters but after creating the mesh I than set its position and rotation:
mesh.position.copy( position[0] );
mesh.rotation.set( rotation[0], rotation[1], rotation[2] );
I've tried setting those:
mesh.updateMatrixWorld( true );
mesh.geometry.computeBoundingSphere();
mesh.geometry.verticesNeedUpdate = true;
mesh.geometry.elementsNeedUpdate = true;
mesh.geometry.morphTargetsNeedUpdate = true;
mesh.geometry.uvsNeedUpdate = true;
mesh.geometry.normalsNeedUpdate = true;
mesh.geometry.colorsNeedUpdate = true;
mesh.geometry.tangentsNeedUpdate = true;
texture.needsUpdate = true;
I've played with wrapS / wrapT and offset.
I've checked UV's - I don't yet fully understand this concept but it seems fine. Example of UV for one mesh (I understand those are XY coordinates and they seem to reflect the actual corners of my mesh):
uv: Float32BufferAttribute
array: Float32Array(8)
0: -208
1: 188
2: 338
3: 188
4: 338
5: 12
6: -208
7: 12
I've tried setting:
texture.repeat.set(imgHeight/geometryHeight/1000, imgWidth/geometryWidth/1000);
This is how THREE.ShapeGeometry() computes UV coordinate:
https://github.com/mrdoob/three.js/blob/e622cc7890e86663011d12ec405847baa4068515/src/geometries/ShapeGeometry.js#L157
But you can re-compute them, to put in range [0..1].
Here is an example, click the button to re-compute uvs of the shape geometry:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var grid = new THREE.GridHelper(10, 10);
grid.rotation.x = Math.PI * 0.5;
scene.add(grid);
var points = [
new THREE.Vector2(0, 5),
new THREE.Vector2(-5, 4),
new THREE.Vector2(-3, -3),
new THREE.Vector2(2, -5),
new THREE.Vector2(5, 0)
];
var shape = new THREE.Shape(points);
var shapeGeom = new THREE.ShapeBufferGeometry(shape);
var shapeMat = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/uv_grid_opengl.jpg")
});
var mesh = new THREE.Mesh(shapeGeom, shapeMat);
scene.add(mesh);
btnRecalc.addEventListener("click", onClick);
var box3 = new THREE.Box3();
var size = new THREE.Vector3();
var v3 = new THREE.Vector3(); // for re-use
function onClick(event) {
box3.setFromObject(mesh); // get AABB of the shape mesh
box3.getSize(size); // get size of that box
var pos = shapeGeom.attributes.position;
var uv = shapeGeom.attributes.uv;
for (let i = 0; i < pos.count; i++) {
v3.fromBufferAttribute(pos, i);
v3.subVectors(v3, box3.min).divide(size); // cast world uvs to range 0..1
uv.setXY(i, v3.x, v3.y);
}
uv.needsUpdate = true; // set it to true to make changes visible
}
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<button id="btnRecalc" style="position: absolute;">Re-calculate UVs</button>

Basic Three.js template scene, can´t animate

I'm really new in Three.js and javascript in general.
My question, I'm been trying to create some king of basic frankenstein template ( mainly based on Lee Stemkoski's examples) to use Three.js but as right know i can't make the cube spin infinitely, I have been watching tutorials and other examples but i can't make it work, any ideas why or how to solve it?
And
Any suggestions on how to improve this template scene?
Thanks in advance
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js Template</title>
<meta charset=utf-8>
<link rel="stylesheet" type="text/css" href="css/styles.css">
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,300,700' rel='stylesheet' type='text/css'>
<script src="js/three.js"></script>
<script src="js/Detector.js"></script>
<script src="js/Stats.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/OBJLoader.js"></script>
<script src="js/MTLLoader.js"></script>
<script src="js/DDSLoader.js"></script>
<script src="js/THREEx.KeyboardState.js"></script>
<script src="js/THREEx.FullScreen.js"></script>
<script src="js/THREEx.WindowResize.js"></script>
</head>
<body>
<div id="info">
three.js Template Scene<br />
from Base scene
</div>
<div id="threeJSScene"></div>
<script>
// MAIN //
// standard global variables
var container, scene, camera, renderer, controls, stats, animate;
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();
// initialization
init();
// animation loop / game loop
animate();
// FUNCTIONS //
function init()
{
// SCENE //
scene = new THREE.Scene();
//Add fog to the scene
// scene.fog = new THREE.FogExp2( 0xcccccc, 0.001 );
// CAMERA //
// set the view size in pixels (custom or according to window size)
// var SCREEN_WIDTH = 400, SCREEN_HEIGHT = 300;
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight;
// camera attributes
var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
// set up camera
camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
// add the camera to the scene
scene.add(camera);
// the camera defaults to position (0,0,0)
// so pull it back (z = 400) and up (y = 100) and set the angle towards the scene origin
camera.position.set(0,150,400);
camera.lookAt(scene.position);
// RENDERER //
// create and start the renderer; choose antialias setting.
if ( Detector.webgl )
renderer = new THREE.WebGLRenderer( {alpha:true, antialias:true} );
else
renderer = new THREE.CanvasRenderer();
// Configure renderer size
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
//Change BG Color
//renderer.setClearColor( 0xAA20AA );
//Configure pixel aspect ratio
renderer.setPixelRatio( window.devicePixelRatio );
//Enable shadows
renderer.shadowMapEnabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// Modify gamma
// renderer.gammaInput = true;
// renderer.gammaOutput = true;
//Attach div element to variable to contain the renderer
container = document.getElementById( 'threeJSScene' );
// alternatively: to create the div at runtime, use:
// container = document.createElement( 'div' );
// document.body.appendChild( container );
// attach renderer to the *container* div
container.appendChild( renderer.domElement );
// EVENTS //
// automatically resize renderer
THREEx.WindowResize(renderer, camera);
// toggle full-screen on given key press
THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) });
// CONTROLS //
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // remove when using animation loop
// enable animation loop when using damping or autorotation
//controls.enableDamping = true;
//controls.dampingFactor = 0.25;
controls.enableZoom = true;
//controls.update(); ----------> // required if controls.enableDamping = true, or if controls.autoRotate = true
// STATS //
// displays current and past frames per second attained by scene
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
// LIGHT //
// Add ambient light to Scene - Color(Blue) - Intensity
var Ambientlight = new THREE.AmbientLight (0x506699, 1);
scene.add(Ambientlight);
// Add light to Scene - Color(Red) - Intensity - Distance - decay
var light1 = new THREE.PointLight (0xff0000, 2, 400, 2);
light1.position.set(-60,150,-30);
light1.castShadow = true;
light1.shadowCameraVisible = true;
light1.shadow.mapSize.width = 1024 * 2;
light1.shadow.mapSize.height = 1024 * 2;
light1.shadowDarkness = 0.95;
light1.shadow.camera.near = 20;
light1.shadow.camera.far = 10000;
scene.add(light1);
// spotlight #1 -- yellow, dark shadow
var spotlight = new THREE.SpotLight(0xffff00);
spotlight.position.set(-60,150,-30);
spotlight.shadowCameraVisible = true;
spotlight.shadowDarkness = 0.95;
spotlight.intensity = 2;
// must enable shadow casting ability for the light
spotlight.castShadow = true;
scene.add(spotlight);
// GEOMETRY //
// Create a Cube Mesh //
var geometry = new THREE.BoxGeometry( 50, 50, 50 );
// Create a basic material
var material = new THREE.MeshStandardMaterial( {
color: "#ffffff",
side: THREE.DoubleSide,
//transparent: true,
//opacity: 0.5,
//wireframe: true,
//wireframeLinewidth: 5,
map: new THREE.TextureLoader().load('img/pattern.jpg'),
normalMap: new THREE.TextureLoader().load('img/pattern_NRM.png')
});
//Join the two attribute ( Geometry and material )
var mesh = new THREE.Mesh( geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.position.set(0, 50, 0); // Chance object position
//Add geometry to the scene
scene.add (mesh);
// Create a TorusKnot //
var TorusknotGeometry = new THREE.TorusKnotGeometry( 15, 5, 60, 25 );
var Torusknot = new THREE.Mesh( TorusknotGeometry, material); // We are using the same material created for the cube
Torusknot.castShadow = true;
Torusknot.receiveShadow = true;
Torusknot.position.set (0,100,0);
scene.add (Torusknot);
// Create a cube for the ground //
var groundGeometry = new THREE.BoxGeometry(200,200,10);
var ground = new THREE.Mesh( groundGeometry, material);
ground.castShadow = true;
ground.receiveShadow = true;
ground.position.set (0,0,0);
ground.rotation.x = 1.57;
scene.add (ground);
// Load in the mesh and add it to the scene.
var loader = new THREE.JSONLoader();
loader.load( "models/treehouse_logo.js", function(log){
var materiallogo = new THREE.MeshLambertMaterial({color: 0x55B663});
logo = new THREE.Mesh(log, materiallogo);
logo.scale.set (50,50,50);
logo.position.y = -1;
logo.castShadow = true;
logo.receiveShadow = true;
scene.add(logo);
});
// FLOOR //
// note: 4x4 checkboard pattern scaled so that each square is 25 by 25 pixels.
var floorTexture = new THREE.ImageUtils.loadTexture( 'img/checkerboard.jpg' );
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set( 10, 10 );
// DoubleSide: render texture on both sides of mesh
var floorMaterial = new THREE.MeshBasicMaterial( { map: floorTexture, side: THREE.DoubleSide } );
var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 1, 1);
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.castShadow = true;
floor.receiveShadow = true;
floor.position.y = -0.5;
floor.rotation.x = Math.PI / 2;
scene.add(floor);
// create a set of coordinate axes to help orient user
// specify length in pixels in each direction
var axes = new THREE.AxisHelper(100);
scene.add( axes );
// SKY //
// recommend either a skybox or fog effect (can't use both at the same time)
// without one of these, the scene's background color is determined by webpage background
// make sure the camera's "far" value is large enough so that it will render the skyBox!
var skyBoxGeometry = new THREE.CubeGeometry( 10000, 10000, 10000 );
// BackSide: render faces from inside of the cube, instead of from outside (default).
var skyBoxMaterial = new THREE.MeshBasicMaterial( { color: 0x9999ff, side: THREE.BackSide } );
var skyBox = new THREE.Mesh( skyBoxGeometry, skyBoxMaterial );
// scene.add(skyBox);
}
function update()
{
controls.update();
stats.update();
}
//Animate function
function animate()
{
requestAnimationFrame( animate );
render();
update();
}
// Render the scene - Always goes at the end
function render()
{
renderer.render( scene, camera );
}
</script>
</body>
</html>
To rotate your cube you'll need to add some value to the cube's rotation every frame. The reason this didn't work when you did it before is that the cube is defined in your init function and the render function doesn't have a reference to it.
So your fix requires two things:
Define your cube in a scope that both methods can "see"
Add some value to the rotation of your cube every frame
Inside of the init function you're defining your cube as mesh, so rename this to cube and remove var:
//Join the two attribute ( Geometry and material )
//var mesh = new THREE.Mesh( geometry, material); // Old
cube = new THREE.Mesh( geometry, material); // new
Removing var causes cube to become a global variable defined on the dom window rather than the init function. So your render function can "see" cube. So now all you have to do is rotate it!
function render()
{
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render( scene, camera );
}
I hope that helps!
and if you'd like to learn more about scope give this link a good read, it's helped me quite a bit.
https://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/
I ctrl + f'd the code, and you don't reference the rotation of the cube once. That would explain why it's not rotating. You can't will it to rotate, you need to actually write something that will change the values of the elements in your scene.
In order to continuously change the rotation of something, you need to reference or increment its rotation in the part of the code that loops.

Three.js - Create new mesh from certain faces/vertices of another mesh

I´ve been several days struggling with a particular Three.js issue, and I cannot find any way to do it. This is my case:
1) I have a floating mesh, formed by several triangled faces. This mesh is created from the geometry returned by a loader, after obtaining its vertices and faces using getAttribute('position'): How to smooth mesh triangles in STL loaded BufferGeometry
2) What I want to do now is to "project" the bottom face agains the floor.
3) Later, with this new face added, create the resulting mesh of filling the space between the 3 vertices of both faces.
I already have troubles in step 2... To create a new face I´m supossed to have its 3 vertices already added to geometry.vertices. I did it, cloning the original face vertices. I use geometry.vertices.push() results to know their new indexes, and later I use that indexes (-1) to finally create the new face. But its shape is weird, also the positions and the size. I think I´m not getting the world/scene/vector position equivalence theory right :P
I tried applying this, with no luck:
How to get the absolute position of a vertex in three.js?
Converting World coordinates to Screen coordinates in Three.js using Projection
http://barkofthebyte.azurewebsites.net/post/2014/05/05/three-js-projecting-mouse-clicks-to-a-3d-scene-how-to-do-it-and-how-it-works
I discovered that if I directly clone the full original face and simply add it to the mesh, the face is added but in the same position, so I cannot then change its vertices to place it on the floor (or at least without modifying the original face vertices!). I mean, I can change their x, y, z properties, but they are in a very small measure that doesn´t match the original mesh dimensions.
Could someone help me get this concept right?
EDIT: source code
// Create geometry
var geo = new THREE.Geometry();
var geofaces = [];
var geovertices = [];
original_geometry.updateMatrixWorld();
for(var index in original_geometry.faces){
// Get original face vertexNormals to know its 3 vertices
var face = original_geometry[index];
var vertexNormals = face.vertexNormals;
// Create 3 new vertices, add it to the array and then create a new face using the vertices indexes
var vertexIndexes = [null, null, null];
for (var i = 0, l = vertexNormals.length; i < l; i++) {
var vectorClone = vertexNormals[i].clone();
vectorClone.applyMatrix4( original_geometry.matrixWorld );
//vectorClone.unproject(camera); // JUST TESTING
//vectorClone.normalize(); // JUST TESTING
var vector = new THREE.Vector3(vectorClone.x, vectorClone.z, vectorClone.y)
//vector.normalize(); // JUST TESTING
//vector.project(camera); // JUST TESTING
//vector.unproject(camera); // JUST TESTING
vertexIndexes[i] = geovertices.push( vector ) - 1;
}
var newFace = new THREE.Face3( vertexIndexes[0], vertexIndexes[1], vertexIndexes[2] );
geofaces.push(newFace);
}
// Assign filled arrays to the geometry
geo.faces = geofaces;
geo.vertices = geovertices;
geo.mergeVertices();
geo.computeVertexNormals();
geo.computeFaceNormals();
// Create a new mesh with resulting geometry and add it to scene (in this case, to the original mesh to keep the positions)
new_mesh = new THREE.Mesh( geo, new THREE.MeshFaceMaterial(material) ); // material is defined elsewhere
new_mesh.position.set(0, -100, 0);
original_mesh.add( new_mesh );
I created a fully operational JSFiddle with the case to try things and see the problem more clear. With this STL (smaller than my local example) I cannot even see the badly cloned faces added to the scene.. Maybe they are too small or out of focus.
Take a look to the calculateProjectedMesh() function, here is where I tried to clone and place the bottom faces (already detected because they have a different materialIndex):
JSFiddle: https://jsfiddle.net/tc39sgo1/
var container;
var stlPath = 'https://dl.dropboxusercontent.com/s/p1xp4lhy4wxmf19/Handle_Tab_floating.STL';
var camera, controls, scene, renderer, model;
var mouseX = 0,
mouseY = 0;
var test = true;
var meshPlane = null, meshStl = null, meshCube = null, meshHang = null;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
/*THREE.FrontSide = 0;
THREE.BackSide = 1;
THREE.DoubleSide = 2;*/
var materials = [];
materials.push( new THREE.MeshPhongMaterial({color : 0x00FF00, side:0, shading: THREE.FlatShading, transparent: true, opacity: 0.9, overdraw : true, wireframe: false}) );
materials.push( new THREE.MeshPhongMaterial({color : 0xFF0000, transparent: true, opacity: 0.8, side:0, shading: THREE.FlatShading, overdraw : true, metal: false, wireframe: false}) );
materials.push( new THREE.MeshPhongMaterial({color : 0x0000FF, side:2, shading: THREE.FlatShading, overdraw : true, metal: false, wireframe: false}) );
var lineMaterial = new THREE.LineBasicMaterial({ color: 0x0000ff, transparent: true, opacity: 0.05 });
init();
animate();
function webglAvailable() {
try {
var canvas = document.createElement('canvas');
return !!(window.WebGLRenderingContext && (
canvas.getContext('webgl') || canvas.getContext('experimental-webgl')));
} catch (e) {
return false;
}
}
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100000000);
camera.position.x = 1500;
camera.position.z = -2000;
camera.position.y = 1000;
controls = new THREE.OrbitControls(camera);
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight(0x101030); //0x101030
scene.add(ambient);
var directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.position.set(0, 3, 0).normalize();
scene.add(directionalLight);
var directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.position.set(0, 1, -2).normalize();
scene.add(directionalLight);
if (webglAvailable()) {
renderer = new THREE.WebGLRenderer();
} else {
renderer = new THREE.CanvasRenderer();
}
renderer.setClearColor( 0xCDCDCD, 1 );
// renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
document.addEventListener('mousemove', onDocumentMouseMove, false);
window.addEventListener('resize', onWindowResize, false);
createPlane(500, 500);
createCube(500);
loadStl();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onDocumentMouseMove(event) {
mouseX = (event.clientX - windowHalfX) / 2;
mouseY = (event.clientY - windowHalfY) / 2;
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
function createPlane(width, height) {
var planegeometry = new THREE.PlaneBufferGeometry(width, height, 0, 0);
var material = new THREE.MeshLambertMaterial({
color: 0xFFFFFF,
side: THREE.DoubleSide
});
planegeometry.computeBoundingBox();
planegeometry.center();
meshPlane = new THREE.Mesh(planegeometry, material);
meshPlane.rotation.x = 90 * (Math.PI/180);
//meshPlane.position.y = -height/2;
scene.add(meshPlane);
}
function createCube(size) {
var geometry = new THREE.BoxGeometry( size, size, size );
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
geometry.center();
var material = new THREE.MeshPhongMaterial({
color: 0xFF0000,
opacity: 0.04,
transparent: true,
wireframe: true,
side: THREE.DoubleSide
});
meshCube = new THREE.Mesh(geometry, material);
meshCube.position.y = size/2;
scene.add(meshCube);
}
function loadStl() {
var loader = new THREE.STLLoader();
loader.load( stlPath, function ( geometry ) {
// Convert BufferGeometry to Geometry
var geometry = new THREE.Geometry().fromBufferGeometry( geometry );
geometry.computeBoundingBox();
geometry.computeVertexNormals();
geometry.center();
var faces = geometry.faces;
for(var index in faces){
var face = faces[index];
var faceNormal = face.normal;
var axis = new THREE.Vector3(0,-1,0);
var angle = Math.acos(axis.dot(faceNormal));
var angleReal = (angle / (Math.PI/180));
if(angleReal <= 70){
face.materialIndex = 1;
}
else{
face.materialIndex = 0;
}
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
meshStl = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
meshStl.position.x = 0;
meshStl.position.y = 400;
scene.add( meshStl );
// Once loaded, calculate projections mesh
calculateProjectedMesh();
});
}
function calculateProjectedMesh(){
var geometry = meshStl.geometry;
var faces = geometry.faces;
var vertices = geometry.vertices;
var geometry_projected = new THREE.Geometry();
var faces_projected = [];
var vertices_projected = [];
meshStl.updateMatrixWorld();
for(var index in faces){
var face = faces[index];
// This are the faces
if(face.materialIndex == 1){
var vertexIndexes = [face.a, face.b, face.c];
for (var i = 0, l = vertexIndexes.length; i < l; i++) {
var relatedVertice = vertices[ vertexIndexes[i] ];
var vectorClone = relatedVertice.clone();
console.warn(vectorClone);
vectorClone.applyMatrix4( meshStl.matrixWorld );
////////////////////////////////////////////////////////////////
// TEST: draw line
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z));
//geometry.vertices.push(new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z));
geometry.vertices.push(new THREE.Vector3(vectorClone.x, meshPlane.position.y, vectorClone.z));
var line = new THREE.Line(geometry, lineMaterial);
scene.add(line);
console.log("line added");
////////////////////////////////////////////////////////////////
vectorClone.y = 0;
var vector = new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z);
vertexIndexes[i] = vertices_projected.push( vector ) - 1;
}
var newFace = new THREE.Face3( vertexIndexes[0], vertexIndexes[1], vertexIndexes[2] );
newFace.materialIndex = 2;
faces_projected.push(newFace);
}
}
geometry_projected.faces = faces_projected;
geometry_projected.vertices = vertices_projected;
geometry_projected.mergeVertices();
console.info(geometry_projected);
meshHang = new THREE.Mesh(geometry_projected, new THREE.MeshFaceMaterial(materials));
var newY = -(2 * meshStl.position.y) + 0;
var newY = -meshStl.position.y;
meshHang.position.set(0, newY, 0);
meshStl.add( meshHang );
}
EDIT: Finally!! I got it! To clone the original faces I must access their 3 original vertices using "a", "b" and "c" properties, which are indexes referencing Vector3 instances in the "vertices" array of the original geometry.
I cloned the 3 vertices flatting the Z position to zero, use their new indexes to create the new face and add it to the projection mesh (in blue).
I´m also adding lines as a visual union between both faces. Now I´m ready for step 3, but I think this is complex enough to close this question.
Thanks for the updateMatrixWorld clue! It was vital to achieve my goal ;)
try this
original_geometry.updateMatrixWorld();
var vertexIndexes = [null, null, null];
for (var i = 0, l = vertexNormals.length; i < l; i++) {
var position = original_geometry.geometry.vertices[i].clone();
position.applyMatrix4( original_geometry.matrixWorld );
var vector = new THREE.Vector3(position.x, position.y, position.z)
vertexIndexes[i] = geovertices.push( vector ) - 1;
}

EdgesHelper loses its mesh when scene is rotated

I've drawn a mesh with a edgeshelper.
When I rotate the scene using the mouse, the edges seems to react twice as fast the mesh.
The result is that the edges don't "fit" around the mesh anymore when the scene is rotated.
What am I doing wrong ?
<html>
<body onmousemove="bmousemove(event);">
<script src="three.min.js"></script>
<script>
var prevX = 0, prevY = 0;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight, 1,10000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
geometry = new THREE.BoxGeometry( 10, 20, 40, 1, 1, 1 );
material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
object = new THREE.Mesh( geometry, material );
edges = new THREE.EdgesHelper( object, 0xffffff );
camera.position.z = 100;
scene.add( object );
scene.add( edges );
render();
function render()
{
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function bmousemove(e)
{
if (prevX == 0)
{
prevX = e.clientX;
prevY = e.clientY;
}
scene.rotation.y += (e.clientX - prevX) / 100;
scene.rotation.x += (e.clientY - prevY) / 100;
prevX = e.clientX;
prevY = e.clientY;
}
</script>
</body>
</html>
I'm using version r71 under Windows 7
Yes, this is a consequence of how EdgesHelper(and some of the other helpers) have been implemented.
If you want to use EdgesHelper, you have to adhere to two rules:
EdgesHelper must be added to the scene, directly.
The scene can't have a transform applied (for example, be rotated).
Consequently, you will have to rotate the mesh, instead.
three.js r.71

camera inside a sphere

I want to create a skydome and made the shpere, texture loading also fine but I cannot move the camera inside the sphere.
The sphere disappears. I know it is an amateur issue but cannot see the inside of the sphere.
Is it some kind of cutting or Z-buffer issue?
How can I fix it?
My code:
<html>
<head>
<script src="js/jquery-1.8.3.min.js"></script>
<script src="js/three.min.js"></script>
</head>
<body>
<div id="container">
</div>
<script>
function addSpaceSphere( ){
// set up the sphere vars
var radius = 200,
segments = 16,
rings = 16;
var material = new THREE.MeshPhongMaterial({
color:0xFFFFFF,
map: THREE.ImageUtils.loadTexture( 'textures/SPACE014SX.png' )
});
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(
radius,
segments,
rings
),
material
);
// add the sphere to the scene
scene.add(sphere);
}
function addLights(){
// create a point light
var ambient = new THREE.AmbientLight( 0xFFFFFF );
scene.add( ambient );
}
function render() {
camera.lookAt( focus );
camera.updateProjectionMatrix();
renderer.render( scene, camera );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function createScene(){
// add the camera to the scene
scene.add(camera);
// the camera starts at 0,0,0
// so pull it back
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 300;
// start the renderer
renderer.setSize(WIDTH, HEIGHT);
$container.append(renderer.domElement);
addSpaceSphere( );
addLights();
animate();
}
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.01,
FAR = 10000;
var focus = new THREE.Vector3( 0, 0, 0 );
var isUserInteracting = false,
onPointerDownPointerX = 0, onPointerDownPointerY = 0,
lon = 0, onPointerDownLon = 0,
lat = 0, onPointerDownLat = 0,
phi = 0, theta = 0;
var $container = $('#container');
// create a WebGL renderer, camera
// and a scene
//var renderer = new THREE.CanvasRenderer();
var renderer = new THREE.WebGLRenderer();
var camera = new THREE.PerspectiveCamera(
VIEW_ANGLE, ASPECT, NEAR, FAR
);
var scene = new THREE.Scene();
createScene();
</script>
</body>
make the skydome material double-sided -- it's being culled. Set the 'side' attribute to THREE.DoubleSide
(or THREE.BackSide should work too, if the camera is ONLY inside the sphere)

Resources