I create a closed spline and extrude geometry with rectangle shape to create a track for my game. I use spline to calculate the position of my ship, but when I check, the spline does not match completely with the geometry. Is there something wrong?
shape used to extrude example
extrude geometry and spline not match
Here is my code:
var genTrack = function (lanes, controlPoints, material, isClose) {
//extrude setting
var extrudeSettings = {
bevelEnabled: true,
bevelSegments: 2,
steps: 200,
material: 1,
extrudeMaterial: 1
};
//track shape
var trackShape = new THREE.Shape([
new THREE.Vector2(-config.laneHeight/2, config.laneWidth/2),
new THREE.Vector2(-config.laneHeight/2, - config.laneWidth/2),
new THREE.Vector2(config.laneHeight/2, - config.laneWidth/2),
new THREE.Vector2(config.laneHeight/2, config.laneWidth/2),
]);
//spline
if (isClose)
var spline = new THREE.ClosedSplineCurve3(controlPoints);
else
var spline = new THREE.SplineCurve3(controlPoints);
utils.public('spline', spline);
//set path
extrudeSettings.extrudePath = spline;
//frenet
var frenet = new THREE.TubeGeometry.FrenetFrames(spline, 200, false)
extrudeSettings.frames = frenet;
utils.public('extrudeSettings', extrudeSettings);
//create geometry
var geometry = new THREE.ExtrudeGeometry(trackShape, extrudeSettings);
var splineGeo = new THREE.TubeGeometry(spline, 200, 1, 4, true, false);
utils.public('frenet', frenet);
var angle = 0;
var track = new THREE.Object3D();
track.matrixAutoUpdate = false;
track.receiveShadow = true;
for (var i=0; i< lanes; i++){
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(i * (config.laneSpan + config.laneWidth), -30, 0);
//mesh.rotation.set(0, 0,angle);
//angle += Math.PI/180 * 20;
track.add(mesh);
}
track.scale.set(1.5, 1.5, 1.5);
var splineMesh = new THREE.Mesh(splineGeo, new THREE.MeshBasicMaterial({color: 0xffffff}));
return {
'geometry': geometry,
'mesh': track,
'spline': spline,
'frenet': frenet,
'splineMesh': splineMesh,
'controlPoints' : controlPoints
};
}
I believe your problem is with this line:
mesh.position.set(i * (config.laneSpan + config.laneWidth), -30, 0);
I think what you want is something more like:
mesh.position.set(i * config.laneWidth / lanes, config.laneHeight / 2, 0);
I haven't tested it, but I think you need something like this.
Related
Im trying to add two textures. One for the wall and another for the floor. After rendering Im just getting a solid color instead of the texture.
Here is my scene & camera configuration:
const tempScene = new THREE.Scene();
tempScene.background = new THREE.Color(0x021D49);
const ambient = new THREE.AmbientLight(0xffffff, 0.6);
tempScene.add(ambient);
const directionalLight = new THREE.DirectionalLight(0xfafafa, 0.6);
directionalLight.position.set(0, 300, 300);
tempScene.add(directionalLight);
const tempCamera = new THREE.PerspectiveCamera(
60,
el.clientWidth / el.clientHeight,
0.1,
10000,
);
tempCamera.up.set(0, 0, 1);
tempCamera.position.set(0, 300, 300);
tempCamera.lookAt(0, 0, 0);
const tempControls = new OrbitControls(tempCamera, el);
tempControls.maxPolarAngle = Math.PI / 2;
tempControls.minDistance = 10;
tempControls.maxDistance = 500;
tempControls.enablePan = false;
const tempRenderer = new THREE.WebGLRenderer({ canvas: el, antialias: true });
tempRenderer.setSize(el.clientWidth, el.clientHeight);
tempRenderer.setPixelRatio(window.devicePixelRatio);
tempRenderer.shadowMap.enabled = true;
tempRenderer.shadowMap.type = THREE.PCFSoftShadowMap;
tempRenderer.outputEncoding = THREE.sRGBEncoding;
Here is the code for texture:
const floorTex = new THREE.TextureLoader().load(floorTexture);
const wallMaterial = new THREE.MeshBasicMaterial({ map: wallTex, side: THREE.DoubleSide });
const floorMaterial = new THREE.MeshStandardMaterial({ map: floorTex });
const lineMaterial = new THREE.LineBasicMaterial({ color: 0x000000 });
const wallGeometry = new THREE.BufferGeometry();
const wallVertices = new Float32Array([
-x1,
y1,
0, // vertex 1
-x1,
y1,
10, // vertex 2
-x2,
y2,
0, // vertex 3
-x2,
y2,
10, // vertex 4
]);
wallGeometry.setAttribute('position', new THREE.BufferAttribute(wallVertices, 3));
wallGeometry.setIndex([0, 2, 1, 2, 3, 1]);
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
const geo = new THREE.EdgesGeometry(wall.geometry);
const wireframe = new THREE.LineSegments(geo, lineMaterial);
wall.add(wireframe);
wall.castShadow = true;
wall.receiveShadow = true;
scene!.add(wall);
const floorGeometry = new THREE.Shape(floorPoints.map((point) => new THREE.Vector2(point[0], point[1])));
const shapeGeometry = new THREE.ShapeGeometry(floorGeometry);
const floor = new THREE.Mesh(shapeGeometry, floorMaterial);
floor.receiveShadow = true;
scene!.add(floor);
This is how it looks
But in real the texture look like this:
floorTexture
wallTexture
Your wall does not get a texture applied since you define no texture coordinates for wallGeometry. Next to position you need a uv buffer attribute holding these data.
Unfortunately, the code snippet does not provide enough information to investigate why the floor has no texture. Instances of ShapeGeometry do have a texture coordinates so I could imagine there is a loading issue with your texture.
BTW: If you add tempRenderer.outputEncoding = THREE.sRGBEncoding; to your code, you have to set the encoding property of all color textures to THREE.sRGBEncoding as well. Otherwise your sRGB workflow is incomplete.
I am developing a web application to estimate the cost of a 3D printing model with three.js and also do other stuff.
I have easily calculate bounding box and volume of object and I am now approaching slices.
Following this question I have managed to get intersections with a plane: Three JS - Find all points where a mesh intersects a plane and I put the function inside a for loop that appends to a three.js group.
// Slices
function drawIntersectionPoints() {
var contours = new THREE.Group();
for(i=0;i<10;i++){
a = new THREE.Vector3(),
b = new THREE.Vector3(),
c = new THREE.Vector3();
planePointA = new THREE.Vector3(),
planePointB = new THREE.Vector3(),
planePointC = new THREE.Vector3();
lineAB = new THREE.Line3(),
lineBC = new THREE.Line3(),
lineCA = new THREE.Line3();
var planeGeom = new THREE.PlaneGeometry(50,50);
planeGeom.rotateX(-Math.PI / 2);
var plane = new THREE.Mesh(planeGeom, new THREE.MeshBasicMaterial({
color: "lightgray",
transparent: true,
opacity: 0.75,
side: THREE.DoubleSide
}));
plane.position.y = i;
scene.add(plane);
var mathPlane = new THREE.Plane();
plane.localToWorld(planePointA.copy(plane.geometry.vertices[plane.geometry.faces[0].a]));
plane.localToWorld(planePointB.copy(plane.geometry.vertices[plane.geometry.faces[0].b]));
plane.localToWorld(planePointC.copy(plane.geometry.vertices[plane.geometry.faces[0].c]));
mathPlane.setFromCoplanarPoints(planePointA, planePointB, planePointC);
meshGeometry.faces.forEach(function(face) {
mesh.localToWorld(a.copy(meshGeometry.vertices[face.a]));
mesh.localToWorld(b.copy(meshGeometry.vertices[face.b]));
mesh.localToWorld(c.copy(meshGeometry.vertices[face.c]));
lineAB = new THREE.Line3(a, b);
lineBC = new THREE.Line3(b, c);
lineCA = new THREE.Line3(c, a);
setPointOfIntersection(lineAB, mathPlane);
setPointOfIntersection(lineBC, mathPlane);
setPointOfIntersection(lineCA, mathPlane);
});
var lines = new THREE.LineSegments(pointsOfIntersection, new THREE.LineBasicMaterial({
color: 0xbc4e9c,
lineWidth: 2,
}));
contours.add(lines);
function setPointOfIntersection(line, plane) {
pointOfIntersection = plane.intersectLine(line);
if (pointOfIntersection) {
pointsOfIntersection.vertices.push(pointOfIntersection.clone());
};
};
};
console.log(contours);
scene.add(contours);
And this is what I see:
The for loop works and I visualise all the planes and also the lines inside the group but only the first intersection is showing in the canvas (pink).
screenshot
Many thanks.
You have to call .updateMatrixWorld(true) on your plane object after you set its y-coordinate in the loop:
plane.position.y = i;
plane.updateMatrixWorld(true);
scene.add(plane);
jsfiddle example.
Working off the "Basic Scene" example on babylonjs-playground.com here, I am trying to do a simple modification on the color of the sphere.
Here is my attempt, which can be run interactively:
https://www.babylonjs-playground.com/#95BNBS
Here is the code:
var createScene = function () {
// The original example, without comments:
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);
var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.7;
var sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene);
sphere.position.y = 1;
var ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene);
// My attempt to color the sphere
var material = new BABYLON.StandardMaterial(scene);
material.alpha = 1;
material.diffuseColor = new BABYLON.Color3(1,0,0);
scene.material = material;
return scene;
};
My attempt to add the colored material to the sphere has no effect.
I also tried to look for color-related attributes on the sphere object:
Object.keys(sphere).filter((key) => return key.includes("Color") )
// => "outlineColor", "overlayColor", "_useVertexColors", "edgesColor"
Except for _useVertexColors, all of these seem to be color objects, but changing them has no effect:
sphere.overlayColor.g = 1;
sphere.outlineColor.g = 1;
sphere.edgesColor.g = 1;
You're pretty close. You are setting a color correctly with diffuseColor but you aren't actually adding it specifically to your sphere.
Your sphere object is stored in sphere so what you need to do is you need to set the material you created on sphere and not on scene.
// My attempt to color the sphere
var material = new BABYLON.StandardMaterial(scene);
material.alpha = 1;
material.diffuseColor = new BABYLON.Color3(1.0, 0.2, 0.7);
sphere.material = material; // <--
See this tutorial
I have a mesh defined which is at the origin.
var textureCanvas = new THREE.CanvasTexture( imageCanvas );
textureCanvas.repeat.set( 4, 4 );
textureCanvas.wrapS = THREE.RepeatWrapping;
textureCanvas.wrapT = THREE.RepeatWrapping;
var materialCanvas = new THREE.MeshBasicMaterial( { map: textureCanvas }
);
var geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3(-4,-4,0),
new THREE.Vector3(-4,4,0),
new THREE.Vector3(4,4,0),
new THREE.Vector3(4,-4,0),
);
geometry.faces.push(
new THREE.Face3(3, 1, 0),
new THREE.Face3(3, 2, 1)
);
var vertexMappings = [];
vertexMappings[0] = new THREE.Vector2(0,1);
vertexMappings[1] = new THREE.Vector2(0,0);
vertexMappings[2] = new THREE.Vector2(1,0);
vertexMappings[3] = new THREE.Vector2(1,1);
var vm = vertexMappings;
geometry.faceVertexUvs[ 0 ] = [];
geometry.faceVertexUvs[0][0] = [ vm[3], vm[1], vm[0] ];
geometry.faceVertexUvs[0][1] = [ vm[3], vm[2], vm[1] ];
meshCanvas = new THREE.Mesh( geometry, materialCanvas );
meshCanvas.rotation.x = - Math.PI / 3;
meshCanvas.rotation.z = - Math.PI / 2.5;
meshCanvas.scale.set( 80, 80, 80 );
scene.add( meshCanvas );
I also have a line which goes through the mesh. It was originally passing through the origin but I moved it a bit (See github issue below).
var linegeo = new THREE.Geometry();
linegeo.vertices.push(
new THREE.Vector3(55, 300, 0),
new THREE.Vector3(-10, -300, 32)
);
scene.add(linemesh);
I want to get the position where the line intersects the mesh, but the intersections result is always empty:
getInersectionPosition(linemesh, meshCanvas);
function getInersectionPosition(linemesh, meshCanvas) {
linemesh.updateMatrixWorld();
meshCanvas.updateMatrixWorld();
var p1 = linemesh.geometry.vertices[0].clone(),
p2 = linemesh.geometry.vertices[1].clone();
p1.applyMatrix4(linemesh.matrixWorld);
p2.applyMatrix4(linemesh.matrixWorld);
//console.log(`p1: ${JSON.stringify(p1)}, p2: ${JSON.stringify(p2)}`);
//console.log(`canvas position: ${JSON.stringify(meshCanvas.position)}`);
var raycaster = new THREE.Raycaster(p1, p2);
raycaster.linePrecision = 10;
//var intersections = raycaster.intersectObjects([meshCanvas]);
var intersections = raycaster.intersectObjects(scene.children, true);
if (intersections.length > 0)
console.log(`intersections: ${ intersections.length}`);
}
Full sample: https://jsfiddle.net/mribbons/103wwsda/
Is it possible that I have this issue?
https://github.com/mrdoob/three.js/issues/11449
The raycaster.intersectObjects() call seems to fail here, with very large values for distance (2.7 million or so, while sphere.radius is ~450).
https://github.com/mrdoob/three.js/blob/dev/build/three.js#L9761
intersectsSphere: function ( sphere ) {
var distance = this.distanceToPoint( sphere.center )
return distance <= sphere.radius;
},
My mistake, raycaster expects a point and a normal, this resolves the issue:
var normal = new THREE.Vector3();
normal.subVectors(p2, p1).normalize();
var raycaster = new THREE.Raycaster(p1, normal);
var intersections = raycaster.intersectObjects(scene.children, true);
if (intersections.length > 0)
console.log(`intersections: ${ intersections.length}`); // outputs "intersections: 2"
I am trying to create spikes on earth(sphere geometry). Though everything works fines, but spikes dont align with globe. I want spike to align something like below image. But my spikes dont lookAt(new THREE.Vector3(0,0,0)) despite mentioned. Please help me out.
I purposefully mentioned code required for debugging. Let me know if you need more code for this. Below image is how i want my spikes to align with sphere.
But this is how it looks
My Main JS initialization file.
$(document).ready(function () {
// Initializing Camera
Influx.Camera = new Influx.Camera({
fov: 60,
aspectRatio: window.innerWidth / window.innerHeight,
near: 1,
far: 1000,
position: {
x: 0,
y: 0,
z: 750
}
});
//Initializing Scene
Influx.Scene = new Influx.Scene();
// Initializing renderer
Influx.Renderer = new Influx.Renderer({
clearColor: 0x000000,
size: {
width: window.innerWidth,
height: window.innerHeight
}
});
Influx.Globe = new Influx.Globe({
radius: 300,
width: 50,
height: 50
});
//
Influx.Stars = new Influx.Stars({
particleCount: 15000,
particle: {
color: 0xFFFFFF,
size: 1
}
});
Influx.moveTracker = new Influx.moveTracker();
Influx.EventListener = new Influx.EventListener();
(function animate() {
requestAnimationFrame( animate );
render();
controls.update();
})();
function render() {
camera.lookAt(scene.position);
group.rotation.y -= 0.001;
renderer.render( scene, camera );
};
});
Below is code responsible for generating spikes on Globe.
Influx.Spikes = function (lat, long) {
// convert the positions from a lat, lon to a position on a sphere.
var latLongToVector3 = function(lat, lon, RADIUS, heigth) {
var phi = (lat) * Math.PI/180,
theta = (lon-180) * Math.PI/180;
var x = -(RADIUS+heigth) * Math.cos(phi) * Math.cos(theta),
y = (RADIUS+heigth) * Math.sin(phi),
z = (RADIUS+heigth) * Math.cos(phi) * Math.sin(theta);
return new THREE.Vector3(x, y, z);
};
var geom = new THREE.Geometry();
var BoxGeometry = new THREE.BoxGeometry(1, 100, 1);
//iterates through the data points and makes boxes with the coordinates
var position = latLongToVector3(lat, long, 300, 2);
var box = new THREE.Mesh( BoxGeometry );
//each position axis needs to be set separately, otherwise the box
//will instantiate at (0,0,0)
box.position.x = position.x;
box.position.y = position.y;
box.position.z = position.z;
box.lookAt(new THREE.Vector3(0, 0, 0));
box.updateMatrix();
//merges the geometry to speed up rendering time, don't use THREE.GeometryUtils.merge because it's deprecated
geom.merge(box.geometry, box.matrix);
var total = new THREE.Mesh(geom, new THREE.MeshBasicMaterial({
color: getRandomColor(),
morphTargets: true
}));
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
//add boxes to the group
group.add(total);
scene.add(group);
};
Influx.Camera = function(params = {}) {
if ( !$.isEmptyObject(params) ) {
window.camera = new THREE.PerspectiveCamera(params.fov, params.aspectRatio, params.near, params.far);
camera.position.set(params.position.x, params.position.y, params.position.z);
camera.lookAt(new THREE.Vector3(0,0,0));
} else {
console.log("Trouble with Initializing Camera");
return;
}
};
Remember that lookAt takes a direction vector, you give to this method the vector (0, 0, 0), this is actually not a normalized direction vector. So you must calculate the direction:
from your box position to the center of the sphere AND normalize it.
var dir = box.position.sub(world.position).normalize();
box.lookAt(dir);
And now just a set of code good conventions that may help you:
var BoxGeometry = new THREE.BoxGeometry(1, 100, 1);
Here I would rather use another var name for the box geometry, not to mix up with the "class" definition from THREE and to follow naming conventions:
var boxGeometry = new THREE.BoxGeometry(1, 100, 1);
And here:
box.position.x = position.x;
box.position.y = position.y;
box.position.z = position.z;
You can just set:
box.position.copy(position);
I also meet this problem, and I fixed it, the solution is: box.lookAt(new THREE.Vector3(0, 0, 0)) must after box.scale.z = xxxx