three.js bone control/ visualization and skeleton animation - three.js

How to visualize the skeleton correctly? I feel like I did it right, but somehow the skeleton is not shown.
How do I specify the bone position/rotation in x,y,z axis in separate guiControls for each bone in GUI, such as "Bone_0.x" instead of "Bone_0"? Right now each 'Bone_0' is controlling both rotation and position... Or is there a way to control the bones by clicking on the bone, and use key input to translate/rotate, such as 'g'+'x' to translate the bone in x axis?
Thanks in advance!
//load file
let mixer;
const loader = new THREE.GLTFLoader();
loader.load('js/simple.gltf', function (gltf) {
scene.add(gltf.scene);
const model = gltf.scene;
mixer = new THREE.AnimationMixer(model);
gltf.animations.forEach((clip) => {
mixer.clipAction(clip).play();
});
helper = new THREE.SkeletonHelper(model);
helper.material.linewidth = 3;
helper.visible = true;
scene.add(helper);
////GUI
guiControls = new function () {
this.Bone_0 = 0.0;
this.Bone_1 = 0.0;
}
datGUI = new dat.GUI();
//datGUI.add(guiControls, "scene");
var folder = datGUI.addFolder('Controls');
folder.add(guiControls, 'Bone_0', -3.14, 3.14);
folder.add(guiControls, 'Bone_0', -3.14, 3.14);
folder.add(guiControls, 'Bone_1', -3.14, 3.14);
folder.add(guiControls, 'Bone_1', -3.14, 3.14);
});
//RENDER LOOP
render();
function render() {
controls.update();
var delta = 0.75 * clock.getDelta();
if (mixer) {
mixer.update(delta);
}
scene.traverse(function (child) {
if (child instanceof THREE.SkinnedMesh) {
child.position.y += .01;
child.skeleton.bones[0].position.y = guiControls.Bone_0;
child.skeleton.bones[0].rotation.y = guiControls.Bone_0;
child.skeleton.bones[1].position.y = guiControls.Bone_1;
child.skeleton.bones[1].rotation.y = guiControls.Bone_1;
}
});
renderer.render(scene, camera);
requestAnimationFrame(render);
};

Something wrong in the model, the code is correct.
Use Bone_0_x to specify axis, such as folder.add(guiControls, 'Bone_0_x', -3.14, 3.14);
Or:
var datGUI = new dat.GUI();
var folder = datGUI.addFolder('Controls');
var bones = skeleton.bones;
for ( var i = 0; i < bones.length; i++ ) {
folder.add( bones[i].position, 'x', 0, Math.PI * 2 ).name( 'bones[' + i + '].position.x' );
folder.add( bones[i].position, 'y', 0, Math.PI * 2 ).name( 'bones[' + i + '].position.y' );
// ...
}
https://discourse.threejs.org/t/bone-properties/4076

Related

GLB animation in three.js is too fast

I have uploaded a glb file with an animation, and the animation is moving extremely fast, and I do not know why.
This is my character's animation code:
class MainChar extends THREE.Object3D {
constructor() {
super();
this.object = new THREE.Object3D();
this.object.position.set(0, 1, 50);
this.object.scale.x=20;
this.object.scale.y=20;
this.object.scale.z=20;
//load house model form blender file
/*
loader.setPath('../../models/characters/');
const gltf = loader.load('Douglas.glb', (gltf) => {
gltf.scene.traverse(c => {
c.castShadow = true;
});
this.object.add( gltf.scene);
});
*/
const loader = new THREE.GLTFLoader();
loader.setPath('../../models/characters/');
const gltf = loader.load('walk.glb', (gltf) => {
gltf.scene.traverse(c => {
c.castShadow = true;
});
this.mixer = new THREE.AnimationMixer( gltf.scene );
this.mixer.timeScale=1/5;
var action = this.mixer.clipAction( gltf.animations[ 0 ] );
action.play();
this.object.add( gltf.scene );
});
//save keyboard bindings
this.keyboard = new THREEx.KeyboardState();
/*
//creating a box (need to change it to a character with animations)
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
this.object = new THREE.Mesh( geometry, material );
this.object.scale.x=5;
this.object.scale.y=10;
this.object.scale.z=5;
//starting position for character
this.object.position.set(0, 10, 50);
*/
this.update = function (time) {
if ( this.mixer ){
this.mixer.update( time );
console.log(time);
}
//MOVEMENT OF BOX
//speed
var moveDistance = 0.5 ;
// var rotateAngle = Math.PI / 2 * 0.05;
// move forwards/backwards/left/right
if ( this.keyboard.pressed("W") ){
this.object.translateZ( -moveDistance );
}
if ( this.keyboard.pressed("S") ){
this.object.translateZ( moveDistance );
}
if ( this.keyboard.pressed("A") ){
this.object.translateX( -moveDistance );
}
if ( this.keyboard.pressed("D") ){
this.object.translateX( moveDistance );
}
// move forwards/backwards/left/right
if ( this.keyboard.pressed("up") ){
this.object.translateZ( -moveDistance );
}
if ( this.keyboard.pressed("down") ){
this.object.translateZ( moveDistance );
}
if ( this.keyboard.pressed("left") ){
this.object.translateX( -moveDistance );
}
if ( this.keyboard.pressed("right") ){
this.object.translateX( moveDistance );
}
// FOR CAMERA ROTATIONS
//this.object.rotateOnAxis( new THREE.Vector3(0,1,0), -rotateAngle);
//this.object.rotateOnAxis( new THREE.Vector3(0,1,0), rotateAngle);
//var rotation_matrix = new THREE.Matrix4().identity();
if ( this.keyboard.pressed("Z") )
{
this.object.position.set(0, 1, 50);
this.object.rotation.set(0,0,0);
}
/*
// global coordinates
if ( this.keyboard.pressed("left") )
this.object.position.x -= moveDistance;
if ( this.keyboard.pressed("right") )
this.object.position.x += moveDistance;
if ( this.keyboard.pressed("up") )
this.object.position.z -= moveDistance;
if ( this.keyboard.pressed("down") )
this.object.position.z += moveDistance;
*/
};
}
}
This is the time class that allows the game to be paused, as well as returns the delta time:
class Time {
constructor(){
this.is_pause = false;
this.accumalated_run_time = 0;
this.clock = new THREE.Clock();
this.pause_clock = new THREE.Clock();
}
getRunTime()
{
this.accumalated_run_time += this.clock.getDelta();
return this.accumalated_run_time
}
pause()
{
this.is_pause = true;
}
unpause()
{
this.is_pause = false;
this.clock.getDelta();
}
}
This is the sceneManager that calls up my character for updating animations:
class SceneManager {
constructor(canvas) {
//this entire function renders a scene where you can add as many items as you want to it (e.g. we can create the house and add as
//many items as we want to the house). It renders objects from other javascript files
//------------------------------------------------------------------------------------------------------------------------------------------
//These are supposed to act like constants. DO NOT CHANGE
this.GAME_PAUSE = "pause";
this.GAME_RUN = "run";
//------------------------------------------------------------------------------------------------------------------------------------------
//we use (this) to make variables accessible in other classes
this.time = new Time();
this.game_state = this.GAME_RUN;
this.screenDimensions = {
width: canvas.width,
height: canvas.height
};
//the essentials for rendering a scene
this.scene = this.buildScene();
this.renderer = this.buildRender(this.screenDimensions);
this.camera = this.buildCamera(this.screenDimensions);
this.managers = this.createManagers();
this.loadToScene(this.managers[0].entities);
//allow camera to orbit target (player)
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.target.set(0, 20, 0);
this.controls.update();
}
loadToScene(entities)
{
for (let i = 0 ; i < entities.length ; i++)
{
console.log("before" +i.toString());
this.scene.add(entities[i].object);
console.log("after");
}
}
//this function creates our scene
buildScene() {
//create a new scene
const scene = new THREE.Scene();
//set the scene's background-> in this case it is our skybox
const loader = new THREE.CubeTextureLoader();
//it uses different textures per face of cube
const texture = loader.load([
'../skybox/House/posx.jpg',
'../skybox/House/negx.jpg',
'../skybox/House/posy.jpg',
'../skybox/House/negy.jpg',
'../skybox/House/posz.jpg',
'../skybox/House/negz.jpg'
]);
scene.background = texture;
//if we wanted it to be a colour, it would have been this commented code:
//scene.background = new THREE.Color("#000");
return scene;
}
//this creates a renderer for us
buildRender({ width, height }) {
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true, alpha: true
});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
return renderer;
}
//create a camera for the screen
buildCamera({ width, height }) {
//SETTING FIELD OF VIEW, ASPECT RATIO (which should generally be width/ height), NEAR AND FAR (anything outside near/ far is clipped)
const aspectRatio = width / height;
const fieldOfView = 60;
const nearPlane = 1;
const farPlane = 1000;
//there are 2 types of cameras: orthographic and perspective- we will use perspective (more realistic)
const camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, nearPlane, farPlane);
//set where the camera is
camera.position.set(-50, 50, 70);
return camera;
}
//add subjects to the scene
createManagers() {
const managers=[new EntityManager()];
//can be altered so we can add multiple entities, and depending on which position
//it is, certain ones won't be paused, and some will be
managers[0].register(new GeneralLights());
managers[0].register(new House());
managers[0].register(new MainChar());
managers[0].register(new SceneSubject())
return managers;
}
//this updates the subject/model every frame
update() {
//won't call this loop if it's paused-> only for objects that need to be paused (managers that need to be paused)
if (this.game_state == this.GAME_RUN)
{
const runTime = this.time.getRunTime();
this.managers[0].update(runTime);
}
//update orbit controls
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
//this resizes our game when screen size changed
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
pause(){ //when pause mode is entered. The pause menu needs to be rendered.
this.game_state = this.GAME_PAUSE;
this.time.pause();
}
unpause(){
this.game_state = this.GAME_RUN;
this.time.unpause();
}
}
I think the issue is with your AnimationMixer.update() call. If you look at the docs, update is expecting a time-delta in seconds, but it looks like you're passing the total running time. This means it should receive the time passed since the last frame. You can fix this by using clock.getDelta(); as the argument:
this.update = function (time) {
if ( this.mixer ){
const delta = this.clock.getDelta();
this.mixer.update(delta);
}
// ...
}

Draw a 2D line with width in three.js

I'm looking to draw a continuous line with a given thickness showing only the edges using three.js. I have achieved it. I'm trying to add thickness to the line but it is not getting reflected in the scene due to some angle in three.js. Can anyone help me out with the issue.
Here's the fiddle https://jsfiddle.net/16vhjm0y/1/
var renderer, scene, camera;
var line;
var count = 0;
var mouse = new THREE.Vector3();
var mesh3D;
var maxPoint = 6;
var height = window.innerHeight * .99;
var plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); // facing us for mouse intersection
var raycaster = new THREE.Raycaster();
var point3ds = [];
var usePerspectiveCamera = false; // toggles back and forth
var perspOrbit;
var perspCam;
var orthoOrbit;
var orthoCam;
var labelRenderer, labelAjay;
var testBoolean = false;
var mouseDownBoolean = false;
var distanceData, showDistanceData;
var ajay;
var arrAjay = [];
var arrAjayFinal = [];
var mouseUpBoolean = false;
init();
animate();
function init() {
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, height);
document.body.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
// camera perspective
perspCam = new THREE.PerspectiveCamera(45, window.innerWidth / height, 1, 10000);
perspCam.position.set(0, 0, 200);
// camera ortho
var width = window.innerWidth;
//var height = window.innerHeight;
orthoCam = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, 0, 1200);
// assign cam
camera = perspCam;
someMaterial = new THREE.MeshBasicMaterial({ color: 0xA9A9A9, side: THREE.DoubleSide, transparent: true, opacity: 0.3 });
// grid
var grid = new THREE.GridHelper(1024, 56);
grid.rotateX(Math.PI / 2);
// scene.add(grid);
// geometry
var geometry = new THREE.BufferGeometry();
var MAX_POINTS = 500;
positions = new Float32Array(MAX_POINTS * 3);
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
// material
var material = new THREE.LineBasicMaterial({
color: 0xff0000,
linewidth: 10
});
// line
line = new THREE.Line(geometry, material);
// line.position.z = 20;
scene.add(line);
// var geometry = new THREE.BoxBufferGeometry( 10, 2, 20 );
// var edgesPavement = new THREE.EdgesGeometry( geomPavement );
// var lineGeometry = new THREE.LineSegmentsGeometry().setPositions( edgesPavement.attributes.position.array );
// var lineMaterial = new THREE.LineMaterial( { color: 0xff0000, linewidth: 10 } );
// lineMaterial.resolution.set( window.innerWidth, window.innerHeight ); // important, for now...
// var line = new THREE.LineSegments2( lineGeometry, lineMaterial );
// scene.add( line );
document.addEventListener("mousemove", onMouseMove, false);
document.addEventListener('mousedown', onMouseDown, false);
document.addEventListener('mouseup', onMouseUp, false);
createUI();
labelRenderer = new THREE.CSS2DRenderer();
ajay = document.createElement('div');
ajay.className = 'ajay';
ajay.style.color = "black";
ajayInsert = document.createElement('div');
ajayInsert.className = 'ajay';
ajayInsert.style.color = "black";
// ajay.style.color = "black";
// console.log(ajay)
labelAjay = new THREE.CSS2DObject(ajay);
labelAjayFinal = new THREE.CSS2DObject(ajayInsert);
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0';
labelRenderer.domElement.style.pointerEvents = 'none';
ajay.style.display = "none";
ajayInsert.style.display = "none";
}
// update line
function updateLine() {
positions[count * 3 - 3] = mouse.x;
positions[count * 3 - 2] = mouse.y;
positions[count * 3 - 1] = mouse.z;
line.geometry.attributes.position.needsUpdate = true;
}
// mouse move handler
function onMouseMove(event) {
var rect = renderer.domElement.getBoundingClientRect();
mouse.x = (event.clientX - rect.left) / (rect.right - rect.left) * 2 - 1;
mouse.y = - ((event.clientY - rect.top) / (rect.bottom - rect.top)) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
mouse = raycaster.ray.intersectPlane(plane, mouse);
if (count !== 0 && count < maxPoint) {
updateLine();
}
testBoolean = true;
if (testBoolean == true) {
// scene.remove(labelAjay);
var geometry = line.geometry;
geometry.computeBoundingBox();
center = geometry.boundingBox.getCenter();
// line.localToWorld(center);
// console.log(center);
if (mouseDownBoolean == true) {
labelAjay.position.set(mouse.x, mouse.y, mouse.z);
// console.log(line.position)
scene.add(labelAjay);
document.body.appendChild(labelRenderer.domElement);
// console.log(positions);
distanceData = point3ds[0].distanceTo(new THREE.Vector3(mouse.x, mouse.y, mouse.z));
showDistanceData = Math.round(distanceData * 1000);
// console.log(point3ds[0]);
// console.log(point3ds[1]);
// console.log(distanceData);
// console.log(showDistanceData)
ajay.textContent = showDistanceData + ' mm';
// console.log(labelRenderer)
}
// console.log(labelRenderer.domElement)
// document.getElementsByClassName("ajay").remove();
// document.getElementsByClassName("ajay").outerHTML = "";
}
}
// add point
function addPoint(event) {
if (count < maxPoint) {
console.log("point nr " + count + ": " + mouse.x + " " + mouse.y + " " + mouse.z);
positions[count * 3 + 0] = mouse.x;
positions[count * 3 + 1] = mouse.y;
positions[count * 3 + 2] = mouse.z
count++;
line.geometry.setDrawRange(0, count);
updateLine();
point3ds.push(new THREE.Vector3(mouse.x, mouse.y, mouse.z));
} else {
console.log('max points reached: ' + maxPoint);
}
}
function getPointInBetweenByLen(pointA, pointB, length) {
var dir = pointB.clone().sub(pointA).normalize().multiplyScalar(length);
return pointA.clone().add(dir);
}
// mouse down handler
function onMouseDown(evt) {
mouseDownBoolean = true;
// force add an extra point on first click so buffer line can display
// buffer geometry requires two points to display, so first click should add two points
if (count === 0) {
addPoint();
}
if (count < maxPoint) {
addPoint();
}
}
function onMouseUp(event){
mouseUpBoolean = true;
if(mouseUpBoolean == true){
// showDistanceData = Math.round(distanceData * 1000);
arrAjay.push(showDistanceData);
console.log(arrAjay);
arrAjayFinal = arrAjay.splice(-1)[0];
var geometry = line.geometry;
geometry.computeBoundingBox();
center = geometry.boundingBox.getCenter();
if (mouseDownBoolean == true) {
labelAjayFinal.position.set(center.x, center.y, center.z);
scene.add(labelAjayFinal);
document.body.appendChild(labelRenderer.domElement);
// distanceData = point3ds[0].distanceTo(new THREE.Vector3(mouse.x, mouse.y, mouse.z));
// showDistanceData = Math.round(distanceData * 1000);
console.log('arrAjayFinal', arrAjayFinal);
ajayInsert.textContent = arrAjayFinal;
}
}
}
// render
function render() {
renderer.render(scene, camera);
labelRenderer.render(scene, camera);
}
// animate
function animate() {
requestAnimationFrame(animate);
render();
}
// loop through all the segments and create their 3D
function create3D() {
if (!mesh3D && point3ds && point3ds.length) {
console.log('creating 3D');
mesh3D = new THREE.Mesh(); // metpy mesh but is the root mesh for all 3D
scene.add(mesh3D);
// prepare create segments from point3ds - every two points create a segement
var index = 1;
var segmentHeight = 56;
point3ds.forEach(point3d => {
if (index < point3ds.length) {
var seg = new Segment(point3d, point3ds[index], someMaterial, segmentHeight);
mesh3D.add(seg.mesh3D);
index++;
}
});
}
}
function createUI() {
// create3D
var btn = document.createElement('button');
document.body.appendChild(btn);
btn.innerHTML = 'Create3D';
btn.addEventListener('mousedown', () => {
create3D();
// add orbiting controls to both cameras
var controls;
if (!perspOrbit) {
perspOrbit = new THREE.OrbitControls(perspCam, renderer.domElement);
perspOrbit.screenSpacePanning = true;
// raotation is enabled once create3D is pressed
setToFullOrbit(perspOrbit);
perspOrbit.enabled = true; // set to true by default
}
// add orbit to orthocam
if (!orthoOrbit) {
orthoOrbit = new THREE.OrbitControls(orthoCam, renderer.domElement);
orthoOrbit.screenSpacePanning = true;
orthoOrbit.enabled = false; // set to false by default
//orthoOrbit.enableDamping = true;
//orthoOrbit.dampingFactor = .15;
}
});
}
function switchCam() {
usePerspectiveCamera = !usePerspectiveCamera;
if (usePerspectiveCamera) {
if (perspCam) {
camera = perspCam;
perspOrbit.enabled = true;
orthoOrbit.enabled = false;
} else {
throw new Error('Switch to perspective cam failed, perspective cam is null');
}
} else {
if (orthoCam) {
camera = orthoCam;
orthoOrbit.enabled = true;
perspOrbit.enabled = false;
} else {
throw new Error('Switch to ortho cam failed, orthoCam is null');
}
}
}
function rotateCam90() {
if (camera instanceof THREE.OrthographicCamera) {
orthoOrbit.update();
camera.applyMatrix(new THREE.Matrix4().makeRotationZ(Math.PI / 2));
}
}
function reset() {
scene.remove(mesh3D);
mesh3D = null;
for (var i = 0; i < 3 * 8; i++) {
positions[i] = 0;
}
count = 0;
line.geometry.setDrawRange(0, count);
updateLine();
point3ds = [];
}
function setToFullOrbit(orbitControl) {
// how far you can orbit vertically
orbitControl.minPolarAngle = 0;
orbitControl.maxPolarAngle = Math.PI;
// How far you can dolly in and out ( PerspectiveCamera only )
orbitControl.minDistance = 0;
orbitControl.maxDistance = Infinity;
orbitControl.enableZoom = true; // Set to false to disable zooming
orbitControl.zoomSpeed = 1.0;
orbitControl.enableRotate = true;
// allow keyboard arrows
orbitControl.enableKeys = true;
// Set to false to disable panning (ie vertical and horizontal translations)
orbitControl.enablePan = true;
}
// each segment knows how to create its 3D
class Segment {
constructor(start, end, material, height) {
this.start = start;
this.end = end;
this.height = height; // height of the segment's 3D
this.material = material;
this.mesh3D = null;
this.create3D();
}
create3D() {
if (this.start && this.end) {
//create the shape geometry
var distStartToEnd = this.start.distanceTo(this.end);
var vec2s = [
new THREE.Vector2(),
new THREE.Vector2(0, this.height),
new THREE.Vector2(distStartToEnd, this.height),
new THREE.Vector2(distStartToEnd, 0)
];
console.log('vec2s', vec2s);
var shape = new THREE.Shape(vec2s);
var geo = new THREE.BoxGeometry(5, 5, 5);
// console.log('shape', shape);
var geo = new THREE.ShapeGeometry(shape);
geo.applyMatrix(new THREE.Matrix4().makeRotationX(THREE.Math.degToRad(90)));
this.mesh3D = new THREE.Mesh(geo, this.material);
this.alignRotation();
this.alignPosition();
// the mesh3D should be added to the scene outside of this class
}
}
alignRotation() {
var p1 = this.start.clone();
var p2 = this.end.clone();
var direction = new THREE.Vector3();
direction.subVectors(p2, p1);
direction.normalize();
this.mesh3D.quaternion.setFromUnitVectors(new THREE.Vector3(1, 0, 0), direction);
}
alignPosition() {
if (this.mesh3D) {
this.mesh3D.position.copy(this.start);
} else {
throw new Error('mesh3D null');
}
}
}
The linewidth parameter relies on native WebGL support for drawing line thickness, but its performance is very spotty across browsers & operating systems. I think Windows doesn't support it, but MacOS does, so it shouldn't be relied upon. See this discussion on the Three.js Github for several bug reports.
As a workaround, they've created LineGeometry, which sort of re-builds a line with geometry to allow for thickness. See this example for how to use it. It even allows for dashed lines. After importing the module, you can implement it with:
const geometry = new LineGeometry();
geometry.setPositions( positions );
geometry.setColors( colors );
matLine = new LineMaterial( {
color: 0xffffff,
linewidth: 5, // in pixels
vertexColors: true,
dashed: false
} );
line = new Line2( geometry, matLine );
line.computeLineDistances();
scene.add( line );

Why is OrbitControls not working as expected ? (Three.js)

I am using THREE.OrbitControls in my experimental project and have something very similar to this example.
I have different radio buttons at the top and I want to only enable THREE.OrbitControls if the rotate radio button is active.
I have replaced the code inside the if statement form the code Pen example:
function doMouseMove(x,y,evt,prevX,prevY) {
if (mouseAction == ROTATE) {
var dx = x - prevX;
world.rotateY( dx/200 );
render();
}
with:
function doMouseMove(x,y,evt,prevX,prevY) {
if (mouseAction == ROTATE) {
controls = new THREE.OrbitControls(camera, canvas);
controls.rotateSpeed = 0.1;
controls.zoomSpeed = 1;
controls.update();
}
This works perfectly, however once I go back from the rotate button to the drag button (or any other button), the OrbitControls is still active, and the camera moves with the object being dragged/added/removed.
This was not the case with the original example (as can be seen) and so I was wondering if I have to add further functionality to disable the OrbitControls.
I have tried:
controls.reset();
However, the orbitControls is still active even after the rotate radio button is not pressed!
I would like to add that the orbitControls is not active (as expected) when the page is reloaded on the drag button (or any other button). However once the rotate button has been pressed, it remains active throughout the session regardless of which input is pressed.
Any pointers on how I can solve this functionality?
The following is the full code outline from the example (excluding HTML file with references) of the code:
var canvas, scene, renderer, camera, controls;
var raycaster; // A THREE.Raycaster for user mouse input.
var ground; // A square base on which the cylinders stand.
var cylinder; // A cylinder that will be cloned to make the visible
cylinders.
var world;
var ROTATE = 1,
DRAG = 2,
ADD = 3,
DELETE = 4; // Possible mouse actions
var mouseAction; // currently selected mouse action
var dragItem; // the cylinder that is being dragged, during a drag operation
var intersects; //the objects intersected
var targetForDragging; // An invisible object that is used as the target for
raycasting while
// call functions to initialise trackballcontrols
init();
// animate();
function init() {
canvas = document.getElementById("maincanvas");
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
document.getElementById("mouseDrag").checked = true;
mouseAction = DRAG;
document.getElementById("mouseRotate").onchange = doChangeMouseAction;
document.getElementById("mouseDrag").onchange = doChangeMouseAction;
document.getElementById("mouseAdd").onchange = doChangeMouseAction;
document.getElementById("mouseDelete").onchange = doChangeMouseAction;
createWorld();
setUpMouseHander(canvas, doMouseDown, doMouseMove);
setUpTouchHander(canvas, doMouseDown, doMouseMove);
raycaster = new THREE.Raycaster();
render();
}
// loop that causes the renderer to draw the scene 60 times per second.
function render() {
renderer.render(scene, camera);
}
function createWorld() {
renderer.setClearColor(0x222222);
// First parameter is FOV in degrees. Second: Aspect ratio. Third/Fourth:
Near/Far clipping plane
camera = new THREE.PerspectiveCamera(37, canvas.width / canvas.height, 1,
10000);
camera.position.z = 5;
camera.position.y = 60;
/**Creating the scene */
scene = new THREE.Scene();
camera.lookAt(new THREE.Vector3(0, 1, 0));
camera.add(new THREE.PointLight(0xffffff, 0.7)); // point light at camera
position
scene.add(camera);
scene.add(new THREE.DirectionalLight(0xffffff, 0.5)); // light shining from
above.
world = new THREE.Object3D();
ground = new THREE.Mesh(
new THREE.BoxGeometry(40, 1, 40),
new THREE.MeshLambertMaterial({ color: "gray" })
);
ground.position.y = -0.5; // top of base lies in the plane y = -5;
world.add(ground);
targetForDragging = new THREE.Mesh(
new THREE.BoxGeometry(1000, 0.01, 1000),
new THREE.MeshBasicMaterial()
);
targetForDragging.material.visible = false;
cylinder = new THREE.Mesh(
new THREE.CylinderGeometry(1, 2, 6, 16, 32),
new THREE.MeshLambertMaterial({ color: "yellow" })
);
cylinder.position.y = 3; // places base at y = 0;
addCylinder(10, 10);
addCylinder(0, 15);
addCylinder(-15, -7);
addCylinder(-8, 5);
addCylinder(5, -12);
}
function addCylinder(x, z) {
var obj = cylinder.clone();
obj.position.x = x;
obj.position.z = z;
world.add(obj);
}
function doMouseDown(x, y) {
//enable rotate
if (mouseAction == ROTATE) {
return true;
}
if (mouseAction != ROTATE) {
controls = 0;
controls.enabled = false;
}
// Affecting drag function
if (targetForDragging.parent == world) {
world.remove(targetForDragging); // I don't want to check for hits on
targetForDragging
}
var a = 2 * x / canvas.width - 1;
var b = 1 - 2 * y / canvas.height;
raycaster.setFromCamera(new THREE.Vector2(a, b), camera);
intersects = raycaster.intersectObjects(world.children); // no need for
recusion since all objects are top-level
if (intersects.length == 0) {
return false;
}
var item = intersects[0];
var objectHit = item.object;
switch (mouseAction) {
case DRAG:
if (objectHit == ground) {
return false;
} else {
dragItem = objectHit;
world.add(targetForDragging);
targetForDragging.position.set(0, item.point.y, 0);
render();
return true;
}
case ADD:
if (objectHit == ground) {
var locationX = item.point.x; // Gives the point of intersection
in world coords
var locationZ = item.point.z;
var coords = new THREE.Vector3(locationX, 0, locationZ);
world.worldToLocal(coords); // to add cylider in correct
position, neew local coords for the world object
addCylinder(coords.x, coords.z);
render();
}
return false;
default: // DELETE
if (objectHit != ground) {
world.remove(objectHit);
render();
}
return false;
}
}
//this function is used when dragging OR rotating
function doMouseMove(x, y, evt, prevX, prevY) {
if (mouseAction == ROTATE) {
controls = new THREE.OrbitControls(camera, canvas);
controls.rotateSpeed = 0.1;
controls.zoomSpeed = 1;
controls.addEventListener('change', render, renderer.domElement);
controls.update();
} else { // drag
var a = 2 * x / canvas.width - 1;
var b = 1 - 2 * y / canvas.height;
raycaster.setFromCamera(new THREE.Vector2(a, b), camera);
intersects = raycaster.intersectObject(targetForDragging);
if (intersects.length == 0) {
return;
}
var locationX = intersects[0].point.x;
var locationZ = intersects[0].point.z;
var coords = new THREE.Vector3(locationX, 0, locationZ);
world.worldToLocal(coords);
a = Math.min(19, Math.max(-19, coords.x)); // clamp coords to the range
-19 to 19, so object stays on ground
b = Math.min(19, Math.max(-19, coords.z));
dragItem.position.set(a, 3, b);
render();
}
}
function doChangeMouseAction() {
if (document.getElementById("mouseRotate").checked) {
mouseAction = ROTATE;
} else if (document.getElementById("mouseDrag").checked) {
mouseAction = DRAG;
} else if (document.getElementById("mouseAdd").checked) {
mouseAction = ADD;
} else {
mouseAction = DELETE;
}
}
window.requestAnimationFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function(callback) {
setTimeout(function() {
callback(Date.now());
}, 1000 / 60);
};
function setUpMouseHander(element, mouseDownFunc, mouseDragFunc,
mouseUpFunc) {
if (!element || !mouseDownFunc || !(typeof mouseDownFunc == "function")) {
throw "Illegal arguments in setUpMouseHander";
}
if (typeof element == "string") {
element = document.getElementById(element);
}
if (!element || !element.addEventListener) {
throw "first argument in setUpMouseHander is not a valid element";
}
var dragging = false;
var startX, startY;
var prevX, prevY;
function doMouseDown(evt) {
if (dragging) {
return;
}
var r = element.getBoundingClientRect();
var x = evt.clientX - r.left;
var y = evt.clientY - r.top;
prevX = startX = x;
prevY = startY = y;
dragging = mouseDownFunc(x, y, evt);
if (dragging) {
document.addEventListener("mousemove", doMouseMove);
document.addEventListener("mouseup", doMouseUp);
}
}
function doMouseMove(evt) {
if (dragging) {
if (mouseDragFunc) {
var r = element.getBoundingClientRect();
var x = evt.clientX - r.left;
var y = evt.clientY - r.top;
mouseDragFunc(x, y, evt, prevX, prevY, startX, startY);
}
prevX = x;
prevY = y;
}
}
function doMouseUp(evt) {
if (dragging) {
document.removeEventListener("mousemove", doMouseMove);
document.removeEventListener("mouseup", doMouseUp);
if (mouseUpFunc) {
var r = element.getBoundingClientRect();
var x = evt.clientX - r.left;
var y = evt.clientY - r.top;
mouseUpFunc(x, y, evt, prevX, prevY, startX, startY);
}
dragging = false;
}
}
element.addEventListener("mousedown", doMouseDown);
}
function setUpTouchHander(element, touchStartFunc, touchMoveFunc, t
touchEndFunc, touchCancelFunc) {
if (!element || !touchStartFunc || !(typeof touchStartFunc == "function")) {
throw "Illegal arguments in setUpTouchHander";
}
if (typeof element == "string") {
element = document.getElementById(element);
}
if (!element || !element.addEventListener) {
throw "first argument in setUpTouchHander is not a valid element";
}
var dragging = false;
var startX, startY;
var prevX, prevY;
function doTouchStart(evt) {
if (evt.touches.length != 1) {
doTouchEnd(evt);
return;
}
evt.preventDefault();
if (dragging) {
doTouchEnd();
}
var r = element.getBoundingClientRect();
var x = evt.touches[0].clientX - r.left;
var y = evt.touches[0].clientY - r.top;
prevX = startX = x;
prevY = startY = y;
dragging = touchStartFunc(x, y, evt);
if (dragging) {
element.addEventListener("touchmove", doTouchMove);
element.addEventListener("touchend", doTouchEnd);
element.addEventListener("touchcancel", doTouchCancel);
}
}
function doTouchMove(evt) {
if (dragging) {
if (evt.touches.length != 1) {
doTouchEnd(evt);
return;
}
evt.preventDefault();
if (touchMoveFunc) {
var r = element.getBoundingClientRect();
var x = evt.touches[0].clientX - r.left;
var y = evt.touches[0].clientY - r.top;
touchMoveFunc(x, y, evt, prevX, prevY, startX, startY);
}
prevX = x;
prevY = y;
}
}
function doTouchCancel() {
if (touchCancelFunc) {
touchCancelFunc();
}
}
function doTouchEnd(evt) {
if (dragging) {
dragging = false;
element.removeEventListener("touchmove", doTouchMove);
element.removeEventListener("touchend", doTouchEnd);
element.removeEventListener("touchcancel", doTouchCancel);
if (touchEndFunc) {
touchEndFunc(evt, prevX, prevY, startX, startY);
}
}
}
element.addEventListener("touchstart", doTouchStart);
}
You can instantiate controls once:
controls = new THREE.OrbitControls(camera, canvas);
controls.enableZoom = false;
controls.enablePan = false;
controls.enableRotate = false;
and then just switch controls.enableRotate between true and false. For example, in the doChangeMouseAction() function. Creativity is up to you.

how can i modify mesh size in my scene?

First to say, I do not speak English well.
Anyway, Let's get something straight.
I want to modify mesh(cube) size when i scroll the mouse wheel to zoomin or zoomout.
I hope to increase mesh(cube) size when zoom in and Opposite case, too.
my code is below.
<script src="../lib/Three.js/build/Three.js"></script>
<script src="http://code.jquery.com/jquery.min.js"></script>
<script>
var CANVAS_WIDTH = 400,
CANVAS_HEIGHT = 300;
var renderer = null, //웹지엘 또는 2D
scene = null, //씬 객체
camera = null; //카메라 객체
var capture = false,
start = [],
angleX = 0,
angleY = 0,
zoom = 1.0;
function initWebGL()
{
setupRenderer();
setupScene();
setupCamera();
var myColor = new THREE.Color( 0xff0000 );
myColor.setRGB(0.0, 1.0, 0.0);
var alpha = 1.0;
renderer.setClearColor(myColor, alpha);
(function animLoop(){
//camera zoom in and zomm out
renderer.render(scene, camera);
requestAnimationFrame( animLoop );
})();
/**
mouse event code for screen control about zoom, rotate
**/
$(document).ready(function() {
console.log($("#my-canvas").length);
$("#my-canvas").on("mousedown", function(e) {
capture = true;
start = [e.pageX, e.pageY];
console.log("start:" + start);
});
$("#my-canvas").on("mouseup", function(e) {
console.log(e.type);
capture = false;
console.log("end capture");
});
$("#my-canvas").mousemove(function(e) {
console.log(e.type);
if (capture)
{
var x = (e.pageX - start[0]);
var y = (e.pageY - start[1]);
//시작위치 업데이트
start[0] = e.pageX;
start[1] = e.pageY;
angleX += x;
angleY += y;
//console.log()
}
});
});
$(document).ready(function(evt) {
$("#my-canvas").on("mousewheel", function (e) {
adjustZoom(window.event.wheelData);
}).on("DOMMouseScroll", function (e) {
//파이어폭스
adjustZoom(e.originalEvent.detail * -1.0);
});
});
function adjustZoom(delta) {
if(delta > 0)
{
zoom += 0.1;
} else {
zoom -= 0.1;
if(zoom < 0.01) { zoom = 0.1;}
}
}
}
function setupRenderer()
{
renderer = new THREE.WebGLRenderer({canvas: document.createElement( 'canvas')});
renderer.setSize( CANVAS_WIDTH, CANVAS_HEIGHT );
$(renderer.domElement).attr('id','my-canvas');
//캔버스 엘리먼트를 추가하는 곳
document.body.appendChild( renderer.domElement );
}
function setupScene()
{
scene = new THREE.Scene();
addMesh();
addLight();
}
function setupCamera()
{
camera = new THREE.PerspectiveCamera(
35, //시야
CANVAS_WIDTH / CANVAS_HEIGHT, //종횡비
.1, //전방 절단면
10000 //후방 절단면
);
camera.position.set(-15, 10, 10);
camera.lookAt( scene.position );
scene.add( camera );
}
function addMesh()
{
var cube = new THREE.Mesh(
new THREE.CubeGeometry( 5, 7, 5 ),
new THREE.MeshLambertMaterial( { color: 0x0000FF} )
);
scene.add(cube);
}
function addLight()
{
var light = new THREE.PointLight( 0xFFFFFF );
light.position.set( 20, 20, 20 );
scene.add(light);
}
</script>
You wish to modify the scale value of the object. This can be done for each axis.
Each mesh object has a scale value as a vector.
So this would
mesh.scale.set( 2, 1, 1 )
Or in your case
cube.scale.set();
You can also access it this way,
cube.scale.x = 2.0;
Though the cube object is stored locally, you might want to set the globally and alter this value with the mouse action.
Hope that well.
As a note, the question provides a bit too much of the script, shorter and faster to the point is better.

Accessing single particles in THREE.js Particle System

I really tried every example, searched the web for hours but I can't seem to get it working!
So I simply tried to implement a little particle system simulating falling snow, just like this: http://www.aerotwist.com/tutorials/creating-particles-with-three-js/
But I only can access it in whole. Meaning I can rotate it as such but as soon as I try to iterate over it's vertices, the whole animation is getting the hiccups! I would really appreciate some help here!
-
Here are the key parts:
-> Setting up the particle system:
var partikelGeo = new THREE.Geometry();
var partikelMaterial = new THREE.ParticleBasicMaterial({
color:0xffffff,
size: 10,
map: THREE.ImageUtils.loadTexture('snowflake2.png'),
blending: THREE.AdditiveBlending,
transparent:true
});
var partikelAnzahl = 3500;
for (var p = 0; p < partikelAnzahl; p++) {
var pX = Math.random() * 1000 -500;
var pY = Math.random() * 1000 -500;
var pZ = Math.random() * 1000 -500;
var partikel = new THREE.Vertex(new THREE.Vector3(pX,pY,pZ));
partikel.velocity = new THREE.Vector3(0,-Math.random(),0);
partikelGeo.vertices.push(partikel);
}
var partikelSystem = new THREE.ParticleSystem(partikelGeo, partikelMaterial);
partikelSystem.sortParticles = true;
scene.add(partikelSystem);
-> Rendering & Animation on mouseclick
var frame = 0;
function animate(){
// request new frame
requestAnimationFrame(function(){
animate();
});
// render
render();
}
animate();
var check = 0;
onmousedown = function(){
if (check) {
check = 0;
}else{
check = 1;
}
}
function render() {
if (check) {
clickDo();
}
camera.lookAt(new THREE.Vector3(0,0,0));
renderer.render(scene,camera);
}
function clickDo() {
frame++;
partikelSystem.rotation.y += 0.01;
var pCount = partikelAnzahl;
while(pCount--) {
// get the particle
var particle =
partikelGeo.vertices[pCount];
// check if we need to reset
if(particle.position.y < -200) {
particle.position.y = 200;
particle.velocity.y = 0;
}
// update the velocity with
// a splat of randomniz
particle.velocity.y -=
Math.random() * .1;
// and the position
particle.position.addSelf(
particle.velocity);
}
// flag to the particle system
// that we've changed its vertices.
partikelSystem.
geometry.
__dirtyVertices = true;
}
Rah
Your code looks good to me. I would just suggest to try not sorting your particles as you use an additive blending:
partikelSystem.sortParticles = false;

Resources