Adding a different colour to each side of this obj - three.js

I've recreated a bag model for my application and exported it into ThreeJs as an .obj:
I've assigned a different colour to every face found in the models geometry like this:
var geometry = new THREE.Geometry().fromBufferGeometry( bagMesh.children[0].geometry );
for (var i = 0; i < geometry.faces.length; i ++ ) {
var face = geometry.faces[i];
// 7 & 8 = front side
// can we flip its normal?
if(i === 7 || i === 8) {
face.color.setHex( 0xff0000 );
} else {
face.color.setHex( Math.random() * 0xffffff );
}
}
geometry.translate( 0, -1, 0.75);
mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors, side: THREE.DoubleSide }) );
scene.add(mesh);
I've identified the faces of the front-side at indices 7 and 8 of the faces array and turned them red.
The problem is that this colour can be seen when I look inside of the bag too:
I realize that this is because I've set the object to THREE.DoubleSide but if I change it to THREE.FrontSide then the sides only partially visible.
So my question is how do I assign a different unique colour to each side (all 11 of them, counting the inside too) without that colour appearing on that sides respective opposite?
I'm trying to keep things simple here by only using colours as opposed to mapping images onto it, which is what I'll want to eventually get to.
Note - My previous model solved this problem by treating each side as a seperate mesh but this caused other issues like z-hiding and flickering problems.
Thanks
EDIT
#WestLangley I've setup a fiddle to demonstrate what you added in your comment. Assuming that I got it right it didn't have the desired affect:
(function onLoad() {
var canvasElement;
var width, height;
var scene, camera;
var renderer;
var controls;
var pivot;
var bagMesh;
var planeMesh;
const objLoader = new THREE.OBJLoader2();
const fileLoader = new THREE.FileLoader();
init();
function init() {
container = document.getElementById('container');
initScene();
addGridHelper();
addCamera();
addLighting();
addRenderer();
addOrbitControls();
loadPlaneObj();
// Logic
var update = function() {};
// Draw scene
var render = function() {
renderer.render(scene, camera);
};
// Run game logic (update, render, repeat)
var gameLoop = function() {
requestAnimationFrame(gameLoop);
update();
render();
};
gameLoop();
}
/**** Basic Scene Setup ****/
function initScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0xd3d3d3);
var axis = new THREE.AxesHelper();
scene.add(axis);
}
function addCamera() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(3,3,3);
scene.add(camera);
}
function addGridHelper() {
var planeGeometry = new THREE.PlaneGeometry(2000, 2000);
planeGeometry.rotateX(-Math.PI / 2);
var planeMaterial = new THREE.ShadowMaterial({
opacity: 0.2
});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.position.y = -200;
plane.receiveShadow = true;
scene.add(plane);
var helper = new THREE.GridHelper(2000, 100);
helper.material.opacity = 0.25;
helper.material.transparent = true;
scene.add(helper);
var axis = new THREE.AxesHelper();
scene.add(axis);
}
// *********** Lighting settings **********************
function addLighting() {
var light = new THREE.HemisphereLight(0xffffff, 0xffffff, 1);
scene.add(light);
}
// ************** Material settings **************
function setMaterial(materialName) {
// get the object from the scene
var bagMesh = scene.getObjectByName('bag');
var material;
if (!materialName) {
materialName = materials.material;
}
if (bagMesh) {
var colour = parseInt(materials.colour);
switch (materialName) {
case 'MeshBasicMaterial':
material = new THREE.MeshBasicMaterial({
color: colour
});
break;
case 'MeshDepthMaterial':
material = new THREE.MeshDepthMaterial();
break;
case 'MeshLambertMaterial':
material = new THREE.MeshLambertMaterial({
color: colour
});
break;
case 'MeshNormalMaterial':
material = new THREE.MeshNormalMaterial();
break;
case 'MeshPhongMaterial':
material = new THREE.MeshPhongMaterial({
color: colour
});
break;
case 'MeshPhysicalMaterial':
material = new THREE.MeshPhysicalMaterial({
color: colour
});
break;
case 'MeshStandardMaterial':
material = new THREE.MeshStandardMaterial({
color: colour
});
break;
case 'MeshToonMaterial':
material = new THREE.MeshToonMaterial({
color: colour
});
break;
}
bagMesh.children.forEach(function(c) {
c.material = material;
});
}
}
function setMaterialColour(colour) {
materials.colour = colour;
setMaterial(null);
}
// ************** End of materials ***************
function addRenderer() {
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
}
function addOrbitControls() {
var controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function addPivot() {
var cubeGeo = new THREE.BoxBufferGeometry(5, 5, 5);
var cubeMat = new THREE.MeshBasicMaterial();
pivot = new THREE.Mesh(cubeGeo, cubeMat);
bagMesh.position.x -= 15;
bagMesh.position.z -= 55;
pivot.add(bagMesh);
pivot.add(handle);
scene.add(pivot);
}
function loadPlaneObj() {
loadObj('Plane', 'https://rawgit.com/Katana24/threejs-experimentation/master/models/Plane.obj', 'https://rawgit.com/Katana24/threejs-experimentation/master/models/Plane.mtl', addPlaneToSceneSOAnswer);
}
function loadObj(objName, objUrl, mtlUrl, onLoadFunc) {
var onLoadMtl = function(materials) {
objLoader.setModelName(objName);
objLoader.setMaterials(materials);
fileLoader.setPath('');
fileLoader.setResponseType('arraybuffer');
fileLoader.load(objUrl,
function(onLoadContent) {
var mesh = objLoader.parse(onLoadContent);
onLoadFunc(mesh);
},
function(inProgress) {},
function(error) {
throw new Error('Couldnt load the model: ', error);
});
};
objLoader.loadMtl(mtlUrl, objName+'.mtl', onLoadMtl);
}
function addPlaneToSceneSOAnswer(mesh) {
var frontMaterial = new THREE.MeshBasicMaterial( { color : 0xff0000, side: THREE.FrontSide } );
var backMaterial = new THREE.MeshBasicMaterial( { color : 0x00ff00, side: THREE.BackSide } );
var geometry = new THREE.Geometry().fromBufferGeometry( mesh.children[0].geometry );
var length = geometry.faces.length;
geometry.faces.splice(14, 1);
for (var i = 0; i < geometry.faces.length; i ++ ) {
var face = geometry.faces[i];
face.color.setHex(Math.random() * 0xffffff);
}
mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors, side: THREE.DoubleSide }) );
mesh.material.side = THREE.FrontSide;
var mesh2 = new THREE.Mesh( geometry, mesh.material.clone() );
mesh2.material.side = THREE.BackSide;
// mesh2.material.vertexColors = THREE.NoColors;
mesh2.material.vertexColors = [new THREE.Color(0xff0000), new THREE.Color(0x00ff00), new THREE.Color(0x0000ff)];
mesh.add( mesh2 );
scene.add(mesh);
}
})();
body {
background: transparent;
padding: 0;
margin: 0;
font-family: sans-serif;
}
#canvas {
margin: 10px auto;
width: 800px;
height: 350px;
margin-top: -44px;
}
<body>
<div id="container"></div>
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/loaders/MTLLoader.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/LoaderSupport.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/OBJLoader2.js"></script>
</body>
What am I missing here?

I followed along with Don's suggestion about the different materials but didn't know entirely what he meant.
I examined this question which details setting the materialIndex. I investigated what this means and what it means is that when you pass a geometry and an array of materials to a mesh like this:
mesh = new THREE.Mesh( geometry, [frontMaterial, backMaterial, otherMaterial] );
then that face will get the material (frontMaterial because it's at position 0) assigned to it.
Coming back to my original question, I decided to simplify (for the moment) and see if I could apply what I want to just a Plane mesh exported from Blender.
The Plane has two Faces when added into 3JS. I found I could flip each face or assign a different material to each but I needed to duplicate the faces in order to achieve this:
function addMeshTwoToScene() {
var frontMaterial = new THREE.MeshBasicMaterial( { color : 0xff0000, side: THREE.FrontSide } );
var backMaterial = new THREE.MeshBasicMaterial( { color : 0x00ff00, side: THREE.BackSide } );
var geometry = new THREE.Geometry().fromBufferGeometry( planeMesh.children[0].geometry );
// Duplicates the face
var length = geometry.faces.length;
for (var i = 0; i < length; i++ ) {
var face = geometry.faces[i];
var newFace = Object.assign({}, face);
geometry.faces.push(newFace);
}
for (var i = 0; i < geometry.faces.length; i ++ ) {
var face = geometry.faces[i];
if(i === 0 || i === 3) {
face.materialIndex = 0;
} else {
face.materialIndex = 1;
}
}
var mesh = new THREE.Mesh( geometry, [frontMaterial, backMaterial] );
scene.add(mesh);
}
This results in the following:
I'm not going to mark this as the accepted answer yet as I still need to apply it to the more complex model in the question plus I think there could still be a better way to do this, like flipping a particular vertex to some other value.

One solution would be to use a ShaderMaterial and define the colors based on whether the face is front or back facing.
Let me walk you through this simple example
Hold left click to rotate the mesh. If you're not familiar with ShaderFrog, click "Edit Source" on the right and scroll down the bottom of the fragment shader.
if (!gl_FrontFacing) gl_FragColor = vec4(vec3(0.0, 0.0, 1.0) * brightness, 1.0);
gl_FrontFacing is a boolean. Quite self explanatory, it'll return true if a face is front, and false otherwise.
The line reads "if the face is not front facing, render it blue at with alpha = 1.0.
Hoping that helps.

Related

ThreeJS - Bounding Box not set on object with material

I've tried to add Bounding Box to my object but it seems to work only for the testObj, he does not work for my others objects with texture.
var testObj = new THREE.Mesh(
new THREE.CylinderGeometry( 1 , 1 , 4 , 8 ),
new THREE.MeshLambertMaterial({ color: 0xff00ff })
);
scene.add(testObj );
staticCollideMesh.push(testObj );
// PADDLE1
loaderTexture.load('http://localhost:8000/WoodTexture.jpg', function (texture ) {
var material = new THREE.MeshLambertMaterial( {
map: texture
});
var geometry = new THREE.BoxGeometry(PADDLE_WIDTH, PADDLE_HEIGHT, PADDLE_DEPTH );
paddle1 = new THREE.Mesh( geometry, material);
paddle1.castShadow = true;
paddle1.receiveShadow = true;
paddle1.name = "paddle1";
scene.add( paddle1 );
staticCollideMesh.push(paddle1);
}, undefined, function ( err ) {
console.error( 'WoodTexture1.jpg : An error happened.' );
}
);
This is how I add BBox and BoxHelper :
let constructCollisionBoxes = function() {
staticCollideMesh.forEach( function( mesh ){
mesh.BBox = new THREE.Box3().setFromObject( mesh );
mesh.BBoxHelper = new THREE.BoxHelper( mesh , 0xff0000 );
scene.add( mesh.BBoxHelper );
});
}
I don't know why the loop just apply for my cylinder ... I need help to understand why this is not working.
EDIT: thanks to #prisoner849 I just added the function in the loader
scene.add(paddle1);
staticCollideMesh.push(paddle1);
constructionCollisionMesh();
In case of using loaders, keep in mind, that loading is asynchronous, so when you call constructCollisionBoxes(), your box, whose creation relies on the moment of finishing of loading of the texture, is not in the staticCollideMesh array yet.
To fix it, you can do it this way:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(1);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.25));
var staticCollideMesh = [];
var PADDLE_WIDTH = 3,
PADDLE_HEIGHT = 3,
PADDLE_DEPTH = 2;
var testObj = new THREE.Mesh(
new THREE.CylinderGeometry(1, 1, 4, 8),
new THREE.MeshLambertMaterial({
color: 0xff00ff
})
);
scene.add(testObj);
staticCollideMesh.push(testObj);
var texture = new THREE.TextureLoader().load(`https://threejs.org/examples/textures/uv_grid_opengl.jpg`);
// PADDLE1
var material = new THREE.MeshLambertMaterial({
map: texture
});
var geometry = new THREE.BoxGeometry(PADDLE_WIDTH, PADDLE_HEIGHT, PADDLE_DEPTH);
paddle1 = new THREE.Mesh(geometry, material);
paddle1.castShadow = true;
paddle1.receiveShadow = true;
paddle1.name = "paddle1";
scene.add(paddle1);
staticCollideMesh.push(paddle1);
let constructCollisionBoxes = function() {
staticCollideMesh.forEach(function(mesh) {
mesh.BBox = new THREE.Box3().setFromObject(mesh);
mesh.BBoxHelper = new THREE.BoxHelper(mesh, 0xff0000);
scene.add(mesh.BBoxHelper);
});
}
constructCollisionBoxes();
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://unpkg.com/three#0.115.0/build/three.min.js"></script>
<script src="https://unpkg.com/three#0.115.0/examples/js/controls/OrbitControls.js"></script>

Threejs & PhysiJs physics engine not updated when Mesh rotate

I am a beginner of Threejs.
I created a Box Mesh and a Sphere Mesh and applied physics using physiJs.
What I want to do is to hit the ball when the Box Mesh rotates and passing through the ball.
However, when the box mesh rotates, it passes without hitting the ball.
I think the box mesh loses physicality when it starts spinning.
function createBall () {
var ball = null;
var ballGeo = new THREE.SphereGeometry(1.5, 30, 30);
var ballMat = Physijs.createMaterial(
new THREE.MeshBasicMaterial({specular: 0x111111})
, 0.3, 0.1
);
ball = new Physijs.SphereMesh(
ballGeo,
ballMat,
5
);
ball.position.set(30, 10, 0);
scene.add(ball);
}
function createBox () {
var material = Physijs.createMaterial(
new THREE.MeshLambertMaterial(
{
color: 0x8041D9,
}), 5, 0.3);
var boxMesh = new THREE.BoxGeometry(5, 5, 25);
box = new Physijs.BoxMesh(
boxMesh,
material,
5
);
box.position.z = 20;
scene.add(box);
}
function createHeightMap() {
var initColor = new THREE.Color( 0x00ff00 );
initColor.setHSL( 0.25, 0.85, 0.5 );
var ground_material = Physijs.createMaterial(
new THREE.MeshPhongMaterial(
{ color: 0x47C83E}
),
.5,
.5
);
var ground_geometry = new THREE.PlaneGeometry(800, 800, 100, 100);
ground = new Physijs.HeightfieldMesh(
ground_geometry,
ground_material,
0, // 질량
100, // PlaneGeometry 의 분할 세그먼트랑 똑같은 값으로 줘야 한다.
100 // PlaneGeometry 의 분할 세그먼트랑 똑같은 값으로 줘야 한다.
);
ground.position.y = -10;
ground.rotation.x = Math.PI / -2;
ground.receiveShadow = true;
var meshes = [];
var controls = new function () {
this.startRotate = false;
this.addBall = function () {
createBall();
};
this.addBox = function () {
createBox();
};
this.clearMeshes = function () {
meshes.forEach(function (e) {
scene.remove(e);
});
meshes = [];
}
};
var gui = new dat.GUI();
gui.add(controls, 'addBall');
gui.add(controls, 'addBox');
gui.add(controls, 'clearMeshes');
gui.add(controls, 'startRotate').onChange(function (e) {
isStartRoate = e;
});
return ground;
}
render = function () {
stats.update();
if (isStartRoate === true) {
var rotateMatrix = new THREE.Matrix4();
rotateMatrix.identity();
rotateMatrix.makeRotationY(0.05);
box.applyMatrix(rotateMatrix);
}
requestAnimationFrame(render);
renderer.render(scene, camera);
var axes = new THREE.AxesHelper(30);
scene.add(axes);
scene.simulate(undefined, 2);
};
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
window.onload = initScene;
below is codepen link
codepen
It seems like physicality not updated.
please give me any idea
When using Physijs, you should use setLinearVelocity() or setAngularVelocity() in order to update the position and rotation of your objects in a physical correct way. The updated codepen shows this approach:
https://codepen.io/anon/pen/YJmajN
Besides, the way you create AxesHelper in the render loop is no good approach. Create the helper once during the setup up of your scene.

Bouncy Ball doesn't bounce straight up

I am trying to make the game brickbreaker with Three.js and Physi.js objects. So far I have the bricks and the paddle working. However, when I create a bouncy ball, it seems to be jumping all over the place instead of simply up and down at different angles. It also seems like the ball keeps bouncing even if it isn't hitting the paddle. Can any one help with my code?
var scene, camera, renderer;
var wall, brick, ball;
var npc;
var controls =
{ left:false, right:false,
speed:10,}
var counter=0;
var ball;
console.log("Checking!");
init();
animate();
/****************************************************************
To initialize the scene, we initialize each of its components *
****************************************************************/
function init(){
initPhysijs();
scene = initScene();
initRenderer();
initControls();
addLight();
camera = addCamera();
addBricks();
addWalls();
addNPC();
var ball = createBall();
ball.position.set(0,-10,0);
scene.add(ball)
}
function initScene(){
var scene = new Physijs.Scene();
return scene;
}
function initPhysijs(){
Physijs.scripts.worker = '../js/physijs_worker.js';
Physijs.scripts.ammo = '../js/ammo.js';
}
function initRenderer(){
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xF7F9F9));
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
}
function initControls(){
// here is where we create the eventListeners to respond to operations
//create a clock for the time-based animation ...
clock = new THREE.Clock();
clock.start();
window.addEventListener( 'keydown', keydown);
window.addEventListener( 'keyup', keyup );
}
function keydown(event){
console.dir(event);
console.log("Keydown: '"+event.key+"'");
// this is the regular scene
switch (event.key){
// change the way the avatar is moving
case "ArrowRight": controls.right = true;console.log("coneAvatar moving forward"); break;
case "ArrowLeft": controls.left = true; break;
}
}
function keyup(event){
//console.log("Keydown:"+event.key);
//console.dir(event);
switch (event.key){
case "ArrowRight": controls.right = false; break;
case "ArrowLeft": controls.left = false; break;
}
}
function updateNPC(npc,controls){
var forward = npc.getWorldDirection();
if (controls.left){
npc.position.set(counter-.2,-15,0)
npc._dirtyPosition=true;
// npc.position.x+=1;
counter=counter-.2;
} else if (controls.right){
npc.position.set(counter+.2,-15,0)
npc._dirtyPosition=true;
// npc.position.x=npc.position-1;
counter=counter+.2;
}
else{
npc.position.set(counter,-15,0);
}
}
function addLight() {
var spotLight = new THREE.DirectionalLight(0xffffff);
spotLight.position.set(30, 40, 50);
spotLight.intensity = 1;
scene.add(spotLight);
}
function addCamera(){
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.z=24;
camera.position.y=0;
camera.position.x=0;
camera.lookAt(0,0,0);
return camera;
}
function createBall(){
//var geometry = new THREE.SphereGeometry( 4, 20, 20);
var geometry = new THREE.SphereGeometry( 1, 16, 16);
var material = new THREE.MeshLambertMaterial( { color: 0x444444} );
var pmaterial = new Physijs.createMaterial(material,0.9,3);
var mesh = new Physijs.BoxMesh( geometry, pmaterial );
mesh.setDamping(0.01,0.01);
mesh.castShadow = true;
return mesh;
}
/* Adds the bricks to the scene to form the wall the player will try to break down
Added by Allison Regna */
function addBricks() {
var yPos = 0;
var zPos = 0;
var colors = [0x1DD3B0, 0xAFFC41, 0xB2FF9E, 0x75B9BE, 0x7D82B8, 0x00A5CF, 0x987284, 0xAAEFDF, 0xAED6F1];
for(var i= 1; i <= 8 ; i++){
xPos = -30.5;
for(var j= 1; j <=16; j++){
color = colors[getRandomInt(9)];
brick = createBrick(color);
brick.position.set(xPos, yPos, zPos);
scene.add(brick);
brick.addEventListener('collision',
function( other_object, relative_velocity, relative_rotation, contact_normal ) {
if (other_object == ball){
console.log("The ball broke the brick!");
// make the brick drop below the scene ..
this.position.y = this.position.y - 100;
this.__dirtyPosition = true;
}
}
)
xPos += 4.10;
}
yPos += 2.10;
}
}
/* Returns a random integer between 0 inclusive and maxInt noninclusive
Added by Allison Regna */
function getRandomInt(maxInt) {
return Math.floor(Math.random() * Math.floor(maxInt));
}
function addNPC(){
npc = createBoxMesh(0x0000ff);
npc.position.set(0,-15,0);
npc.scale.set(5,2,1);
npc.rotateY(100)
scene.add(npc);
console.dir(npc);
}
/* Adds walls to the scene so the ball can bounce off back into the players view
Added by Allison Regna */
function addWalls(){
var topWall = createWall();
topWall.position.set(0, 18, 0);
scene.add(topWall);
var leftWall = createWall();
leftWall.position.set(-39, 0, 0);
leftWall.rotateZ(Math.PI/2);
scene.add(leftWall);
var rightWall = createWall();
rightWall.position.set(39, 0, 0);
rightWall.rotateZ(Math.PI/2);
scene.add(rightWall);
}
function createBoxMesh(color){
var geometry = new THREE.BoxGeometry( 2, .1, .1);
var material = new THREE.MeshLambertMaterial( { color: color} );
var pmaterial= new Physijs.createMaterial( material, 0.9, .95 );
mesh = new Physijs.BoxMesh( geometry, pmaterial, 0);
mesh.castShadow = true;
mesh.float=true;
return mesh;
}
/* Creates a brick mesh
Added by Allison Regna */
function createBrick(color){
var geometry = new THREE.PlaneGeometry( 4, 2, 4 );
var material = new THREE.MeshBasicMaterial( { color: color, wireframe: false } );
var pmaterial= new Physijs.createMaterial( material, 0.9, 0.05 );
brickMesh = new Physijs.BoxMesh( geometry, pmaterial, 0 );
brickMesh.castShadow = true;
return brickMesh;
}
/* Creates a wall mesh that will keep the ball inside the scene
Added by Allison Regna */
function createWall(){
var geometry = new THREE.PlaneGeometry( 100, 1, 1 );
var material = new THREE.MeshBasicMaterial( {color: 0xffffff} );
var pmaterial = new Physijs.createMaterial ( material, .9, 0.05 );
var wall = new THREE.Mesh( geometry, material, 0 );
return wall;
}
function animate() {
requestAnimationFrame( animate );
updateNPC(npc,controls);
renderer.render( scene, camera );
scene.simulate();
}
<!DOCTYPE html>
<!--
PA03 Group J-Crew
-->
<html>
<head>
<meta charset=utf-8>
<title>Game 0</title>
<style>
body { margin: 0;}
canvas { width: 100%; height: 100%;}
</style>
</head>
<body>
<script src="https://github.com/mrdoob/three.js/"></script>
<script src="https://github.com/chandlerprall/Physijs"></script>
<script src="https://github.com/dataarts/dat.gui"></script>
<script src="https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js"></script>
<script src="Final_project.js"></script>
<div id="info"></div>
</body>
</html>

Three JS: How to add images on each vertices of icosahedron

I'm new to Three js, i was trying to create a rotating icosahedron with small icon kind of images on each vertex using three js, i could create the icosahedron and make it rotate but I'm not able to attach images on each vertex of it. Can anyone help me do this?
Please check the js fiddle link of what i could acheive so far:
<div id="container"></div>
var $container = $('#container');
var renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
var camera = new THREE.PerspectiveCamera(80, 1, 0.1, 10000);
var scene = new THREE.Scene();
var Ico;
scene.add(camera);
renderer.setSize(576, 576);
// Making the canvas responsive
function onWindowResize() {
var screenWidth = $(window).width();
if (screenWidth <= 479) {
renderer.setSize(300, 300);
} else if (screenWidth <= 767) {
renderer.setSize(400, 400);
} else if (screenWidth <= 991) {
renderer.setSize(500, 500);
} else if (screenWidth <= 1200) {
renderer.setSize(450, 450);
} else if (screenWidth <= 1366) {
renderer.setSize(550, 550);
}
camera.updateProjectionMatrix();
}
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
$container.append(renderer.domElement);
// Camera
camera.position.z = 200;
// Material
var greyMat = new THREE.MeshPhongMaterial({
color: new THREE.Color("rgb(125,127,129)"),
emissive: new THREE.Color("rgb(125,127,129)"),
specular: new THREE.Color("rgb(125,127,129)"),
shininess: "100000000",
shading: THREE.FlatShading,
transparent: 1,
opacity: 1
});
var L2 = new THREE.PointLight();
L2.position.z = 1900;
L2.position.y = 1850;
L2.position.x = 1000;
scene.add(L2);
camera.add(L2);
var Ico = new THREE.Mesh(new THREE.IcosahedronGeometry(125, 1), greyMat);
Ico.rotation.z = 0.5;
scene.add(Ico);
var trackballControl = new THREE.TrackballControls(camera, renderer.domElement);
trackballControl.rotateSpeed = 1.0;
trackballControl.noZoom = true;
function update() {
Ico.rotation.x += 2 / 500;
Ico.rotation.y += 2 / 500;
}
// Render
function render() {
trackballControl.update();
requestAnimationFrame(render);
renderer.render(scene, camera);
update();
}
render();
https://jsfiddle.net/arunvenugopal11/uoxtmtnr/
Thanks in advance :)
You can use THREE.Sprite(), like this:
var txtLoader = new THREE.TextureLoader();
txtLoader.setCrossOrigin(""); // you don't need it, if you get images from your web site
var textures = [ // you can have a full set of 42 images, I used just 2
"https://threejs.org/examples/textures/UV_Grid_Sm.jpg",
"https://threejs.org/examples/textures/colors.png"
];
var direction = new THREE.Vector3(); // we'll re-use it in the loop
Ico.geometry.vertices.forEach(function(vertex, index){
var texture = txtLoader.load(textures[index % 2]); // when you have a full set of images, you don't need that operation with modulus '%'
var spriteMaterial = new THREE.SpriteMaterial({map: texture});
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.setScalar(10); // the size is up to you
direction.copy(vertex).normalize(); // direction is just a normalized vertex
sprite.position.copy(vertex).addScaledVector(direction, 10); // add scaled direction to the position of a sprite
Ico.add(sprite);
});
jsfiddle example. r85

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;
}

Resources