I'm quite new in 3D and Threejs.
I set up a scene with a ground, on the top of the ground lots of cubes.
http://jsfiddle.net/whurp02s/1/
I'm trying to select cubes that cross the yellow rectangle.
So I looked at exemple on internet and found the Raycaster object and it's intersectObject function
//**************** colision detection
var caster = new THREE.Raycaster();
var collisions = [];
var rays = [
new THREE.Vector3(0, 0, 1),
new THREE.Vector3(1, 0, 1),
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(1, 0, -1),
new THREE.Vector3(0, 0, -1),
new THREE.Vector3(-1, 0, -1),
new THREE.Vector3(-1, 0, 0),
new THREE.Vector3(-1, 0, 1)
];
for ( var i = 0; i < rays.length; i += 1 ) {
caster.set( squareTL.position, rays[i] );
for( var boxId in boxGroup ) {
var boxObj = boxGroup[boxId];
collisions = caster.intersectObject( boxObj );
if ( collisions.length ) {
console.log(collisions);
} else console.log("no colision");
}
}
But 0 collision are found.
There is something obvious that I'm missing...
I've looked at your jsfiddle code and I've seen a few things that should help:
-Your "boxGroup" array needs to be populated with another array containing the object and not the object itself
-The statement: if ( collisions.length) should be changed to (collisions.length >0) . In case the length is > 1
-Optional actions: Add your collision logic into an animation/run loop
-I would create a new JSfeedle code specific to collition work with less objects. It should be easier for you to debug and understand.
Good luck,
J3zusla
Related
Background of Question
I am working on a game that is a mix between Europa Universalis 4 and Age of Empires 3. The game is made in JavaScript and utilizes Three.js (r109) library. As of right now I have made randomly generated low-poly terrain with trees and reflective water. In the beginning I want the game to spawn a Navy, represented by a galleon (in screenshot below). I want to make it so when its called to spawn, it will pick a random location within the bounds of the water. The water mesh is represented by a semi-opaque plane spanning the size of the map- with a THREE.Reflector object underneath it. The terrain is also a plane but has been altered using a SimplexNoise heightmap.
The Question
How do I detect if an x and z position intersects with the water mesh and not the terrain mesh? THREE.Raycaster seems to be useful for what I am trying to do, but I wan't to know if there is a better solution. If using THREE.Raycaster is the best option, how would I go about implementing it for this purpose? Should I make an individual THREE.Raycaster for every object I am doing this with? Keep in mind I'm not placing this object with the mouse, I want to place it with a method that checks the position as stated above.
It's difficult to give specific advice without knowing anything at all about your code, but it sounds like all you need to do is create a collision list for your valid water surfaces and then check that when you want to spawn something.
A very simple jsfiddle is here. It creates a "land" mesh (green) and a "water" mesh (blue), adds the "water" mesh to a variable called collisionList. It then calls a spawn function for coordinates diagonally across both surfaces. The function uses a raycaster to check if the coordinates are over the "water" mesh and spawns a red cube if it is.
Here's the code:
window.onload = function() {
var camera = null, land = null, water = null, renderer = null, lights;
var collisionList;
var d, n, scene = null, animID;
n = document.getElementById('canvas');
function load() {
var height = 600, width = 800;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(60, width/height, 1, 1000);
camera.position.set(0, 0, -10);
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(camera);
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]);
water = new THREE.Mesh(new THREE.PlaneGeometry(7, 7, 10),
new THREE.MeshStandardMaterial({
color: 0x0000ff,
side: THREE.DoubleSide,
}));
water.position.set(0, 0, 0);
scene.add(water);
land = new THREE.Mesh(new THREE.PlaneGeometry(12, 12, 10),
new THREE.MeshStandardMaterial({
color: 0x00ff00,
side: THREE.DoubleSide,
}));
land.position.set(0, 0, 1);
scene.add(land);
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
n.appendChild(renderer.domElement);
collisionList = [ water ];
for(var i = -6; i < 6; i++)
spawn(i);
animate();
}
function spawn(x) {
var dir, intersect, mesh, ray, v;
v = new THREE.Vector3(x, x, -1);
dir = new THREE.Vector3(0, 0, 1);
ray = new THREE.Raycaster(v, dir.normalize(), 0, 100);
intersect = ray.intersectObjects(collisionList);
if(intersect.length <= 0)
return;
mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1, 1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0xff0000 }));
mesh.position.set(x, x, 0);
scene.add(mesh);
}
function animate() {
if(!scene) return;
animID = requestAnimationFrame(animate);
render();
update();
}
function render() {
if(!scene || !camera || !renderer) return;
renderer.render(scene, camera);
}
function update() {
if(!scene || !camera) return;
}
load();
As for whether this is a smart way to do it, that really depends on the design of the rest of your game.
If your world is procgen then it may be more efficient/less error prone to generate the spawn points (and any other "functional" parts of the world) first and use that to generate the geography instead of the other way around.
I want to create a BufferGeometry without setting the indices.
(As written here, in this case the renderer assumes that each three contiguous positions represent a single triangle), but I get the warning Render count or primcount is 0 and no geometry is shown.
What am I doing wrong?
Here following the code to reproduce the issue.
var buffGeometry = new THREE.BufferGeometry();
buffGeometry.attributes =
{
position:
{
itemSize: 3, array: new Float32Array([10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 10, 0, 10, 0, 0, 10, 10, 0]),
numItems: 18
}
};
indexArray = new Uint32Array([0, 1, 2, 3, 4, 5]);
< !--it works adding the index array with the following line-- >
// buffGeometry.setIndex(new THREE.BufferAttribute(indexArray, 1));
material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var mesh = new THREE.Mesh(buffGeometry, material);
scene.add(mesh);
three.js r77
(Here the complete sample)
Documentation here:
http://threejs.org/docs/index.html#Reference/Core/BufferGeometry
In short, you're not supposed to set the attribute property directly.
Instead, you're supposed to create a THREE.BufferAttribute and then add it to the geometry by calling .addAttribute('position', bufferAttribute)
EDIT: Not sure how setIndex work, does it actually render anything or not crash?
In my case I was receiving this warning when using this code, which was working completely fine:
const curveMesh = new THREE.Mesh()
let curve
allCoords.forEach(coords => {
curve = new Curve(coords, material, step)
curveMesh.add(curve.mesh)
curveMesh.add(curve.meshOrigin)
curveMesh.add(curve.meshDestination)
})
rootMesh.add(curveMesh)
When I replaced it with this line, it started to not see the warning anymore [.WebGL-0x7fe61b026400]RENDER WARNING: Render count or primcount is 0.
// please notice here is now Group!
const curveMesh = new THREE.Group()
let curve
allCoords.forEach(coords => {
curve = new Curve(coords, material, step)
curveMesh.add(curve.mesh)
curveMesh.add(curve.meshOrigin)
curveMesh.add(curve.meshDestination)
})
rootMesh.add(curveMesh)
I am trying to Extrude the Rectangle drawn in the canvas to Three.js canvas.
Here Blue one is 2d canvas drawing and Green is 3d
var Shape = new THREE.Shape();
Shape.moveTo(0,0,0);
for(var i=0;i<=point.length/2;i++)
{
Shape.lineTo(point[i],point[i+1]);
}
var ExtrusionSettings = {
curveSegments: 3,
bevelThickness:0, bevelSize: 0, bevelEnabled: false,
material: 0, extrudeMaterial: 1,amount: 10
};
var Geometry = new THREE.ExtrudeGeometry( Shape, ExtrusionSettings );
var Material = new THREE.MeshLambertMaterial({color: 0xff8800});
Material.side = THREE.DoubleSide;
Mesh = new THREE.Mesh(Geometry,Material);
Mesh.position.set(0,0,0);
Scene.add(Mesh);
The points is passed as an array which contain the x,y coordinates of the lines in the canvas
the points are passed by
function mouseDown(event)
{
Line[0] = event.pageX - this.offsetLeft;
Line[1] = event.pageY - this.offsetTop;
console.log("down");
}
function mouseUp(event)
{
Line[2] = event.pageX - this.offsetLeft;
Line[3] = event.pageY - this.offsetTop;
console.log("up");
var Width = Math.abs(Line[2] - Line[0]);
var Height = Math.abs(Line[3] - Line[1]);
Context.beginPath();
//Context.moveTo(Line[0], Line[1]);
//Context.lineTo(Line[2], Line[3]);
//Context.rect(Line[0],Line[1],Width,Height);
Context.lineWidth="5";
Context.strokeStyle="red";
var L1P1x = Line[0];
var L1P1y = Line[1];
var L1P2x = Line[0]+Width;
var L1p2Y = Line[1];
var L2P1x = Line[0]+Width;
var L2P1y = Line[1];
var L2P2x = Line[2];
var L2P2y = Line[3];
var L3P1x = Line[2];
var L3P1y = Line[3];
var L3P2x = Line[0];
var L3P2y = Line[1]+Height;
var L4P1x = Line[0];
var L4P1y = Line[1]+Height;
var L4P2x = Line[0];
var L4P2y = Line[1];
Context.moveTo(L1P1x,L1P1y);
Context.lineTo(L1P2x,L1p2Y);
Context.moveTo(L2P1x,L2P1y);
Context.lineTo(L2P2x,L2P2y);
Context.moveTo(L3P1x,L3P1y);
Context.lineTo(L3P2x,L3P2y);
Context.moveTo(L4P1x,L4P1y);
Context.lineTo(L4P2x,L4P2y);
Context.stroke();
Points.push(L1P1x,L1P1y,L1P2x,L1p2Y,L2P1x,L2P1y,L2P2x,L2P2y,L3P1x,L3P1y,L3P2x,L3P2y,L4P1x,L4P1y,L4P2x,L4P2y);
addMesh(Points);//points are passes to draw in 3d
//console.log(Points);
}
Not a fan of three.js but looking at your code you have some basic logic errors.
The short answer
Logic errors in the for loop! Change your first snippet to the following. You have Points in the second snippet and Point in the first. I have used point in the fix as I assume that is the correct name for the array of coordinates.
var i, len, mesh; // define all the vars you will use
len = point.length; // get the number of coordinates.
if(len > 1) { // make sure there are points.
shape.moveTo(point[0], point[1]); // move to the first point
for(i = 2; i < len; i += 2) { // iterate other points and lineTo them
shape.lineTo(point[i], point[i + 1]); // add the line.
}
mesh = new THREE.Mesh(
new THREE.ExtrudeGeometry( // Create geom
shape,
{ // extrusion settings.
curveSegments : 3,
bevelThickness : 0,
bevelSize : 0,
bevelEnabled : false,
material : 0,
extrudeMaterial : 1,
amount : 10
}
),
new THREE.MeshLambertMaterial({color: 0xff8800}) // material
);
mesh.position.set(0, 0 0); // position the mesh
scene.add(mesh); // add it to the scene
}
That will fix the bug. It was the for loop that was bad.
The long answer.
You seem to be a beginner so the long answer is to give you some longer term advice. (advice only as there are no rules).
Creating the shape. You had...
var Shape = new THREE.Shape();
Shape.moveTo(0,0,0);
for(var i=0;i<=point.length/2;i++)
{
Shape.lineTo(point[i],point[i+1]);
}
Now with my pedantic eye
Never name vars with a capital. Capitaitals are reserved for named objects. Though in this case you are safe, using the name Shape in a differing scopy could well overwrite the object constructor.
var Shape = new THREE.Shape(); // you had
should be
var shape = new THREE.shape();
Capitals are only for objects that you can use the new token with, Acronyms, or constants. This is not a trivial convention as all of Javascript's inbuilt naming uses it and I have yet to find a popular framework that does not use it. DON'T CAPITALIZE, it is a bad habit in javascript and will cause endless hours looking for simple syntax bugs.
The THREE.Shape object only deals with 2D paths. You had
// 3 coordinates for for a 2D path???
Shape.moveTo(0,0,0); // remove this line it is not needed
The last 0 is ignored, ( I checked the THREE.js source code) and that is not the error.
You error is here in the following.
for(var i=0;i<=point.length/2;i++) // Only half the points ??
{ // you then line to x,y
Shape.lineTo(point[i],point[i+1]);
// Next lineTo will be y,x then x,y messing everything up.
}
point refers the the array of numbers representing the x and y coordinates of the path. It is organised with the x coord, then y then x then y.
The number of items in the array is the number of 2D points * 2. The * 2 is because there is a X and a ``Y for each point.
So need to iterate the point array correctly.
Step by step.
var i, len; // always put your vars declarations at the top
len = point.length; // I like to get the length before the loop.
The for loop needs to step by 2 as the are two entries for each point.
for(i = 0; i < len; i += 2) { //Put the { at the end. Saves space and is easier to read.
Check for the first point as you need to moveTo (note answer is slightly different)
if(i === 0) {
Add the point to the path (shape).
shape.moveTo(point[i], point[i + 1]);
Then the other points
} else {
shape.lineTo(point[i], point[i + 1]);
}
This will create the correct shape that you can then use to extrude.
Put all together as I would do it.
// where do you define scene. It should be lowercase
var i, len, mesh;
len = point.length;
if(len > 1) { // check if there are points (2 or more.
shape.moveTo(point[0], point[1]); // do the first point outside the for loop
// this saves having to do the if statement
// for each point
for(i = 2; i < len; i += 2) { // iterate points starting at the second
shape.moveTo(point[i], point[i + 1]); // add the line.
}
You had
// Such a long name for a one of abd just a mess
// Your code
//var ExtrusionSettings = {
// curveSegments: 3,
// bevelThickness:0, bevelSize: 0, bevelEnabled: false,
// material: 0, extrudeMaterial: 1,amount: 10
//};
// unless it will be used again put it inline
// Bad naming for the rest
// Your code
//var Geometry = new THREE.ExtrudeGeometry( Shape, ExtrusionSettings );
//var Material = new THREE.MeshLambertMaterial({color: 0xff8800});
//Material.side = THREE.DoubleSide;
//Mesh = new THREE.Mesh(Geometry,Material);
//Mesh.position.set(0,0,0);
//Scene.add(Mesh);
Replace it all with
// Material is used once so no need to create var for it
// Removed Material.side = THREE.DoubleSide; // assuming this is debug code only
// you had Mesh without var. That made it global scope. Never use a var without defining it first with the var token
var mesh = new THREE.Mesh( // define and assign mesh
new THREE.ExtrudeGeometry( // Create geom// indent arguments for readability
shape,
{ // extrusion settings.
curveSegments : 3, // line it all up so you can read it quickly
bevelThickness : 0,
bevelSize : 0,
bevelEnabled : false,
material : 0,
extrudeMaterial : 1,
amount : 10
}
),
new THREE.MeshLambertMaterial({color: 0xff8800})
);
mesh.position.set(0,0,0); // position the mesh
scene.add(mesh); // add it to the scene
// note the lowercase scene for the object instance scene. Need to chage that where you create it.
} // end of if(len > 1){
There is a lot of pedantic stuff there but you are clearly new to programming and bad habits are hard to break so start with good ones. Bugs and debugging are the worst part of programming. Debugging is the single most time consuming part of all programming (even for experienced professionals). The single biggest cause of bugs in all programing languages is bad style and or messy code. Writing clean consistent code makes your code easier to read and hence easier to debug. Try finding a missing { in 5000 lines of `{''s or a capital where a lowercase character should be (Hours debugging code while the simple error is right in front of you hidden in a mess can make or break being a coder)
Hope this helped..
I'm using the babylonjs 3D WebGL library.
It's great library, but I can't find the same, which exists in THREE.JS library.
For example, I have 2D polygons in database, I'm fetching the polygon data from it and then create a custom mesh and extruding it.
With the THREE.JS, there isn't any problem, I can add to some array:
...
points.push( new THREE.Vector2( part.x, -part.y ) );
...
var shape = new THREE.Shape( points );
var extrusion = {
amount: building.height,
bevelEnabled: false
};
var geometry = new THREE.ExtrudeGeometry( shape, extrusion );
var mesh = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial({
ambient: 0xbbbbb,
color: 0xff0000
});
...
scene.add( mesh );
It's very simple. How to do the same, I couldn't find.
I've found only some information here:
http://www.html5gamedevs.com/topic/4530-create-a-mesh-from-a-list-of-vertices-and-faces/
http://blogs.msdn.com/b/eternalcoding/archive/2013/06/27/babylon-js-a-complete-javascript-framework-for-building-3d-games-with-html-5-and-webgl.aspx
With such an example (from msdn by Ctrl + F -> You can also create a mesh from a list of vertices and faces):
var plane = new BABYLON.Mesh(name, scene);
var indices = [];
var positions = [];
var normals = [];
var uvs = [];
// Vertices
var halfSize = size / 2.0;
positions.push(-halfSize, -halfSize, 0);
normals.push(0, 0, -1.0);
uvs.push(0.0, 0.0);
positions.push(halfSize, -halfSize, 0);
normals.push(0, 0, -1.0);
uvs.push(1.0, 0.0);
positions.push(halfSize, halfSize, 0);
normals.push(0, 0, -1.0);
uvs.push(1.0, 1.0);
positions.push(-halfSize, halfSize, 0);
normals.push(0, 0, -1.0);
uvs.push(0.0, 1.0);
// Indices
indices.push(0);
indices.push(1);
indices.push(2);
indices.push(0);
indices.push(2);
indices.push(3);
plane.setVerticesData(positions, BABYLON.VertexBuffer.PositionKind);
plane.setVerticesData(normals, BABYLON.VertexBuffer.NormalKind);
plane.setVerticesData(uvs, BABYLON.VertexBuffer.UVKind);
plane.setIndices(indices);
return plane;
But's it's rather not the same as with the THREE.JS. For example I need to count index buffer manually where in THREE.JS I don't need it, also it's a sample with plane only and I didn't find any info about extruding exactly.
So... Maybe, there are some easy ways in BabylonJS?
There is no support for extrusion right now but this could be a great feature to add :) I will definitely add it to our roadmap. If you would like to discuss the issue further please ask on the babylon.js forum.
EDIT:
Have ability to create custom shapes now.
http://doc.babylonjs.com/tutorials/parametric_shapes#extrusion
Update 2019 PolygonMeshBuilder allows to create custom mesh from the collection of vertexes
Please note that the PolygonMeshBuilder uses Earcut, so, in non
playground projects, you will have to add a reference to their cdn or
download their npm package
Add Earcut as dependency in your index HTML
<script src="https://preview.babylonjs.com/earcut.min.js"></script>
Now you can use Pramaetric shapes to extrude polygon and punch holes.
//Polygon shape in XoZ plane
var shape = [
new BABYLON.Vector3(4, 0, -4),
new BABYLON.Vector3(2, 0, 0),
new BABYLON.Vector3(5, 0, 2),
new BABYLON.Vector3(1, 0, 2),
new BABYLON.Vector3(-5, 0, 5),
new BABYLON.Vector3(-3, 0, 1),
new BABYLON.Vector3(-4, 0, -4),
new BABYLON.Vector3(-2, 0, -3),
new BABYLON.Vector3(2, 0, -3)
];
//Holes in XoZ plane
var holes = [];
holes[0] = [ new BABYLON.Vector3(1, 0, -1),
new BABYLON.Vector3(1.5, 0, 0),
new BABYLON.Vector3(1.4, 0, 1),
new BABYLON.Vector3(0.5, 0, 1.5)
];
holes[1] = [ new BABYLON.Vector3(0, 0, -2),
new BABYLON.Vector3(0.5, 0, -1),
new BABYLON.Vector3(0.4, 0, 0),
new BABYLON.Vector3(-1.5, 0, 0.5)
];
var polygon = BABYLON.MeshBuilder.ExtrudePolygon("polygon",{
shape:shape,
holes:holes,
depth: 2,
sideOrientation: BABYLON.Mesh.DOUBLESIDE }, scene);
Result:
See this playground for reference:
https://playground.babylonjs.com/#4G18GY#7
Advance usage
building Staircases:
https://www.babylonjs-playground.com/#RNCYVM#74
If I have a custom plane geometry like so:
planeGeometry.vertices[0] = new THREE.Vector3(0, 10, 0);
planeGeometry.vertices[1] = new THREE.Vector3(10, 10, 0);
planeGeometry.vertices[2] = new THREE.Vector3(0, 0, 0);
planeGeometry.vertices[3] = new THREE.Vector3(10, 0, 0);
// faceUVs, mesh etc omitted
var right = planeGeometry.vertices[0].clone().sub(planeGeometry.vertices[1]);
var up = planeGeometry.vertices[1].clone().sub(planeGeometry.vertices[3]);
var up = up.normalize();
var normal = up.clone().cross(right).normalize();
How can I rotate another Object3D (CSS3DObject in my case) to have this facing normal and up vector? Can I create a quaternion or rotation matrix to do this?