I am currently working on a voxel Minecraft like JavaScript game to improve my JS/TS skills but i am facing an issue.
I draw my voxels by drawing multiple faces of blocks in a BufferGeometry but, between two faces, there is a glitched line like in this image.
Here are some parts of my code that could be usefull to understand where the problem come from:
My material:
const texture = loader.load(this.instance.ressourceMapUrl);
texture.magFilter = THREE.NearestFilter;
texture.minFilter = THREE.NearestFilter;
const material = this.blockMaterial = new THREE.MeshLambertMaterial({
map: texture,
alphaTest: 0.1,
transparent: true,
vertexColors: THREE.VertexColors
});
My BufferGeometry:
const geometry = new THREE.BufferGeometry();
const positionNumComponents = 3;
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
const normalNumComponents = 3;
geometry.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
const uvNumComponents = 2;
geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
geometry.setIndex(indices);
geometry.colors = new Float32Array(colors);
geometry.setAttribute( 'color', new THREE.BufferAttribute( geometry.colors, 3, true) );
geometry.computeBoundingSphere();
A little sample of the vertexes of a normal chunk:
[0, 12, 0, 0, 12, 1, 1, 12, 0, 1, 12, 1, 1, 12, 0, 1, 12, 1, 2, 12, 0, 2, 12, 1, 2, 12, 0, 2, 12, 1, 3, 12, 0, 3, 12, 1, 3, 12, 0, 3, 12, 1, 4, 12, 0, 4, 12, 1, 4, 12, 0, 4, 12, 1, 5, 12, 0, 5, 12...]
Vertexes are used in this order:
let ndx = positions.length/3;
indices.push(
ndx, ndx + 1, ndx + 2,
ndx + 2, ndx + 1, ndx + 3,
);
My far & near variables:
const near = 0.101;
const far = 240
Thank you a lot for reading this, I know it may be an idiot question but I'm struggling on that for a week and can't manage to find any solution on the web.
Have a nice day.
I found the problem and how to solve it :
This was coming from my textures. Since floats are not precise, it was making these lines.
In order to fix this, I moved the corners I used for my texture by 0.0001 to the center.
The coordinates of my textures from my texture map are stored like this : [x1, y1 x2, y2].
I did this:
let uv: [number, number, number, number] = ...;
let correction = uv[0] > uv[2] ? 0.0001 : -0.0001;
uv[0] -= correction;
uv[2] += correction;
correction = uv[1] > uv[3] ? 0.0001 : -0.0001;
uv[1] -= correction;
uv[3] += correction;
Thank you to everyone that tried to help me and I hope it can help some other people as well !
Related
I'm trying to build a Mesh with holes from an original Shape and then an ExtrudedGeometry. The problem is that the holes I add always go trough the whole height of the resulting Mesh. Is there any way to make the holes shorter in height, so in the end these don't go across the whole shape?
Reference code for the shape to extruded:
var heartShape = new THREE.Shape();
heartShape.moveTo( 25, 25 );
heartShape.bezierCurveTo( 25, 25, 20, 0, 0, 0 );
blah
var innerCircle = new THREE.Path();
innerCircle.moveTo(blah);
heartShape.holes.push(innerCircle);
var extrudeSettings = { amount: 8, bevelEnabled: true, bevelSegments: 2, steps: 2, bevelSize: 1, bevelThickness: 1 };
var geometry = new THREE.ExtrudeGeometry( heartShape, extrudeSettings );
var mesh = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial() );
Thanks!
There is no separate argument for hole depth in THREE.ExtrudeGeometry. You will need two separate operations to solve it.
One solution could be to "plug" it with another extrude based on your inner circle path (convert to Shape first).
var extrudeSettingsForPlug = { amount: 4, bevelEnabled: true, bevelSegments: 2, steps: 2, bevelSize: 1, bevelThickness: 1 };
var geometry = new THREE.ExtrudeGeometry( innerCircleShape, extrudeSettingsForPlug );
I need to draw a 3d house model (walls only) from a 2d path or array (explained later) I receive from FabricJS editor I've built. The type of data sent from 2d to 3d views doesn't matter.
My first (and only quite close to what I want to get) attempt was to create the array of 1s and zeros based on the room I want to draw, and then render it in ThreeJS as one cuboid per 'grid'. I based this approach on this ThreeJS game demo. So if the array look like this:
var map = [ //1 2 3 4 5 6 7 8
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1,],
[1, 1, 0, 0, 0, 0, 0, 1, 1, 1,], // 1
[1, 1, 0, 0, 1, 0, 0, 0, 0, 1,], // 2
[1, 0, 0, 0, 1, 1, 0, 0, 0, 1,], // 3
[1, 0, 0, 1, 1, 1, 1, 0, 0, 1,], // 4
[1, 0, 0, 0, 1, 1, 0, 0, 1, 1,], // 5
[1, 1, 1, 0, 0, 0, 0, 1, 1, 1,], // 6
[1, 1, 1, 0, 0, 1, 0, 0, 1, 1,], // 7
[1, 1, 1, 1, 1, 1, 0, 0, 1, 1,], // 8
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1,],
];
I iterate through the array and render one block for every 1, and calculate it's position from indexes from the 2d 'map' (my array).
var UNITSIZE = 250, units = mapW;
for (var i = 0; i < mapW; i++) {
for (var j = 0, m = map[i].length; j < m; j++) {
if (map[i][j]) {
var wall = new t.Mesh(cube, material);
wall.position.x = (i - units/2) * UNITSIZE;
wall.position.y = WALLHEIGHT/2;
wall.position.z = (j - units/2) * UNITSIZE;
scene.add(wall);
}
}
}
It worked great till I wanted to place other models (.obj, but it doesn't matter. Let's call them furniture) near the walls. Each piece of furniture has it's (x=0, y=0, z=0) point in the center of the model, and since walls are cubes (with the same coord system, with 0 point in the center), furniture are rendered in the center of the wall (when we place it in the corner, only 1/4 of the model is visible). This is more/less how it looks like:
(black - how the walls should look like, blue - each cuboid of the wall, red - piece of furniture)
Thats why I would like to render walls as planes, probably from a 2d closed patch (I can export it from Fabric without a problem). I don't need walls to be thick nor to be visible "from behind", when camera moves through the wall. Any clues on how to achieve something like this?
"Help me StackOverflow, your my only hope."
You can manually populate the vertex and face arrays of a THREE.js mesh, so if you can export the closed path you need for example as an array of coordinates, you can iterate over it, and push needed information to your wall object.
Something like this
var coordArray = [...]; //Array of corner points of a closed shape from your source. Here assumed to be THREE.Vector2() for simplicity.
var walls = new THREE.Geometry();
for(var i = 0; i < coordArray.length(); i++){ //iterate over the coordinate array, pushing vertices to the geometry
var coordinates = coordArray[i];
walls.vertices.push(new THREE.Vector3(coordinates.x, coordinates.y, 0)); //vertex at floor level
walls.vertices.push(new THREE.Vector3(coordinates.x, coordinates.y, 10)); //vertex at the top part of the wall, directly above the last
}
var previousVertexIndex = walls.vertices.length - 2; // index of the vertex at the bottom of the wall, in the segment we are creating faces for
for(var i = 0; i < walls.vertices.length; i += 2){
walls.faces.push(new THREE.Face3(i, i + 1, previousVertexIndex));
walls.faces.push(new THREE.Face3(i + 1, previousVertexIndex + 1, previousVertexIndex));
previousVertexIndex = i;
}
walls.computeVertexNormals();
walls.computeFaceNormals();
scene.add(new THREE.Mesh(walls, new THREE.MeshLambertMaterial());
I'm trying to implement Stemkoskis excellent particle engine (http://stemkoski.github.io/Three.js/Particle-Engine.html) but with multiple instances of his "class". But the problem is that when adding multiple instances, all other instances gets the last added instances properties regarding tween size (sizeTween)
Here is his source:
http://stemkoski.github.io/Three.js/js/ParticleEngine.js
And instantiation:
http://stemkoski.github.io/Three.js/js/ParticleEngineExamples.js
I've tried to google up my knowledge about so called "classes" in javascript and all seems to make sense with the Tween instantiation class and all "classes" are using the 'this' pointer. But still it doesn't make sense that I cannot instantiate two different objects. I wonder if it has something to do with the shaders, but that doesn't make sense either since I can instantiate two different types of particles but it seems to be the Tween that stays the same.
I just wonder if someone can give me a hint if there are some issues with his code that makes multiple instances fail to be unique? (I've tried with Fireball and Snow (in the demo).
Any hint would be great. Spent 8 hours today and I still don't get it.
Here is my code where I use the code from ParticleEngine.js file.
// Clouds
var cloud = new Cloud();
cloud.Create(0,0,0, 4, scene);
objects.push(cloud); // this makes the Draw function gets called in each instance
// Sun
var sun = new Sun();
sun.Create(0,200,0, scene);
objects.push(sun); // this makes the Draw function gets called in each instance
// My own classes
// Baseclass
function Object3D() {
this.mesh;
Object3D.prototype.GetObject = function() {
return this.mesh;
};
Object3D.prototype.Draw = function() {
//draw object
};
}
// Class that creates the snow
function Cloud() {
Object3D.call(this);
Cloud.prototype.Create = function(x ,y ,z, s, scene) {
var engine = new ParticleEngine();
engine.setValues(
{positionStyle : Type.CUBE,
positionBase : new THREE.Vector3( 0, 0, 0 ),
positionSpread : new THREE.Vector3( 200, 0, 200 ),
positionRadius : 0.1,
velocityStyle : Type.CUBE,
velocityBase : new THREE.Vector3( 0, -300, 0 ),
velocitySpread : new THREE.Vector3( 150, 20, 150 ),
accelerationBase : new THREE.Vector3( 0, -5,0 ),
sizeTween : new Tween( [0, 0.25], [1, 10] ),
colorBase : new THREE.Vector3(0.66, 1.0, 0.9), // H,S,L
opacityTween : new Tween( [2, 3], [0.8, 0] ),
blendStyle : THREE.AdditiveBlending,
angleBase : 0,
angleSpread : 720,
angleVelocityBase : 0,
angleVelocitySpread : 60,
particleTexture : THREE.ImageUtils.loadTexture( 'textures/snowflake.png' ),
particlesPerSecond : Math.random()*50+100,
particleDeathAge : 10.5,
// emitterDeathAge : 60
});
engine.initialize();
this.engine = engine;
};
Cloud.prototype.Draw = function(time) {
this.engine.update(time * 0.00005);
};
}
Cloud.prototype = new Object3D();
Cloud.prototype.constructor = Cloud;
// Class that creates the fireball effect
function Sun() {
Object3D.call(this);
Sun.prototype.Create = function(x, y, z, scene) {
var sunEngine = new ParticleEngine();
sunEngine.setValues(
{
positionStyle : Type.SPHERE,
positionBase : new THREE.Vector3(0, 200, 0),
positionRadius : 2,
sizeTween : new Tween( [0, 0.4], [1, 150] ),
opacityTween : new Tween( [0.7, 1], [1, 0] ),
colorBase : new THREE.Vector3(0.02, 1, 0.4),
blendStyle : THREE.AdditiveBlending,
velocityStyle : Type.SPHERE,
speedBase : 40,
speedSpread : 8,
particleTexture : THREE.ImageUtils.loadTexture( 'textures/smokeparticle.png' ),
particlesPerSecond : 60,
particleDeathAge: 1.5,
//emitterDeathAge : 60
});
sunEngine.initialize();
this.sunEngine = sunEngine;
};
Sun.prototype.Draw = function(time) {
this.sunEngine.update(time * 0.00005 );
};
}
Sun.prototype = new Object3D();
Sun.prototype.constructor = Sun;
In the first instance (Cloud/Snow) I set:
sizeTween : new Tween( [0, 0.25], [1, 10] ),
And then I initiate the "Sun" class with this property:
sizeTween : new Tween( [0, 0.4], [1, 150] ),
And the clouds, initiated first, gets the same values for sizeTween as the last added one. And this is the core problem I've got.
I am trying to create a house geometry and attach different textures to the faces of the geometry. I am using r55. The problem is that faces with material created from a texture just don't appear. Faces with a simple color material do however appear. If I replace the material generated from my roofTexture with a simple color material, the faces using that material do appear correctly as well.
Here is the relevant part of my code:
var geom = new THREE.Geometry();
// Load the roof texture
var roofTexture = new THREE.ImageUtils.loadTexture('gfx/textures/roof.jpg');
// Let the roof texture repeat itself
roofTexture.wrapS = roofTexture.wrapT = THREE.RepeatWrapping;
roofTexture.repeat.set(10, 10);
// Materials
var materialArray = [];
materialArray.push(new THREE.MeshLambertMaterial({color: 0xD3E3F0 }));
materialArray.push(new THREE.MeshLambertMaterial({map: roofTexture}));
// Base edges
var edge0 = new THREE.Vector2(obj.ridgeLength/2, -obj.buildingDepth/2);
var edge1 = new THREE.Vector2(obj.ridgeLength/2, obj.buildingDepth/2);
var edge2 = new THREE.Vector2(-obj.ridgeLength/2, obj.buildingDepth/2);
var edge3 = new THREE.Vector2(-obj.ridgeLength/2, -obj.buildingDepth/2);
// Floor
geom.vertices.push(new THREE.Vector3(edge0.x, -1, edge0.y));
geom.vertices.push(new THREE.Vector3(edge1.x, -1, edge1.y));
geom.vertices.push(new THREE.Vector3(edge2.x, -1, edge2.y));
geom.vertices.push(new THREE.Vector3(edge3.x, -1, edge3.y));
// Eave
geom.vertices.push(new THREE.Vector3(edge0.x, obj.eaveHeight, edge0.y));
geom.vertices.push(new THREE.Vector3(edge1.x, obj.eaveHeight, edge1.y));
geom.vertices.push(new THREE.Vector3(edge2.x, obj.eaveHeight, edge2.y));
geom.vertices.push(new THREE.Vector3(edge3.x, obj.eaveHeight, edge3.y));
// Ridge
geom.vertices.push(new THREE.Vector3(obj.ridgeLength/2, obj.ridgeHeight, 0));
geom.vertices.push(new THREE.Vector3(-obj.ridgeLength/2, obj.ridgeHeight, 0));
// Ground
geom.faces.push( new THREE.Face4(0, 0, 0, 0) );
// Front
geom.faces.push( new THREE.Face4(0, 3, 7, 4) );
// Left side
geom.faces.push( new THREE.Face4(0, 4, 5, 1) );
// Back
geom.faces.push( new THREE.Face4(1, 5, 6, 2) );
// Right side
geom.faces.push( new THREE.Face4(2, 6, 7, 3) );
// Left triangle
geom.faces.push( new THREE.Face3(4, 8, 5));
// Right triangle
geom.faces.push( new THREE.Face3(6, 9, 7));
// Front roof
geom.faces.push( new THREE.Face4(7, 9, 8, 4) );
// Back roof
geom.faces.push( new THREE.Face4(5, 8, 9, 6) );
// Assign materials to the faces
geom.faces[0].materialIndex = 0;
geom.faces[1].materialIndex = 0;
geom.faces[2].materialIndex = 0;
geom.faces[3].materialIndex = 0;
geom.faces[4].materialIndex = 0;
geom.faces[5].materialIndex = 0;
geom.faces[6].materialIndex = 0;
geom.faces[7].materialIndex = 1;
geom.faces[8].materialIndex = 1;
geom.computeFaceNormals();
obj.house = new THREE.Mesh( geom, new THREE.MeshFaceMaterial(materialArray) );
obj.house.doubleSided = true;
obj.house.castShadow = true;
obj.sun.shadowDarkness = 1.0;
obj.scene.add(obj.house);
What am I doing wrong?
You are missing UV coordinates on your geometry. UV coords go from 0 to 1 so since you are creating the geometry yourself you can assign at your lower left corner UVs (0.0, 0.0) on your lower right (1.0, 0.0), top left (0.0, 1.0) and top right (1.0, 1.0). You can look at the PlaneGeometry.js file to see how UVs are assigned.
I'm having an issue, how can I obtain a kind of "open ring" like the torus but squared?
I tried with a shape plus a path as a hole:
var arcShape = new THREE.Shape();
arcShape.moveTo( 40, 0 );
arcShape.arc( 0, 0, 40, 0, 2*Math.PI, false );
var holePath = new THREE.Path();
holePath.moveTo( 30,0 )
holePath.arc( 0, 0, 30, 0, 2*Math.PI, true );
And until now, making a mesh:
new THREE.Mesh( arcShape.extrude({ amount: 5, bevelEnabled: false }), MATERIAL );
it works, but how to make a middle ring? I mean, with:
var arcShape = new THREE.Shape();
arcShape.moveTo( 40, 0 );
arcShape.arc( 0, 0, 40, 0, Math.PI, false );
var holePath = new THREE.Path();
holePath.moveTo( 30,0 );
holePath.arc( 0, 0, 30, 0, Math.PI, true );
It works, but it remains a subtle face between the terminal parts... is there a way to make it completely open?
Rather than start from square one, try changing the parameters in the Torus geometry constructor:
// Torus geometry parameters:
// radius of entire torus,
// diameter of tube (should be less than total radius),
// segments around radius,
// segments around torus ("sides")
var torusGeom = new THREE.TorusGeometry( 25, 10, 4, 4 );