Three.js Applying Repeated Texture to JSON Scene Object - three.js

I have a basic json scene exported from the three.js/editor. I want to add a repeated texture wrap to an object in this scene but I do not know how to do so since the only examples I have found add the wrap in the creation of the object.
I have already tried accessing the texture and giving it a wrap, but I think I might need to add a texture to the object from the JavaScript then declare the texture wrap instead of trying to add it to an already loaded texture.
<script>
var camera, scene, renderer;
var mesh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 400;
scene = new THREE.Scene();
var objectLoader = new THREE.ObjectLoader();
objectLoader.load( "models/cube.json", function ( obj ) {
scene.add( obj )
obj.traverse(function(child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
obj.name = "cube";
obj.position.set(0,0,0);
obj.scale.set(200,200,200);
});
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
while (scene.getObjectByName('Box 1')){
var texture = scene.getObjectByName('Box 1').material;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 2, 2 );
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
if (scene.getObjectByName('Box 1') ){
scene.getObjectByName('Box 1').material.map.offset.x += 0.001;
}
renderer.render( scene, camera );
}
</script>

You need to set the wrap and repeat properties of the texture/map and not of the material. Docs also say it's important to set needsUpdate to true, if wrap settings changed. And you are trying to set the properties before the json is loaded (in a while loop?). You should do it within the load callback.
objectLoader.load( "models/cube.json", function ( obj ) {
scene.add( obj )
obj.traverse(function(child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
obj.name = "cube";
obj.position.set(0,0,0);
obj.scale.set(200,200,200);
var box1 = scene.getObjectByName('Box 1');
if (box1) {
var texture = box1.material.map;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 2, 2 );
texture.needsUpdate = true;
}
});

Related

How to access the any property of imported point-cloud object

I’m trying to access any property of my imported point-cloud model.
When I console.log the model within the function loader.load it works. The first problem is that I don’t know how to get the same result outside the function. I did it only with the last three lines of code but initializing the object with different name.
I used the code for point-cloud models from three.js docs and everything works fine except when I want to change the size of the points. I guess it is due to the different structure of my model (photo attached).
Here’s the code that I use.
import './style.css'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { PCDLoader } from 'three/examples/jsm/loaders/PCDLoader'
import { BufferAttribute, BufferGeometry } from 'three';
let camera, scene, renderer;
init();
render();
function init() {
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 0.01, 40 );
camera.position.set( 0, 0, 7 );
scene.add( camera );
const controls = new OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // use if there is no animation loop
controls.minDistance = 0.5;
controls.maxDistance = 10;
//scene.add( new THREE.AxesHelper( 1 ) );
const loader = new PCDLoader();
loader.load( 'pointcloud.pcd', function ( points ) {
points.geometry.center();
points.geometry.rotateY( Math.PI );
points.geometry.rotateX( Math.PI );
points.geometry.rotateZ( Math.PI );
scene.add( points );
console.log(points)
render();
} );
window.addEventListener( 'resize', onWindowResize );
window.addEventListener( 'keypress', keyboard );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function keyboard( ev ) {
const points = scene.getObjectByProperty( 'pointcloud.pcd' );
switch ( ev.key || String.fromCharCode( ev.keyCode || ev.charCode ) ) {
case '+':
points.material.size *= 1.2;
break;
case '-':
points.material.size /= 1.2;
break;
}
render();
}
function render() {
renderer.render( scene, camera );
}
// I used this to acces the object
const points2 = scene.getObjectByProperty( 'points' );
console.log(points2)
enter image description here
change let camera, scene, renderer; to let camera, scene, renderer, points
to make 'points' global.
because the 'points' object only live inside the function, try create a variable outside the loader.load function, then assign the 'points' object to the new variable before render(); now you can access the 'points' from outside this function

Improve Three.js performance with SSR (Node,js next,js)

I'm trying to find best solution to improve my website with three.js performance.
I would like to ask someone who have tried three.js with Server Side Rendering and if the
performance improve.
My three.js project include 5 glb files in the scene. Each file is about 1MB.
So far I improved.
Speed Index First
Meaningful Paint
by using webpack
** minify and include all javascript into one file.
** use next generation image wep for all textures.
** able gzip to my sever
Also, to improve loading speed instead of load all 5 glb models at the beginning,
load it 1 or two first, then load rest of models when user needed.
To reduce fan sound( the CPU cause)
I did animate render only user interact
window.addEventListener("wheel", function(e) {
animate();
}, true);
var scene, renderer, camera, model, mixer;
function init() {
var container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 1, 2, - 3 );
camera.lookAt( 0, 1, 0 );
clock = new THREE.Clock();
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xa0a0a0 );
scene.fog = new THREE.Fog( 0xa0a0a0, 10, 50 );
var hemiLight = new THREE.HemisphereLight( 0xffffff, 'rgb(100,100,100)' );
hemiLight.position.set( 0, 20, 0 );
scene.add( hemiLight );
var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0x999999, depthWrite: false } ) );
mesh.rotation.x = - Math.PI / 2;
mesh.receiveShadow = true;
scene.add( mesh );
var grid = new THREE.GridHelper( 2000, 20, 0x000000, 0x000000 );
grid.material.opacity = 0.2;
grid.material.transparent = true;
scene.add( grid );
var textload = new THREE.TextureLoader();
var text = textload.load( require('./material/text.jpg') );
//text.jpg = 80kb;
text.flipY = false;
var textloadbbump = new THREE.TextureLoader();
var textbump = textloadbbump.load( require('./material/textbump.jpg') );
//textbump.jpg = 50kb;
textbump.flipY = false;
var loader = new GLTFLoader();
//character.glb is 1.1MB
loader.load( require('./material/character.glb'), function ( gltf ) {
model = gltf.scene;
model.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material.roughness = 1;
if(child.name=='body'){
child.material.map = text;
child.material.bumpMap = textbump;
child.material.bumpScale = 0.001;
}
}});
scene.add( model );
mixer = new THREE.AnimationMixer( model );
mixer.clipAction( gltf.animations[ 0 ] ).play();
});
renderer = new THREE.WebGLRenderer( {
// antialias: true
powerPreference: 'low-power',
//precision: 'lowp'
} );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = false;
container.appendChild( renderer.domElement );
}
window.onresize = function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
};
init();
window.addEventListener("wheel", function(e) {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}, true);

How to animate a Collada model's children in three.js?

I'm trying to find a way to animate the children in a Collada model of the Canadarm2. The model contains 7 arm segments whose angles need to be set by reading in rotation angle data from each of 7 spreadsheet columns one row at a time. Each line holds 7 columns of data representing each second's angles in an hour, or longer, mission.
I'm hoping someone might suggest a solution to get me started -- perhaps something like the guesswork I've tried inside the animate() function below.
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var container, stats, clock;
var camera, scene, renderer, canadarm2;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 2000 );
camera.position.set( 8, 10, 80 );
camera.lookAt( new THREE.Vector3( 0, 3, 0 ) );
scene = new THREE.Scene();
clock = new THREE.Clock();
// loading manager
var loadingManager = new THREE.LoadingManager( function() {
scene.add( canadarm2 );
} );
// collada
var loader = new THREE.ColladaLoader( loadingManager );
//loader.load( 'examples/models/collada/elf/elf.dae', function ( collada ) {
loader.load( 'canadarm2.dae', function ( collada ) {
canadarm2 = collada.scene;
} );
//
var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
scene.add( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
directionalLight.position.set( 1, 1, 0 ).normalize();
scene.add( directionalLight );
//
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
//
stats = new Stats();
container.appendChild( stats.dom );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
/*
//#######################################
// doesn't work
var armEP = canadarm2.getChildByName("EP", true);// EP is one of 7 arms
armEP.position.x += 0.01;
armEP.rotation.y += 45;
//#######################################
*/
render();
stats.update();
}
function render() {
var delta = clock.getDelta();
if ( canadarm2 !== undefined ) {
canadarm2.rotation.z += delta * 0.5;
}
renderer.render( scene, camera );
}
This seems to work although it seems an awfully roundabout way to locate the arms.
scene.traverse(function (child) {
switch(child.name) {
case "SR":
child.rotation.x += 0.01;
break;
case "SP":
child.rotation.y += 0.01;
break;
case "SY":
child.rotation.y += 0.01;
break;
case "EP":
child.rotation.y += 0.01;
break;
case "WP":
child.rotation.x += 0.01;
break;
case "WY":
child.rotation.y += 0.01;
break;
case "WR":
child.rotation.x += 0.01;
break;
}
});

Three.js ray object intersection

friends.
Here is my code. It should find intersection of ray and cube. But it does not work and makes me mad. This code is quite simple, but it is hard for me to find the error. Please, help.
jsFiddle
<script>
var container;
var camera, controls, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 1000;
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
// world
scene = new THREE.Scene();
var testObject_G = new THREE.CubeGeometry(100, 100, 100);
var testObject_M = new THREE.MeshBasicMaterial({ color: 0xBBBBBB });
var testObject_Mesh = new THREE.Mesh(testObject_G, testObject_M);
testObject_Mesh.position.x = 300;
scene.add(testObject_Mesh);
scene2 = new THREE.Object3D();
// rays
var direction = new THREE.Vector3(1, 0, 0);
direction.normalize();
var startPoint = new THREE.Vector3(0, 0, 0);
var ray = new THREE.Raycaster(startPoint, direction);
var rayIntersects = ray.intersectObjects(scene.children, true);
if (rayIntersects[0]) {
console.log(rayIntersects[0]);
var geometry = new THREE.CubeGeometry(10, 10, 10);
var material = new THREE.MeshBasicMaterial({color: 0xff0000});
var cube = new THREE.Mesh(geometry, material);
cube.position = rayIntersects[0].point;
scene2.add(cube);
}
var ray_G = new THREE.Geometry();
ray_G.vertices.push(new THREE.Vector3(0, 0, 0));
ray_G.vertices.push(direction.multiplyScalar(1000));
var ray_M = new THREE.LineBasicMaterial({ color: 0x000000 });
var ray_Mesh = new THREE.Line(ray_G, ray_M);
scene2.add(ray_Mesh);
scene.add(scene2);
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0xffffff, 1);
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
controls.handleResize();
render();
}
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
renderer.render( scene, camera );
}
</script>
</body>
Thank you very much for replies.
During the render loop, three.js updates each object's transform matrix for you, based on your specified object.position, object.rotation/quaternion, and object.scale.
Since you are calling Raycaster.intersectObjects() before the first render call, you have to update the object matrices yourself prior to raycasting.
scene.updateMatrixWorld();
fiddle: http://jsfiddle.net/mzRtJ/5/
three.js r.64

Three.js CanvasRenderer Artifacts

I am trying to load a collada file using Threejs. All works well, but if I make the object spin I can see that the rendering is not right. Check the image:
Here is the code (which was partially stolen from another example):
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'Drag to spin the cube';
container.appendChild( info );
camera = new THREE.PerspectiveCamera( 20, window.innerWidth / window.innerHeight, .1, 10000 );
camera.position.x=50;
camera.position.y=50;
camera.position.z=50;
camera.lookAt(new THREE.Vector3(0,0,0));
scene = new THREE.Scene();
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
var ambientLight = new THREE.AmbientLight(0x000000);
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(100,50,80).normalize();
scene.add(directionalLight);
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true; // this rotates so it looks right
loader.load('models/VM.dae', function (result) {
cube = result.scene;
// cube.doubleSided = true;
// cube.flipSided = true;
console.log(cube);
cube.updateMatrix();
scene.add(result.scene);
animate();
});
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
cube.rotation.y += 0.01;
// plane.rotation.y = cube.rotation.y += ( targetRotation - cube.rotation.y ) * 0.05;
renderer.render( scene, camera );
}
New update:
I changed the loader to a MTL+OBJ one. The result is exactly the same:
var loader = new THREE.OBJMTLLoader();
loader.addEventListener("load", function (event) {
cube = event.content;
cube.doubleSided = true;
console.log(event);
scene.add(cube);
animate();
});
loader.load ("models/VM.obj", "models/VM.mtl");
Fiddle: http://jsfiddle.net/qMqH7/
Your model is throwing errors in the Console, I would suggest you track them down.
What you are seeing is a known limitation of CanvasRenderer related to depth-sorting. It is made worse by your geometry which has several elongated faces. The model renders correctly with WebGLRenderer.
Also, object.doubleSided has been deprecated. It has been replaced by material.side = THREE.DoubleSide. It does not appear that flag is required in this case.
three.js r.58

Resources