Three.js not scaling or rotating Mesh on render() - geometry

I've been trying out Three.js today and I've had trouble getting my Mesh to change according using mesh.rotation.x += 10; for example.
The code below renders a triangle and the camera moves around onMouseMove but inside the render() function, it fails to scale or rotate the Mesh obj.
Appreciate the pointers.
<body>
<div id="container" style="border: #0f0 solid;">
</div>
<script type="text/javascript">
var mouseX = 0, mouseY = 0;
windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2;
// get the DOM element to attach to
var container = document.getElementById("container");
// create a WebGL renderer, camera
// and a scene
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
camera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 50, 1e7 );
var scene = new THREE.Scene();
var obj;
var geom = new THREE.Geometry();
var stats;
init();
animate();
function init(){
// the camera starts at 0,0,0 so pull it back
camera.position.z = 400;
// start the renderer
// attach the render-supplied DOM element
container.appendChild(renderer.domElement);
// create a new mesh with triangle geometry
// create the sphere's material
var material = new THREE.MeshLambertMaterial(
{
color: 0x00FF00
});
var v1 = new THREE.Vector3(50,0,0);
var v2 = new THREE.Vector3(50,100,0);
var v3 = new THREE.Vector3(0, 50, 0);
geom.vertices.push(new THREE.Vertex(v1));
geom.vertices.push(new THREE.Vertex(v2));
geom.vertices.push(new THREE.Vertex(v3));
geom.faces.push(new THREE.Face3(0,1,2));
geom.computeFaceNormals();
obj = new THREE.Mesh(geom, material);
obj.doubleSided = true;
obj.rotation.x = 0.1;
// add the obj to the scene
scene.addObject(obj);
// create a point light
var pointLight = new THREE.PointLight( 0xFFFFFF );
// set its position
pointLight.position.x = 0;
pointLight.position.y = 0;
pointLight.position.z = 600;
//pointLight.lookAt(obj);
// add to the scene
scene.add(pointLight);
ambientLight = new THREE.AmbientLight( 0xbbbbbb );
scene.add(ambientLight);
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
// draw!
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function onDocumentMouseMove(event) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY + 200 - camera.position.y ) * .05;
camera.lookAt( scene.position );
var time = Date.now() * 0.0015;
for ( var i = 0; i < scene.objects.length; i ++ ) {
scene.objects[ i ].rotation.y = time * ( i % 2 ? 1 : -1 );
}
obj.rotation.x += 20;
renderer.clear();
renderer.render( scene, camera );
}
</script>
</body>

From r45 scene.addObject(obj); must be now scene.add(obj);
See Three.js commit log:
2011 10 06 - r45 - Object/Scene.add*() and Object/Scene.remove*() are now Object/Scene.add() and Object/Scene.remove()

Related

Coloring faces of a Three.js BoxGeometry

I saw some solutions that accessed the THREE.BoxGeometry faces like that:
var geometry = new THREE.BoxGeometry(n,n,n)
let faces = geometry.faces;
for (let face of faces) {
face.color.set(Math.random() * 0xffffff);
}
But it seems that the faces-array doesn't exist in the current Three.js version r129 (Uncaught TypeError: faces is not iterable).
How can I achieve an easy coloring of the six cube faces?
Try it like so:
let camera, scene, renderer, mesh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;
scene = new THREE.Scene();
const geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 ).toNonIndexed();
// vertexColors must be true so vertex colors can be used in the shader
const material = new THREE.MeshBasicMaterial( { vertexColors: true } );
// generate color data for each vertex
const positionAttribute = geometry.getAttribute( 'position' );
const colors = [];
const color = new THREE.Color();
for ( let i = 0; i < positionAttribute.count; i += 3 ) {
color.set( Math.random() * 0xffffff );
// define the same color for each vertex of a triangle
colors.push( color.r, color.g, color.b );
colors.push( color.r, color.g, color.b );
colors.push( color.r, color.g, color.b );
}
// define the new attribute
geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render( scene, camera );
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.129.0/build/three.js"></script>
The previous geometry format was removed from the core with r125. More information in the following post at the three.js forum.
https://discourse.threejs.org/t/three-geometry-will-be-removed-from-core-with-r125/22401

THREE.js - How to TWEEN individual paths/polygons of imported SVG?

I have imported a test SVG using the SVGLoader and am able to tween the SVG object using Tween.js.
I would like to tween the individual paths and polygons within the loaded SVG object - is this possible?
SVGLoader:
var loader = new THREE.SVGLoader();
loader.load( 'svg/test.svg', function ( paths ) {
var group = new THREE.Group();
group.scale.multiplyScalar( 0.25 );
group.position.x = -150;
group.position.y = 70;
group.scale.y *= - 1;
for ( var i = 0; i < paths.length; i ++ ) {
var path = paths[ i ];
var material = new THREE.MeshBasicMaterial( {
color: path.color,
side: THREE.DoubleSide,
depthWrite: false
} );
var shapes = path.toShapes( true );
for ( var j = 0; j < shapes.length; j ++ ) {
var shape = shapes[ j ];
var geometry = new THREE.ShapeBufferGeometry( shape );
var mesh = new THREE.Mesh( geometry, material );
group.add( mesh );
}
}
scene.add( group );
} );
Tween Function:
triggerTweens();
function triggerTweens() {
new TWEEN.Tween( title.position ).to( { x: 10 }, 5000 ).start();
}
I have found the following solution to this. Essentially adding the tween trigger into the loader function and passing the variable mesh, ( not group ) - allows tweening on the constituent paths and polygons etc of the SVG. To take this a step further I will figure out how to perform the tween for each separately which shouldn't be too tricky at this point.
New SVG Loader:
var loader = new THREE.SVGLoader();
loader.load( 'svg/test.svg', function ( paths ) {
var group = new THREE.Group();
group.scale.multiplyScalar( 0.25 );
group.position.x = -150;
group.position.y = 70;
group.scale.y *= - 1;
for ( var i = 0; i < paths.length; i ++ ) {
var path = paths[ i ];
var material = new THREE.MeshBasicMaterial( {
// color: path.color,
color: 0xff0000,
side: THREE.DoubleSide,
depthWrite: false
} );
var shapes = path.toShapes( true );
for ( var j = 0; j < shapes.length; j ++ ) {
var shape = shapes[ j ];
var geometry = new THREE.ExtrudeGeometry(shape, {
depth: 1,
bevelEnabled: false
});
var mesh = new THREE.Mesh( geometry, material );
// Randomly position each constituent mesh to demo
randX = Math.random() * (20000 - -20000) + -20000;
randY = Math.random() * (20000 - -20000) + -20000;
randZ = Math.random() * (20000 - -20000) + -20000;
mesh.position.set( randX, randY, randZ );
group.add( mesh );
}
triggerTweens(mesh); // Trigger tweens passing mesh variable
}
scene.add( group );
} );
New Tween Function:
// Bring all meshes back to position 0,0,0 Over 5000ms
function triggerTweens(mesh) {
new TWEEN.Tween( mesh.position ).to( { x: 0, y: 0, z: 0 }, 5000 ).easing(TWEEN.Easing.Cubic.In).start();
}

Three JS SVG Renderer with grid and transparent surfaces

I am trying to render SVG Files with threeJS. As PNG files don't look pretty nice I am using now the SVGRenderer.
My SVG Files looks sth like this:
When I render the Image it appears to be completely black (the colour I have chosen though)
I found the option to render it as a wireframe, but that shows some framelines I don't want to see
My JS looks like this:
var loader = new THREE.SVGLoader();
loader.load( 'media/qpartdark/SVG/qpart2.svg', function ( paths ) {
var group = new THREE.Group();
group.scale.multiplyScalar( 0.25 );
group.position.x = - 70;
group.position.y = 0;
group.position.z = 0;
group.scale.y *= -1;
for ( var i = 0; i < paths.length; i ++ ) {
var path = paths[ i ];
var material = new THREE.MeshBasicMaterial( {
color: path.color,
side: THREE.DoubleSide,
depthWrite: false,
wireframe: false,
wireframeLinewidth: 0.01
} );
var shapes = path.toShapes( true );
for ( var j = 0; j < shapes.length; j ++ ) {
var shape = shapes[ j ];
var geometry = new THREE.ShapeBufferGeometry( shape );
var mesh = new THREE.Mesh( geometry, material );
group.add( mesh );
}
}
scene.add( group );
} );
//
renderer = new THREE.WebGLRenderer( { alpha: true, antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
Do you have any hints how I can render the svg as in the example?
here is the original SVG:

Three.js shooting bullet

I'm a beginner in three.js. My task is to build a simple FPS game. I'm having many troubles with the gun and the bullets. When I press "spacebar" my weapon shoots but the problem is that the bullets go in the right direction only for a small part of the screen then they start to go in direction that are not the ones I want.
This is an example :
image1
image2
Here is the code I wrote for the bullet :
// SHOOT BULLET
for(var index=0; index<bullets.length; index+=1){
if( bullets[index] === undefined ) continue;
if( bullets[index].alive == false ){
bullets.splice(index,1);
continue;
}
bullets[index].position.add(bullets[index].velocity);
}
if(keyboard[32] && canShoot <= 0){ // spacebar key
// creates a bullet as a Mesh object
var bullet = new THREE.Mesh(
new THREE.SphereGeometry(0.2,8,8),
new THREE.MeshBasicMaterial({color:0x42FFFF})
);
// position the bullet to come from the player's weapon
bullet.position.set(
camera.position.x - 0.7*parseInt(-Math.cos(camera.rotation.z)),
camera.position.y - 0.3,
camera.position.z +1*parseInt(-Math.cos(camera.rotation.z))
);
// set the velocity of the bullet
bullet.velocity = new THREE.Vector3( (-mouse.x - Math.sin(camera.rotation.y + Math.PI/6) * 7),//*parseInt(-Math.cos(camera.rotation.z)) ,
mouse.y,
Math.cos(camera.rotation.y)*parseInt(-Math.cos(camera.rotation.z))
).normalize();
console.info(bullet.velocity);
// after 1000ms, set alive to false and remove from scene
// setting alive to false flags our update code to remove
// the bullet from the bullets array
bullet.alive = true;
setTimeout(function(){
bullet.alive = false;
scene.remove(bullet);
}, 1000);
// add to scene, array, and set the delay to 10 frames
bullets.push(bullet);
scene.add(bullet);
canShoot = 10;
}
if(canShoot > 0) canShoot -= 1;
A rough concept of how you can set direction and movement of bullets:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 10000);
camera.position.set(0, 0, 1);
scene.add(camera);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var background = new THREE.Mesh(new THREE.SphereGeometry(1000, 90, 45), new THREE.MeshBasicMaterial({
color: "gray",
wireframe: true
}));
scene.add(background);
var weapon = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 5), new THREE.MeshBasicMaterial({
color: 0x5555ff
}));
weapon.position.set(2, -1, -2.5);
camera.add(weapon);
var emitter = new THREE.Object3D();
emitter.position.set(2, -1, -5);
camera.add(emitter);
var plasmaBalls = [];
window.addEventListener("mousedown", onMouseDown);
function onMouseDown() {
let plasmaBall = new THREE.Mesh(new THREE.SphereGeometry(0.5, 8, 4), new THREE.MeshBasicMaterial({
color: "aqua"
}));
plasmaBall.position.copy(emitter.getWorldPosition()); // start position - the tip of the weapon
plasmaBall.quaternion.copy(camera.quaternion); // apply camera's quaternion
scene.add(plasmaBall);
plasmaBalls.push(plasmaBall);
}
var speed = 50;
var clock = new THREE.Clock();
var delta = 0;
(function render() {
requestAnimationFrame(render);
delta = clock.getDelta();
plasmaBalls.forEach(b => {
b.translateZ(-speed * delta); // move along the local z-axis
});
renderer.render(scene, camera);
})()
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.115.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115.0/examples/js/controls/OrbitControls.js"></script>

.svg asset in three.js 3d space

I'm trying to load a .svg asset into my three.js scene, as a flat vector layer; I found this example with SVGLoader and SVGRenderer from another post, but I can't make it work.
The svg loaded is stuck in 2d space and not responding to camera movement, I can't access its position.
I tried to switch to WebGLRenderer, but the svg doesn’t get loaded.
The option of loading it as sprite would be good, but I would want the sprite to not face the camera and stay still in 3d space.
var svgManager = new THREE.SVGLoader();
var url = 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Europe_laea_location_map.svg';
function svg_loading_done_callback(doc) {
init();
svg(new THREE.SVGObject(doc));
ico();
animate();
};
svgManager.load(url,
svg_loading_done_callback,
function() {
console.log("Loading SVG...");
},
function() {
console.log("Error loading SVG!");
});
var AMOUNT = 100;
var container, camera, scene, renderer;
function init() {
scene = new THREE.Scene();
renderer = new THREE.SVGRenderer();
renderer.setClearColor(0x00ff00);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
var container = document.getElementById('container');
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 1100;
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableZoom = true;
window.addEventListener('resize', onWindowResize, false);
}
function svg(svgObject) {
svgObject.position.x = 510;
svgObject.position.y = -110;
svgObject.position.z = 0;
scene.add(svgObject);
}
function ico() {
geometry = new THREE.IcosahedronGeometry(100, 1)
material = new THREE.MeshNormalMaterial({});
ico = new THREE.Mesh(geometry, material);
ico.position.y = -300;
scene.add(ico);
ico2 = new THREE.Mesh(geometry, material);
ico2.position.y = 500;
ico2.position.x = -500;
ico2.position.z = -50;
scene.add(ico2);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
controls.update;
render();
}
function render() {
renderer.render(scene, camera);
}
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%
}
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<div id="container"></div>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/renderers/SVGRenderer.js"></script>
<script src="https://threejs.org/examples/js/renderers/Projector.js"></script>
<script src="https://threejs.org/examples/js/loaders/SVGLoader.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
The SVGLoader and SVGRenderer are two different things. The first loads an SVG file and converts it to three.js shapes (albeit with some limitations, i.e. can read very simple SVGs, does not render strokes but only filled objects, etc), while the latter renders three.js primitives using SVG elements instead of WebGL. In a sense, they are opposites of each other.
So, first of all, you'd need to use the WebGLRenderer for your case.
Then, you need to change the SVG loading callback. It receives an array of paths with which you can render the SVG.
See the changes in functions svg_loading_done_callback, init and svg, and run it in JSFiddle:
var svgManager = new THREE.SVGLoader();
var url = 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Europe_laea_location_map.svg';
function svg_loading_done_callback(paths) {
init();
svg(paths);
ico();
animate();
};
svgManager.load(url,
svg_loading_done_callback,
function() {
console.log("Loading SVG...");
},
function() {
console.log("Error loading SVG!");
});
var AMOUNT = 100;
var container, camera, scene, renderer;
function init() {
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x00ff00);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
var container = document.getElementById('container');
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 1100;
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableZoom = true;
window.addEventListener('resize', onWindowResize, false);
}
function svg(paths) {
var group = new THREE.Group();
group.position.x = 510;
group.position.y = -110;
group.position.z = 0;
for ( var i = 0; i < paths.length; i ++ ) {
var path = paths[ i ];
var material = new THREE.MeshBasicMaterial( {
color: path.color,
side: THREE.DoubleSide,
depthWrite: false
} );
var shapes = path.toShapes( true );
for ( var j = 0; j < shapes.length; j ++ ) {
var shape = shapes[ j ];
var geometry = new THREE.ShapeBufferGeometry( shape );
var mesh = new THREE.Mesh( geometry, material );
group.add( mesh );
}
}
scene.add( group );
}
function ico() {
geometry = new THREE.IcosahedronGeometry(100, 1)
material = new THREE.MeshNormalMaterial({});
ico = new THREE.Mesh(geometry, material);
ico.position.y = -300;
scene.add(ico);
ico2 = new THREE.Mesh(geometry, material);
ico2.position.y = 500;
ico2.position.x = -500;
ico2.position.z = -50;
scene.add(ico2);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
controls.update;
render();
}
function render() {
renderer.render(scene, camera);
}
PS: Check the SVG Loader to see what it's able to parse

Resources