jnsmalm / pixi3d

The 3D renderer for PixiJS. Seamless integration with 2D applications.
https://pixi3d.org
MIT License
759 stars 44 forks source link

Plane Normals In GLTF - Cannon ConvexPolyhedron from Model #15

Closed JotFX closed 3 years ago

JotFX commented 3 years ago

Hi there! I want to implement deterministic dice rolling with a physics engine and I wonder, how I can identify the side of the dice, which is on top. Ideally, I would have some named normals, to compare this with the up vector. But I have no idea, if this is possible with GLTF.

EDIT: I want this not only for six sides dies, but also for dice with 8, 10, 12 and 20 sides.

Do you have any ideas, how I could identify the sides of a loaded model?

Regards Jonas

jnsmalm commented 3 years ago

Hello!

Yes, you should be able to do that by checking the world transform of the model. Here is an example:

// Depending on the implementation of the physics, maybe it's not possible to do an exact compare "===". In that case you need to compare using some epsilon value.

if (model.worldTransform.up[1] === 1) {
    console.log("up")
}
if (model.worldTransform.up[1] === -1) {
  console.log("down")
}
if (model.worldTransform.up[0] === -1) {
  console.log("left")
}
if (model.worldTransform.up[0] === 1) {
  console.log("right")
}

Let's say the number 6 is the top of the gltf model, if the up vector of world transform is pointing up, then number 6 has landed pointing up. If you know one side, you can easily figure out the other sides as well.

JotFX commented 3 years ago

Hi! Thank you for your reply! I guess I forgot one important detail regarding my problem: I want to implement this also for dice with 8, 10, 12 and 20 sides. Those dice don't have that regular plane orientation and therefor the planes/normals are (at least for me) not trivial to detect. This is why I consider information inside the model much easier to process. Any ideas how to do this?

jnsmalm commented 3 years ago

In that case I would add a plane mesh for each side of the dice. That mesh should be rotated to match the normal of that side. If you do that you can easily read the "up" value of the transform. You can name these meshes so they can be identified in code.

let _1 = model.meshes.find(m => m.name === "1")
// ...
let _6 = model.meshes.find(m => m.name === "6")

if (_1.worldTransform.up[1] === 1) {
  console.log("side 1 is pointing up)
}
if (_6.worldTransform.up[1] === 1) {
  console.log("side 6 is pointing up)
}

If you don't want to render that plane mesh you can make it transparent.

_6.material.transparent = true
_6.material.baseColor[3] = 0
JotFX commented 3 years ago

Hi! This looks good. I will try this out shortly and will be giving feedback then! Regards Jonas

JotFX commented 3 years ago

Hi again! As I am digging deeper into it, I came to the conclusion, that the whole process doesn't make sense, without having the correct physics body for the dice. I now have very simple dice models (no additional polys) and I am loading them as a GLTF. The next step is to create a ConvexPolyhedron with Cannon-ES from those indices and vertices. This is where I am struggling right now. The D6 for example.

I hope I could make myself clear and you are able to help me.

All the best Jonas

JotFX commented 3 years ago

OK, I realize, this is something pretty specific to the GLTF Format and I guess nothing exactly pixi3d related. I am trying to build a collider body from the Gltf data right now. I'll keep you updated.

jnsmalm commented 3 years ago

When creating the ConvexPolyhedron, it seems (according to the API) that it want points and faces. Cant you just read the values directly from the model and use those?

let faces = model.meshes[0].geometry.indices
let points = model.meshes[0].geometry.positions
JotFX commented 3 years ago

Yes, but the number of positions seemed odd to me. I ignored my doubts and sticked to the receipe - which seems to work :) I'll need to get the animations and physics going and will keep you in the loop.

JotFX commented 3 years ago

It is - indeed -pretty simple to do this with TriMesh:

createTriMesh(mesh: Mesh3D): CANNON.Trimesh { return new CANNON.Trimesh( mesh.geometry.positions.buffer as unknown as number[], mesh.geometry.indices.buffer as unknown as number[]); }

(I am too stupid to insert code fragments properly)