I've successfully merged my cubes, although they seem to merge together reverting back to their original position/rotation, ignoring the declared positioning/rotation
var geometries = [];
cube1.position.set( 0, 0, 0 );
geometries.push( cube1 );
cube2.position.set( 1, 0, 0 );
geometries.push( cube2 );
cube3.position.set( 0, 1, 0 );
geometries.push( cube3 );
const cubes = BufferGeometryUtils.mergeBufferGeometries( geometries );
scene.add( cubes );//All the cubes get merged on 0, 0, 0 - overlapping one another
How do I position the cubes so they can be merged in their declared positions?
Here is a working example of using BufferGeometryUtils and .applyMatrix4():
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0";
import {
OrbitControls
} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls.js";
import * as BufferGeometryUtils from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/utils/BufferGeometryUtils";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.setScalar(1).setLength(6);
let renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(0x404040);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
let boxes = [
makeBox(2, 0, 0, 0xff0000),
makeBox(0, 2, 0, 0x00ff00),
makeBox(0, 0, 2, 0x0000ff)
];
let materials = [];
let g = BufferGeometryUtils.mergeBufferGeometries(boxes.map(b => {
materials.push(b.material);
b.updateMatrixWorld(); // needs to be done to be sure that all transformations are applied
return b.geometry.applyMatrix4(b.matrixWorld);
}), true);
let o = new THREE.Mesh(g, materials);
scene.add(o);
window.addEventListener("resize", onWindowResize);
animate();
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
}
function makeBox(x, y, z, color) {
let g = new THREE.BoxGeometry();
let m = new THREE.MeshLambertMaterial({
color: color
});
let box = new THREE.Mesh(g, m);
box.position.set(x, y, z);
return box;
}
</script>
Related
I'm trying to create an ocean for my Three.js application. I took the example from this site:
https://codepen.io/RemiRuc/pen/gJMwOe?fbclid=IwAR2caTQL-AOPE2Gv6x4rzSWBrOmAh2j-raqesOO0XbYQAuSG37imbMszSis
var params = {
res : 32,
speed : 8,
amp : 2,
wireframe : true,
backgroundColor : 0x9c81e3,
planeColor : 0x4a4a4a
}
var scene = new THREE.Scene();
scene.background = new THREE.Color(params.backgroundColor)
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 )
let canvas = document.getElementById("webgl")
var renderer = new THREE.WebGLRenderer({canvas:canvas, antialias: true})
renderer.setSize( window.innerWidth, window.innerHeight )
var simplex = new SimplexNoise()
var light = new THREE.AmbientLight( 0xcccccc ); // soft white light
scene.add( light );
var pointLight = new THREE.PointLight( 0xeeeeee, 1, 100 );
pointLight.position.set( 0, 20, -20 );
scene.add( pointLight );
let geometry, material, plane
createPlane()
camera.position.z = 5;
camera.position.y = 3;
camera.lookAt(new THREE.Vector3( 0, 3, 0 ))
var animate = function () {
requestAnimationFrame( animate );
for (var i = 0; i < geometry.vertices.length; i++) {
var z = (i + Date.now() * params.speed/100000)
geometry.vertices[i].z = simplex.noise4D(z,z,z,z) * params.amp
plane.geometry.verticesNeedUpdate = true;
}
scene.background = new THREE.Color(params.backgroundColor)
material.color = new THREE.Color(params.planeColor)
material.wireframe = params.wireframe
camera.rotation.y += 0.001
renderer.render( scene, camera );
};
animate();
function createPlane(){
geometry = new THREE.PlaneGeometry( 200, 200, params.res,params.res );
material = new THREE.MeshLambertMaterial( {color: params.planeColor, side: THREE.DoubleSide, wireframe: params.wireframe} );
plane = new THREE.Mesh( geometry, material );
scene.add( plane );
plane.rotation.x = Math.PI/2
}
/***RESIZE***/
window.addEventListener('resize', ()=>{
document.querySelector('canvas').style.width = window.innerWidth + "px"
document.querySelector('canvas').style.height = window.innerHeight + "px"
renderer.setSize( window.innerWidth, window.innerHeight )
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
})
var gui = new dat.GUI()
var controller = gui.add(params, "res", 0, 100).name("Plane resolution")
gui.add(params, "speed", 0, 500).name("Wave speed")
gui.add(params, "amp", 0, 20).name("Wave amplitude")
gui.add(params, "wireframe", 0, 20).name("Wireframe")
gui.addColor(params, "backgroundColor").name("Background color")
gui.addColor(params, "planeColor").name("Plane color")
controller.onChange(()=>{
scene.remove(plane)
createPlane()
})
Issue is, I'm using PlaneBufferGeometry instead of PlaneGeometry, and it seems there are some differences
My code in render after creating the waterPlane
for (var i = 0; i < waterGeometry.attributes.position.count; i++) {
var z = (i + Date.now() * params.speed/100000);
waterGeometry.attributes.position[i] = simplex.noise4D(z,z,z,z) * params.amp;
}
waterGeometry.attributes.position.needsUpdate = true;
waterPlaneMesh.attributes.position.needsUpdate = true;
I'm not getting any errors, but no matter what I do, all I get is a flat wireframe plane geometry that doesn't move or anything. I think issue is in the updating of the plane?
This is an example of how you can displace vertices of a buffer geometry, using that SimplexNoise library:
body {
margin: 0;
background-color: #000;
color: #fff;
font-family: Monospace;
font-size: 13px;
line-height: 24px;
overscroll-behavior: none;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0";
import { OrbitControls } from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls";
import { createNoise3D } from "https://cdn.skypack.dev/simplex-noise";
let simplex = createNoise3D();
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 2000);
camera.position.set(0, 0.5, 1).setLength(12);
let renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", onWindowResize);
//scene.add(new THREE.GridHelper())
let controls = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
let v3 = new THREE.Vector3();
let v2 = new THREE.Vector2();
let g = new THREE.PlaneGeometry(200, 200, 100, 100);
g.rotateX(-Math.PI *0.5);
let m = new THREE.MeshLambertMaterial({color: "aqua", wireframe: false});
let o = new THREE.Mesh(g, m);
scene.add(o);
let clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
let t = clock.getElapsedTime();
for(let i = 0; i < g.attributes.position.count; i++){
v2.fromBufferAttribute(g.attributes.uv, i).addScalar(t * 0.01).multiplyScalar(20);
let h = simplex(v2.x, v2.y, t * 0.1);
g.attributes.position.setY(i, h);
}
g.computeVertexNormals();
g.attributes.position.needsUpdate = true;
});
function onWindowResize() {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
}
</script>
Goal
I am trying to create an EllipseCurve (https://threejs.org/docs/#api/en/extras/curves/EllipseCurve) on which a camera should move.
What I did to achive the goal?
This is the code for the ellipse so far.
var curve = new THREE.EllipseCurve(
0,0,
1, 1,
0, 2 * Math.PI,
false,
1.57
)
const points = curve.getPoints( 50 );
const geometry = new THREE.BufferGeometry().setFromPoints( points );
var material = new THREE.LineBasicMaterial( { color : 0xffffff } );
// Create the final object to add to the scene
var curveObject = new THREE.Line( geometry, material );
scene.add(curveObject);
I can see it in the scene like this:
The Problem
I tried to rotate the Ellipse Curve 90 degree arround the x axis clockwise. As I understood from the documentation, the last parameter of the defining function should rotate it.
const curve = new THREE.EllipseCurve(
0, 0, // ax, aY
10, 10, // xRadius, yRadius
0, 2 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
);
Thank you in advance for you answer. I am quite new to Three.js so sorry if this question might be stupid :D
Get a point on the curve and apply a matrix4 to it.
Here is a concept of how you can do it (see the lines with cam in the animation loop, better to watch with "Full page"):
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.134.0";
import {
OrbitControls
} from "https://cdn.skypack.dev/three#0.134.0/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(-10, 10, 10);
let renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(innerWidth, innerHeight);
renderer.autoClear = false;
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
})
let controls = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
let grid = new THREE.GridHelper();
grid.position.y = -5;
scene.add(grid);
let obj = new THREE.Mesh(new THREE.IcosahedronGeometry(1, 0), new THREE.MeshLambertMaterial({
color: "aqua"
}));
scene.add(obj);
let curve = new THREE.EllipseCurve(0, 0, 10, 5);
let line = new THREE.Line(new THREE.BufferGeometry().setFromPoints(curve.getSpacedPoints(100)), new THREE.LineBasicMaterial({
color: "yellow"
}));
line.rotation.x = -Math.PI * 0.25;
line.rotation.z = Math.PI * 0.125;
line.position.x = 5;
scene.add(line);
let cam = new THREE.PerspectiveCamera(25, 1, 1.5, 25);
let camHelper = new THREE.CameraHelper(cam);
scene.add(camHelper);
let clock = new THREE.Clock();
let v = new THREE.Vector3();
renderer.setAnimationLoop(() => {
let t = (clock.getElapsedTime() * 0.05) % 1;
// magic is in these lines //////////////////
cam.position.copy(curve.getPointAt(t, v));
cam.position.applyMatrix4(line.matrixWorld);
cam.lookAt(obj.position);
/////////////////////////////////////////////
renderer.clear();
renderer.setViewport(0, 0, innerWidth, innerHeight);
renderer.render(scene, camera);
renderer.setViewport(0, innerHeight - 256, 256, 256);
renderer.render(scene, cam);
})
</script>
the aRotation, angle-rotation, will affect the local origin of other angle settings for this curve. It is not the overall ellipse rotation, but the orientation of any offset relative to default. A different starting point. It would turn a Pac-Man mouth into a backwards Pac-Man mouth at +/- 180-degrees. To rotate the overall curve in world-space, use one of the various methods available, such as curve.rotation.set(0,1,0) or rotation.y += 1. Please refer to documentation for specific variants of rotation.
I want to make a smoke effect like this demo (Smoke) by using points. Now I have two problems.
At the beginning of the animation, all points get together and go up together like cloud.
the shape of the effect looks like a rectangular prism. How to make it looks like a cone (like the following picture)?
Can somebody please tell me how to fix these problems? Thanks!
let renderer, scene, camera;
let controls, stats;
// points
const particleCount = 500;
let points;
function createPoints() {
const geometry = new THREE.Geometry();
const texture = new THREE.TextureLoader().load('http://stemkoski.github.io/Three.js/images/smokeparticle.png');
let material = new THREE.PointsMaterial({
size: 15,
map: texture,
blending: THREE.AdditiveBlending,
depthWrite: false,
transparent: true,
color: 'rgb(30,30,30)'
});
const range = 10;
for (let i = 0; i < particleCount; i++) {
const x = THREE.Math.randInt(-range, range);
const y = THREE.Math.randInt(-range, range);
const z = THREE.Math.randInt(-range, range);
const point = new THREE.Vector3(x, y, z);
point.velocityX = THREE.Math.randFloat(-0.01, 0.01);
point.velocityY = THREE.Math.randFloat(0.1, 0.3);
geometry.vertices.push(point);
}
points = new THREE.Points(geometry, material);
scene.add(points);
}
function init() {
// scene
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x000000, 0.0008);
// camera
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set(0, 10, 170);
camera.lookAt(scene.position);
let axes = new THREE.AxesHelper(20);
scene.add(axes);
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// OrbitControls
controls = new THREE.OrbitControls( camera, renderer.domElement );
createPoints();
document.body.appendChild(renderer.domElement);
}
function pointsAnimation() {
points.geometry.vertices.forEach(function(v) {
v.y = v.y + v.velocityY;
v.x = v.x + v.velocityX;
if (v.y >= 100) v.y = 0;
});
points.geometry.verticesNeedUpdate = true;
}
function render() {
pointsAnimation();
requestAnimationFrame(render);
controls.update();
renderer.render(scene, camera);
}
window.addEventListener('resize', function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
init();
render();
body {
margin: 0;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/104/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
You could simply set velocityX depending on point x, e.g. :
point.velocityX = THREE.Math.randFloat(0.0, 0.1) * Math.sign(x);
Don't forget to reset x position :
if (v.y >= 100) {
v.x = THREE.Math.randInt(-10, 10);
v.y = 0;
}
I'm trying to raycast skinning mesh (of knowing issue) after some skeleton changes (without animation on it, so performance isn't a priority).
The tricky thing i imagine in this attempt is:
Load skinned mesh add to scene
Make some changes in positions of specific bones at loaded mesh
Copy geometries of transformed loaded mesh (maybe from buffer?)
Create new mesh (some kind of imitation ghost mesh) from copied geometries and apply to it
set raycast on ghost mesh with opacity material= 0.0
Above list should work, but I'm stuck third day on point 3 cause I can't get transformed vertices after skinning.
var scene, camera, renderer, mesh, ghostMesh;
var raycaster = new THREE.Raycaster();
var raycasterMeshHelper;
initScene();
render();
function initScene() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 200);
camera.position.set(20, 7, 3);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
var orbit = new THREE.OrbitControls(camera, renderer.domElement);
//lights stuff
var ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);
var lights = [];
lights[0] = new THREE.PointLight(0xffffff, 1, 0);
lights[1] = new THREE.PointLight(0xffffff, 1, 0);
lights[2] = new THREE.PointLight(0xffffff, 1, 0);
lights[0].position.set(0, 200, 0);
lights[1].position.set(100, 200, 100);
lights[2].position.set(-100, -200, -100);
scene.add(lights[0]);
scene.add(lights[1]);
scene.add(lights[2]);
//raycaster mesh
var raycasterMaterial = new THREE.MeshBasicMaterial({
color: 0xdddddd,
opacity: 0.7,
transparent: true
});
var geometrySphere = new THREE.SphereGeometry(0.5, 16, 16);
raycasterMeshHelper = new THREE.Mesh(geometrySphere, raycasterMaterial);
raycasterMeshHelper.visible = false;
scene.add(raycasterMeshHelper);
renderer.domElement.addEventListener('mousemove', onMouseMove, false);
//model Loading
var loader = new THREE.JSONLoader();
loader.load("https://raw.githubusercontent.com/visus100/skinnedTests/master/js_fiddle/skinned_mesh.json", function(geometry) {
var meshMaterial = new THREE.MeshStandardMaterial({
color: 0x00df15,
skinning: true
});
mesh = new THREE.SkinnedMesh(geometry, meshMaterial);
scene.add(mesh);
var skeleton = new THREE.SkeletonHelper(mesh);
scene.add(skeleton);
//some experimental skeletonal changes
mesh.skeleton.bones[1].rotation.z += 0.10;
mesh.skeleton.bones[2].rotation.x += -0.65;
mesh.skeleton.bones[3].rotation.y += -0.45;
mesh.skeleton.bones[3].position.x += 0.11;
//updates matrix
mesh.updateMatrix();
mesh.geometry.applyMatrix(mesh.matrix);
mesh.updateMatrixWorld(true);
//crate ghost mesh geometry
createGhostMesh();
//crate point cloud helper from buffergeometry
var bufferGeometry = new THREE.BufferGeometry().fromGeometry(mesh.geometry);
var particesMaterial = new THREE.PointsMaterial({
color: 0xff00ea,
size: 0.07,
sizeAttenuation: false
});
particles = new THREE.Points(bufferGeometry, particesMaterial);
particles.sortParticles = true;
scene.add(particles);
});
}
function createGhostMesh() {
var geometryForGhostMesh = new THREE.Geometry();
//push vertices and other stuff to geometry
for (i = 0; i < mesh.geometry.vertices.length; i++) {
var temp = new THREE.Vector3(mesh.geometry.vertices[i].x, mesh.geometry.vertices[i].y, mesh.geometry.vertices[i].z);
geometryForGhostMesh.vertices.push(temp);
//////
//here should be the code for calc translation vertices of skinned mesh and added to geometryForGhostMesh
//////
geometryForGhostMesh.skinIndices.push(mesh.geometry.skinIndices[i]);
geometryForGhostMesh.skinWeights.push(mesh.geometry.skinWeights[i]);
}
for (i = 0; i < mesh.geometry.faces.length; i++) {
geometryForGhostMesh.faces.push(mesh.geometry.faces[i]);
}
//create material and add to scene
var ghostMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000,
opacity: 0.1,
transparent: true,
skinning: true
});
ghostMesh = new THREE.Mesh(geometryForGhostMesh, ghostMaterial);
scene.add(ghostMesh);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
};
function onMouseMove(event) {
//raycaster for ghostMesh
if (ghostMesh) {
var rect = renderer.domElement.getBoundingClientRect();
var mouseX = ((event.clientX - rect.left) / rect.width) * 2 - 1;
var mouseY = -((event.clientY - rect.top) / rect.height) * 2 + 1;
raycaster.setFromCamera(new THREE.Vector2(mouseX, mouseY), camera);
var intersects = raycaster.intersectObject(ghostMesh);
if (intersects.length > 0) {
raycasterMeshHelper.visible = true;
raycasterMeshHelper.position.set(0, 0, 0);
raycasterMeshHelper.lookAt(intersects[0].face.normal);
raycasterMeshHelper.position.copy(intersects[0].point);
} else {
raycasterMeshHelper.visible = false;
}
}
}
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
Please note that I need this in thre.js build r98 or less, because the rest of my code (not included here) and without morph tangents only skinning bones.
I tried to write it clearly and please if anyone want help do it so because I'm not a pro.
I not including my approach of calculating transformed geometries because I failed too hard.
I dug a lot about this problem here e.g. issue6440 and for today it's still not fixed.
But there existing methods to work with it e.g https://jsfiddle.net/fnjkeg9x/1/ but after several of attempts I failed and my conclusion is that the stormtrooper works on morph tanges and this could be the reason I failed.
EDIT:
I created next codepen based on this topics get-the-global-position-of-a-vertex-of-a-skinned-mesh and Stormtrooper.
Decided to start with simple box to make bounding around skinned transformed mesh.
Result is fail because it giving 0 at line:
boneMatrix.fromArray(skeleton.boneMatrices, si * 16);
Here i comparing stormtrooper with my example output from console: Screen shot image
Codpen with new progress: https://codepen.io/donkeyLuck0/pen/XQbBMQ
My other idea is to apply this bones form loaded model and rig as a morph tangent programmatically (but i don't even know if it is possible and how to figure it out)
Founded example of animated model
Sketchfab animation with points tracking
This is super late to the game, but here's an example of GPU picking that works with skinned meshes and doesn't require a separate picking scene to keep in sync with your main scene, nor does it require the user to manage custom materials:
https://github.com/bzztbomb/three_js_gpu_picking
The trick that allows for easy material overriding and scene re-use is here:
https://github.com/bzztbomb/three_js_gpu_picking/blob/master/gpupicker.js#L58
A proper support for raycasting for skinned meshes was added in https://github.com/mrdoob/three.js/pull/19178 in revision 116.
You can use GPU picking to "pick" skinned object. It won't give you a position though
Note: GPU picking requires rendering every pickable object with a custom material. How you implement that is up to you. This article does it by making 2 scenes. That might not be as useful for skinned objects.
Unfortunately three.js provides no way to override materials AFAICT. Here's an example that replaces the materials on the pickable objects before rendering for picking and then restores them after. You would also need to hide any objects you don't want picked.
const renderer = new THREE.WebGLRenderer({
antialias: true,
canvas: document.querySelector('canvas'),
});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 2, 0.1, 200);
camera.position.set(20, 7, 3);
const orbit = new THREE.OrbitControls(camera, renderer.domElement);
//lights stuff
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);
const lights = [];
lights[0] = new THREE.PointLight(0xffffff, 1, 0);
lights[1] = new THREE.PointLight(0xffffff, 1, 0);
lights[2] = new THREE.PointLight(0xffffff, 1, 0);
lights[0].position.set(0, 200, 0);
lights[1].position.set(100, 200, 100);
lights[2].position.set(-100, -200, -100);
scene.add(lights[0]);
scene.add(lights[1]);
scene.add(lights[2]);
//raycaster mesh
const raycasterMaterial = new THREE.MeshBasicMaterial({
color: 0xdddddd,
opacity: 0.7,
transparent: true
});
const geometrySphere = new THREE.SphereGeometry(0.5, 16, 16);
raycasterMeshHelper = new THREE.Mesh(geometrySphere, raycasterMaterial);
raycasterMeshHelper.visible = false;
scene.add(raycasterMeshHelper);
//model Loading
const pickableObjects = [];
const loader = new THREE.JSONLoader();
loader.load("https://raw.githubusercontent.com/visus100/skinnedTests/master/js_fiddle/skinned_mesh.json", (geometry) => {
const meshMaterial = new THREE.MeshStandardMaterial({
color: 0x00df15,
skinning: true
});
const mesh = new THREE.SkinnedMesh(geometry, meshMaterial);
scene.add(mesh);
const id = pickableObjects.length + 1;
pickableObjects.push({
mesh,
renderingMaterial: meshMaterial,
pickingMaterial: new THREE.MeshPhongMaterial({
skinning: true,
emissive: new THREE.Color(id),
color: new THREE.Color(0, 0, 0),
specular: new THREE.Color(0, 0, 0),
//map: texture,
//transparent: true,
//side: THREE.DoubleSide,
//alphaTest: 0.5,
blending: THREE.NoBlending,
}),
});
//some experimental skeletonal changes
mesh.skeleton.bones[1].rotation.z += 0.10;
mesh.skeleton.bones[2].rotation.x += -0.65;
mesh.skeleton.bones[3].rotation.y += -0.45;
mesh.skeleton.bones[3].position.x += 0.11;
//updates matrix
mesh.updateMatrix();
mesh.geometry.applyMatrix(mesh.matrix);
mesh.updateMatrixWorld(true);
});
class GPUPickHelper {
constructor() {
// create a 1x1 pixel render target
this.pickingTexture = new THREE.WebGLRenderTarget(1, 1);
this.pixelBuffer = new Uint8Array(4);
}
pick(cssPosition, scene, camera) {
const {
pickingTexture,
pixelBuffer
} = this;
// set the view offset to represent just a single pixel under the mouse
const pixelRatio = renderer.getPixelRatio();
camera.setViewOffset(
renderer.context.drawingBufferWidth, // full width
renderer.context.drawingBufferHeight, // full top
cssPosition.x * pixelRatio | 0, // rect x
cssPosition.y * pixelRatio | 0, // rect y
1, // rect width
1, // rect height
);
// render the scene
// r102
//renderer.setRenderTarget(pickingTexture);
//renderer.render(scene, camera);
//renderer.setRenderTarget(null);
// r98
renderer.render(scene, camera, pickingTexture);
// clear the view offset so rendering returns to normal
camera.clearViewOffset();
//read the pixel
renderer.readRenderTargetPixels(
pickingTexture,
0, // x
0, // y
1, // width
1, // height
pixelBuffer);
const id =
(pixelBuffer[0] << 16) |
(pixelBuffer[1] << 8) |
(pixelBuffer[2]);
return id;
}
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
const pickPosition = {
x: 0,
y: 0,
};
const pickHelper = new GPUPickHelper();
let lastPickedId = 0;
let lastPickedObjectSavedEmissive;
function render(time) {
time *= 0.001; // convert to seconds;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
if (lastPickedId) {
pickableObjects[lastPickedId - 1].renderingMaterial.emissive.setHex(lastPickedObjectSavedEmissive);
lastPickedId = 0;
}
for (pickableObject of pickableObjects) {
pickableObject.mesh.material = pickableObject.pickingMaterial;
}
const id = pickHelper.pick(pickPosition, scene, camera, time);
for (pickableObject of pickableObjects) {
pickableObject.mesh.material = pickableObject.renderingMaterial;
}
const pickedObject = pickableObjects[id - 1];
if (pickedObject) {
lastPickedId = id;
lastPickedObjectSavedEmissive = pickedObject.renderingMaterial.emissive.getHex();
pickedObject.renderingMaterial.emissive.setHex((time * 8) % 2 > 1 ? 0xFFFF00 : 0xFF0000);
}
renderer.render(scene, camera);
requestAnimationFrame(render);
};
requestAnimationFrame(render);
function setPickPosition(event) {
pickPosition.x = event.clientX;
pickPosition.y = event.clientY;
}
function clearPickPosition() {
// unlike the mouse which always has a position
// if the user stops touching the screen we want
// to stop picking. For now we just pick a value
// unlikely to pick something
pickPosition.x = -100000;
pickPosition.y = -100000;
}
window.addEventListener('mousemove', setPickPosition);
window.addEventListener('mouseout', clearPickPosition);
window.addEventListener('mouseleave', clearPickPosition);
window.addEventListener('touchstart', (event) => {
// prevent the window from scrolling
event.preventDefault();
setPickPosition(event.touches[0]);
}, {
passive: false
});
window.addEventListener('touchmove', (event) => {
setPickPosition(event.touches[0]);
});
window.addEventListener('touchend', clearPickPosition);
window.addEventListener('mousemove', setPickPosition);
window.addEventListener('mouseout', clearPickPosition);
window.addEventListener('mouseleave', clearPickPosition);
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/js/controls/OrbitControls.js"></script>
<canvas></canvas>
I already asked a Question here: Add Thickness to faces
The core question is solved but i ran into another problem.
Before my walls were set on side:THREE.BackSide so that they didnt show when they faced the camera but now when they have a thickness that doesnt work anymore and i dont realy understand why.
Before:
Before
After: After
How can i make the thick walls behave like the Plane walls ?
A very rough concept of controlling the visibility of a wall (I've slightly changed translating and positioning of a geometry):
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 5, 5);
camera.lookAt(scene.position);
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);
scene.add(new THREE.GridHelper(10, 10));
var points = [
new THREE.Vector3(-2, 0, 2),
new THREE.Vector3(2, 0, 2),
new THREE.Vector3(2, 0, -2),
new THREE.Vector3(-2, 0, -2)
]
var walls = [];
points.forEach((p, idx, points) => {
let nextIdx = idx === points.length - 1 ? 0 : idx + 1;
buildWall(p, points[nextIdx], 2, 0.1);
});
function buildWall(pointStart, pointEnd, height, thickness) {
var boxW = pointEnd.clone().sub(pointStart).length();
var boxH = height;
var boxD = thickness;
var boxGeometry = new THREE.BoxGeometry(boxW, boxH, boxD);
boxGeometry.translate(0, boxH * 0.5, 0);
boxGeometry.rotateY(-Math.PI * 0.5);
var wall = new THREE.Mesh(boxGeometry, new THREE.MeshBasicMaterial({
color: "aqua",
wireframe: true
}));
wall.position.copy(pointStart).add(pointEnd).multiplyScalar(0.5);
wall.lookAt(pointEnd);
scene.add(wall);
walls.push(wall);
}
var currentPosition = new THREE.Vector3();
render();
function render() {
requestAnimationFrame(render);
walls.forEach(w => {
w.visible = currentPosition.copy(w.position).sub(camera.position).lengthSq() > camera.position.lengthSq();
})
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/91/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>