@prisoner849, I have created a codepen here - code pen
I am trying to do what you have done here (shape with colored sides) , but I am getting a weird cube with weird textures and the orientation of the canvas is skewed. What I want to know is how have you calculated the positions to place the canvas in and how can I do it with my cube ?
Modified: https://codepen.io/prisoner849/pen/qBwRdQm
Used MeshLambertMaterial
instead of MeshBasicMaterial
.
One cubeās side has four vertices, so you need to provide four pairs of UV data per side.
The dice of mine:
This is what I ended up with
@prisoner849 How are you calculating the base uvs values ? Could please share your process?
Everything is in this post: How to put numbers to the sides of a cube and later get which number landed on top - #22 by prisoner849
Have you had a look at the codepen provided there?
@prisoner849 , Yes I have looked at it, but I am having trouble making a tetrahedron dice. I have mapped the text to the sides but for some reason the numbers on some sides are rotated, I have used similar base UV mapping as yours but I dont understand why the numbers on that side are rotated?
https://codepen.io/Samarth-Bagga/pen/abxpzPe
This is what I achieved, using this snippet instead of the block of three fillText
methods:
let aStep = Math.PI * 2 / 3;
let yAlign = Math.PI * 0.5;
let tileQuarter = tileSize * 0.25;
for(let j = 0; j < 3; j++){
ctx.save();
ctx.translate(
(u + 0.5) * tileSize + Math.cos(j * aStep - yAlign) * tileQuarter,
c.height - (v + 0.5) * tileSize + Math.sin(j * aStep - yAlign) * tileQuarter
);
ctx.rotate(j * Math.PI * 2 / 3);
ctx.fillText(arrOfNums[i][j], 0, 0);
ctx.restore();
}
thank you !!! this has helped a lot .
@prisoner849, I am trying to get the side which landed on the top for the octahedron and tetrahedron dice but the quaternion values for the sides landed on the top are not constant for these. I used the quaternion values to get the side which landed on the top for the cube like this and it works almost well -
if (Math.abs(body.quaternion.y) < 0.00001) {
lastRoll += " 4 +";
presentScore += 4;
}
if (Math.abs(body.quaternion.x) < 0.00001) {
lastRoll += " 3 +";
presentScore += 3;
}
if (
body.quaternion.x.toString().substring(0, 5) ==
body.quaternion.y.toString().substring(1, 6)
) {
lastRoll += " 2 +";
presentScore += 2;
}
if (
body.quaternion.x.toString().substring(0, 5) ==
body.quaternion.y.toString().substring(0, 5)
) {
lastRoll += " 1 +";
presentScore += 1;
}
if (
body.quaternion.z.toString().substring(0, 5) ==
body.quaternion.y.toString().substring(1, 6)
) {
lastRoll += " 6 +";
presentScore += 6;
}
if (
body.quaternion.z.toString().substring(0, 5) ==
body.quaternion.y.toString().substring(0, 5)
) {
lastRoll += " 5 +";
presentScore += 5;
}
But the quaternion values for the octahedron and tetrahedron are not constant for the numbers landed on top like the cube dice. What can I do to achieve this?
I am in search for this solution . Best way is to find info from physics body rotation (quaternion) not from visual(threejs or any other) part.
Looks like perfect solution. Big respect !
Note : For one face top case i have no pass any of 1,2,3,4,5 and 6 .
I solve this problem by waiting for the dice body to fall asleepā¦
Then having an array of normals, one for each faceā¦ I transform the normal to worldspace, and the one with the largest up (y) value is the most up facing face.
I either hand code that array or pull the normals from the geometry, and roll the dice until iāve seen every up face, and then mark which normal that corresponds to.
Hey guys, saw this thread and wanted to point to this solution, including code in a GitHub: Three.js Dice Roller | Codrops
I think they create the dots on the dice in createBoxGeometry() with
if (position.y === 0.5) {
position.y -= notch([position.x, position.z]);
} else if (position.x === 0.5) {
position.x -= notch([position.y + offset, position.z + offset]);
position.x -= notch([position.y - offset, position.z - offset]);
} else if (position.z === 0.5) {
position.z -= notch([position.x - offset, position.y + offset]);
position.z -= notch([position.x, position.y]);
position.z -= notch([position.x + offset, position.y - offset]);
} else if (position.z === -0.5) {
position.z += notch([position.x + offset, position.y + offset]);
position.z += notch([position.x + offset, position.y - offset]);
position.z += notch([position.x - offset, position.y + offset]);
position.z += notch([position.x - offset, position.y - offset]);
} else if (position.x === -0.5) {
position.x += notch([position.y + offset, position.z + offset]);
position.x += notch([position.y + offset, position.z - offset]);
position.x += notch([position.y, position.z]);
position.x += notch([position.y - offset, position.z + offset]);
position.x += notch([position.y - offset, position.z - offset]);
} else if (position.y === -0.5) {
position.y += notch([position.x + offset, position.z + offset]);
position.y += notch([position.x + offset, position.z]);
position.y += notch([position.x + offset, position.z - offset]);
position.y += notch([position.x - offset, position.z + offset]);
position.y += notch([position.x - offset, position.z]);
position.y += notch([position.x - offset, position.z - offset]);
}
@prisoner849 ,
I am using the same method you used in the cube to create a decahedron dice ( pentagonal trapezhedron) but for some reason my dice is looking like this -
Am i doing something wrong -
let g = decaGeometry;
let tileDimension = new THREE.Vector2(2, 5);
let tileSize = 512;
let c = document.createElement("canvas");
c.width = tileSize * tileDimension.x;
c.height = tileSize * tileDimension.y;
let ctx = c.getContext("2d");
ctx.fillStyle = tempFillColor;
ctx.fillRect(0, 0, c.width, c.height);
let uvs = [];
let baseUVs = [
new THREE.Vector2(0.5, 0), // bt\
new THREE.Vector2(1, 0.67), // br
new THREE.Vector2(0.5, 1), // tl
new THREE.Vector2(0, 0.67), // bl
];
for (let i = 0; i < 11; i++) {
let u = i % tileDimension.x;
let v = Math.floor(i / tileDimension.x);
uvs.push(
(baseUVs[0].x + u) / tileDimension.x,
(baseUVs[0].y + v) / tileDimension.y,
(baseUVs[1].x + u) / tileDimension.x,
(baseUVs[1].y + v) / tileDimension.y,
(baseUVs[2].x + u) / tileDimension.x,
(baseUVs[2].y + v) / tileDimension.y,
(baseUVs[3].x + u) / tileDimension.x,
(baseUVs[3].y + v) / tileDimension.y
);
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = `bold 175px Arial`;
ctx.fillStyle = tempTextColor;
let text = i + 1;
if (i === 5) {
text += ".";
}
ctx.fillText(
text,
(u + 0.5) * tileSize,
c.height - (v + 0.5) * tileSize
);
}
g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
let tex = new THREE.CanvasTexture(c);
tex.colorSpace = THREE.SRGBColorSpace;
let m = new THREE.MeshPhongMaterial({
map: tex,
});
decahedron = new THREE.Mesh(g, m);
If you want to have full control, then do everyting yourself, donāt rely on helper classes (PolyhedronGeometry
in your case).
I created only one geometry for a side, then cloned it ten times, recomputing UVs for each side (which is not that tricky, when you know coords of sideās vertices), then put all the clones in an array and merged them with mergeGeometries()
.
And I got this:
@prisoner849 , I did like you advised, but I am getting somehting weird -
const baseGeometry = new THREE.BufferGeometry();
const vertices = new Float32Array(verticesGeo);
const indices = new Uint16Array(facesGeo);
baseGeometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
baseGeometry.setIndex(new THREE.BufferAttribute(indices, 1));
baseGeometry.computeVertexNormals();
let tileDimension = new THREE.Vector2(2, 5); // 2 columns by 5 rows
let tileSize = 512;
let c = document.createElement("canvas");
c.width = tileSize * tileDimension.x;
c.height = tileSize * tileDimension.y;
let ctx = c.getContext("2d");
ctx.fillStyle = tempFillColor;
ctx.fillRect(0, 0, c.width, c.height);
const sideGeometries = [];
const baseUVs = [
new THREE.Vector2(0, 0),
new THREE.Vector2(1, 0),
new THREE.Vector2(1, 1),
new THREE.Vector2(0, 1),
];
for (let i = 0; i < 10; i++) {
const clonedGeometry = baseGeometry.clone();
// Recompute UVs
let u = i % tileDimension.x;
let v = Math.floor(i / tileDimension.x);
const uvs = [];
baseUVs.forEach(uv => {
uvs.push(
(uv.x + u) / tileDimension.x,
(uv.y + v) / tileDimension.y
);
});
clonedGeometry.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
sideGeometries.push(clonedGeometry);
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = `bold ${tileSize / 3}px Calistoga`;
ctx.fillStyle = tempTextColor;
ctx.fillText(
i + 1,
(u + 0.5) * tileSize,
c.height - (v + 0.5) * tileSize
);
}
const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(sideGeometries);
mergedGeometry.computeVertexNormals();
let tex = new THREE.CanvasTexture(c);
tex.needsUpdate = true;
let material = new THREE.MeshPhongMaterial({
map: tex,
color: tempFillColor,
});
decahedron = new THREE.Mesh(mergedGeometry, material);
Are you sure, that 0 and 1 are correct UV values for an irregular tetragon, formed by vertices of a side?
@prisoner849 , I tried these base uvs but still get the same result -
const baseUVs = [
new THREE.Vector2(0.5, 0),
new THREE.Vector2(0, -0.67),
new THREE.Vector2(0.5, -1),
new THREE.Vector2(1, -0.67),
];
Where did you get these values from?
Or how did you get these values?
Negative values are the most curious
I have calculated the base uvs by getting the points of a side of the decahedron and normalising them to 1.