Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Dynamic Meshes with collisions? #24

Closed
Nesh108 opened this issue Apr 11, 2017 · 21 comments
Closed

Adding Dynamic Meshes with collisions? #24

Nesh108 opened this issue Apr 11, 2017 · 21 comments

Comments

@Nesh108
Copy link
Contributor

Nesh108 commented Apr 11, 2017

Hey there!

I am trying to add my own custom meshes but I can't seem to be able to enable the checkCollisions from Babylon, is there a special way to do it with the engine?

Basically something like:

var myMesh = createCustomMesh();
myMesh.checkCollisions = true;
this.noa.rendering.addDynamicMesh(myMesh);

Even with that, my player seems to still go through the custom meshes (not the normal blocks, though).

@fenomas
Copy link
Owner

fenomas commented Apr 11, 2017

Hey, sorry, I really need to document this stuff.

Babylon.js has a lot of game-engine features that this library doesn't use, and collision checking is one of them. Setting mesh.checkCollisions tells Babylon to do something about mesh-vs-mesh checking (I haven't looked at the details), but it's completely orthogonal to this engine, voxels, and so on.

Here's the high-level view of what this engine supports related to what you're looking at:

  • Any entity with the physics component will collide with solid terrain blocks (that is, block IDs that were registered with solid set to true
  • Any entity with the collidesTerrain component will emit an event (to the component callback) when it collides with voxel terrain (i.e. solid blocks)
  • Entities with the collidesEntities component will emit an event when they collide (i.e. overlap) with each other. But there's no physics for this - they just emit events.
  • Custom meshes only exist for rendering purposes. The physics and collision tracking systems only know about voxels and entities.

So, if you assign a custom mesh to a particular voxel ID, noa will make an instance of that mesh every time the block shows up, but the physics engine only knows about the voxel and its solidity. If you want a non-solid voxel that does something when the player overlaps it, what I do is have a voxel that creates an entity in its onSet / onLoad handlers, and that entity does something on collisions with the player entity. But in that case the voxel won't collide (in the physics sense).

So, a little limited, but that's how things stand as of now. Let me know if that's clear!

@fenomas
Copy link
Owner

fenomas commented Apr 11, 2017

Incidentally now that I type all that, the collidesTerrain component doesn't really make sense. I should remove it and make onCollide events happen equivalently for any entity with the physics component.

@fenomas
Copy link
Owner

fenomas commented Apr 11, 2017

Follow-up: I now remember why collidesTerrain is a separate component - because I had a to-do item someday to make it possible to have physics-driven entities that don't collide with terrain (ghosts, etc). Not sure that's worth spending time on, but anyway.

@Nesh108
Copy link
Contributor Author

Nesh108 commented Apr 14, 2017

So, you mean I should be registering the custom mesh as a solid block even though it is not necessarily a block? That might work if I want to add some items but I am not sure for other things.

I am asking because I want to import some mobs and I want them to have collisions and I don't want to handle them as other blocks.

@fenomas
Copy link
Owner

fenomas commented Apr 14, 2017

Right now the physics in this library only knows how to collide entity AABBs and solid voxels. It has no idea whether the entities or voxels have meshes associated with them or not.

If you want voxels that are partly-solid - where the collision volume is still an AABB, just not the full voxel, you could try hacking voxel-physics-engine to support that. It wouldn't be super trivial, but certainly possible. (It's on my to-do list, just not very high 😁)

But if you want something that resembles "real physics" - where objects aren't just AABBs, and entities collide with each other - then that probably isn't feasible with the current physics engine. It's relatively easy to replace the default physics engine with a real one (like cannon.js), but this would only make sense if the engine supported voxel terrain as a collision object type. I've played with extending cannon.js to do that -
see here - but haven't gone far enough to see if it's really doable.

@Nesh108
Copy link
Contributor Author

Nesh108 commented Apr 18, 2017

Oh I see. I took a look at the projectile code you had, since it seemed to be a nice and simple way to add collision between entities. Here is my current code:

    this.blankmesh = this.make_test_mesh();
    this.blankmesh.position = new BABYLON.Vector3(x, y, z);

    let blank_mat = new BABYLON.StandardMaterial('blank-mat', this.scene);

    // Id 254, so it won't bother any of the other blocks
    this.noa.registry.registerBlock(254, {
        blockMesh: this.blankmesh,
        opaque: true,
        solid: true,
    });

    // Make an instance
    let new_sprite_mesh = this.noa.rendering.makeMeshInstance(this.blankmesh, false);

    let vectorsWorld = this.blankmesh.getBoundingInfo().boundingBox.vectorsWorld; 
    let width = Number(vectorsWorld[1].x-(vectorsWorld[0].x));
    let height = Number(vectorsWorld[1].y-(vectorsWorld[0].y));

    // usage: entities.add( pos, w, h, mesh, meshOffset, doPhysics, shadow
    var eid = this.noa.entities.add(new BABYLON.Vector3(x, y, z), width, height, new_sprite_mesh, [0,0,0], true, true);
    
    // make it collide with player
    this.noa.entities.addComponent(eid, this.noa.entities.names.player);

But the problem now is that it doesn't seem to show anywhere, can you spot any problem?

EDIT: I looked into makeMeshInstance and it seems that it handles already whether it should be added as a DynamicMesh or not, so that shouldn't be the problem, right?

@fenomas
Copy link
Owner

fenomas commented Apr 19, 2017

Hi! This is just a guess, but I think that the problem is probably that you've passed a Babylon Vector3 into a method that expects regular [x, y, z] array (for the position argument of entites.add).

As a general rule, noa uses arrays (technically Float32 arrays) for vectors, because I do all my vector twiddling with the gl-vec3 library. Babylon uses its own vector objects (that look like {x:x, y:y, z:z}), but in general noa doesn't use them except internally. I'm pretty sure there are a few exceptions to that - I haven't been completely rigorous about it. But in general, most noa APIs that take a vector are expecting an array.

So, since the code above passes in a Babylon vector, I'm guessing the entity adder probably winds up setting the mesh's position to [NaN, NaN, NaN] and that's why it's not appearing.

@Nesh108
Copy link
Contributor Author

Nesh108 commented Apr 19, 2017

Yeah, that was it!

Now it appears but the shadow is quite off and the mesh doesn't collide with the player.

Should I have used this.noa.entities.names.player or something like this.noa.entities.names.collideEntities (similar to collideTerrain)?

@fenomas
Copy link
Owner

fenomas commented Apr 19, 2017

Okay, looking back a few posts I think there are a few issues confounding each other that I need to explain.

Fundamentally, noa understands two kinds of things - blocks (i.e. voxels) and entities. When you register a block type with a custom mesh, that's meant for things that are part of the voxel terrain, but you don't want them to get meshed like a voxel. So - a flower, a mailbox, etc. Then, when noa is building a "chunk" of terrain, and it encounters a block ID with a custom mesh, instead of making that block part of the terrain, now will create an instance of the custom mesh and put it at the given voxel.

But defining a block ID with a custom mesh doesn't do anything to the world unless you also set some voxels with that ID.

Entities, meanwhile, are completely separate - they represent anything that can move around, etc. The only thing noa really does with entities is, if they have the physics component, it will handle their velocity and whatnot, and keep them from moving through solid voxels.

In general, it's probably necessary to look inside components you want to use, to get some idea what they do. E.g. the player component has no functionality at all, it just tags which entity is the player. (Also, I think it's not currently used - I think I need to remove it!)

Anyway, if I understand your question correctly then the collideEntities component may be what you need. Note, when I said earlier that the physics system doesn't collide entities, I meant it doesn't make the bounce off each other, etc. The collideEntities component does detect entity collisions, and send an event, which is likely what you need for projectiles, etc.

@fenomas
Copy link
Owner

fenomas commented Apr 19, 2017

Oh, and for your current bug, you might try not using the entities.add() helper, and instead try adding the various components (position, shadow, physics) one-by-one. This might show where the issue is. Note that some components depend on each other (e.g. the physics component assumes its entity has a position.)

@Nesh108
Copy link
Contributor Author

Nesh108 commented Apr 19, 2017

Ok, just to clarify my goal: I want to create a second player (some sort of NPC or mob with AI) which just needs to be able to acts like any other solid object. If it can have some physics concerning gravity and whatnot: awesome, but I would like to start off by not being able to go through it with my player.

What would be the simplest thing to do?

Imagine having a mesh which acts like your mobs in the testbed demo but which cannot be walked through.

@fenomas
Copy link
Owner

fenomas commented Apr 19, 2017

Okay, if I understand you correctly then it's back to my first answer - this engine only models terrain and entities, where terrain is solid but doesn't move, and entities can move but are not solid. The collideEntities component will report entities that are overlapping, and you could catch that event and apply a force or impulse to push one entity away from the other, but the engine can't currently model having entities physically collide with each other the way they do with terrain.

@Nesh108
Copy link
Contributor Author

Nesh108 commented Apr 19, 2017

Oh I see!

How do I catch that event? Something like noa.on('onCollision', function(eventData) { ... }); ?

@fenomas
Copy link
Owner

fenomas commented Apr 19, 2017

Every component works the same way - you add a component to an entity, and set any parameters in the state object for that entity-component pair. In this case it would look like:

var id = // my entity ID
var state = {
    callback: function(other) console.log('Entity', id, 'collided with', other)
}
noa.entities.addComponent( id, noa.entities.names.collideEntities, state )

Or something like that!

@Nesh108
Copy link
Contributor Author

Nesh108 commented Apr 19, 2017

That worked nicely, I will try to move the player to its previous position anytime there is a collision, let's see :D

@Nesh108
Copy link
Contributor Author

Nesh108 commented Jun 13, 2017

Hey @AndyHall,

as an extension of this question: I am currently having a hard time properly offsetting my custom mesh.

Here is my code:

this.new_sprite_mesh = this.noa.rendering.makeMeshInstance(this.blankmesh, false);

        // Flip X axis
        this.new_sprite_mesh.scaling.x = -scale;
        this.new_sprite_mesh.scaling.y = scale;
        this.new_sprite_mesh.scaling.z = scale;

        let width = Math.abs(this.new_sprite_mesh.getBoundingInfo().boundingBox.extendSize.x * this.new_sprite_mesh.scaling.x);
        let height = Math.abs(this.new_sprite_mesh.getBoundingInfo().boundingBox.extendSize.y * this.new_sprite_mesh.scaling.y);

        // usage: entities.add( pos, w, h, mesh, meshOffset, doPhysics, shadow
        let eid = this.noa.entities.add([position.x, position.y, position.z], width, height , this.new_sprite_mesh, false, true, false);
        
        this.noa.entities.addComponent(eid, this.noa.entities.names.shadow, { size: width * 2});

        let state = {
            callback: function(other) {
                if(other === this.noa.playerEntity) {
                    this.move_player_to(this.previous_player_position);   
                }
            }.bind(this),    
        };

        // // make it collide with player
        this.noa.entities.addComponent(eid, this.noa.entities.names.collideEntities, state);

The mesh is correctly shown but the problem is the offset (relative to the shadow and its collision). I tried every sort of combination that I could think of but still nothing.

I checked in the debug layer and the bounding box of my meshes is tightly fitting around their geometry, so that's not the problem.

I simply want to have the lowest part of the bounding box of my mesh touching the floor and centered within their shadow. How can I do that?

@fenomas
Copy link
Owner

fenomas commented Jun 19, 2017

Hey, sorry for the late reply. From reading the code above, I think the answer here is to change the mesh offset vector, but let me just try to explain what assumptions the library is making.

Effectively, an entity with a mesh has two positions. There's the "noa position", which is an [x, y, z] array and is stored in the entity's position component. This position is what noa uses to handle the physics, and entity collisions, etc. This noa position refers to the feet position of the entity - i.e. the bottom-center of its bounding box.

On the other hand there's the "Babylon position", that Babylon knows about, and uses to render meshes. Note that babylon positions have arbitrary centering - the babylon position could be the mesh's center, or one of its corners, or whatever, depending on how the mesh was made.

So with all that said, all noa does each frame is, it calculates an entity's "noa position" (from physics etc), then it overwrites the entities "Babylon position" to be equal to the noa position plus the mesh offset.

So, if physics and collisions are working right but the mesh is rendering in the wrong spot, the probelm should be with the mesh offset property of the mesh component. If that's not working, then I suspect something else might be going on (like some property getting set to NaN or whatever), but that's just guessing.

@Nesh108
Copy link
Contributor Author

Nesh108 commented Jun 19, 2017

Ohhh, I see! But shouldn't the mesh have just an incorrect Y position? My meshes seems to be completely off on all axis.

@fenomas
Copy link
Owner

fenomas commented Jun 19, 2017

Well, if I understand your issue correctly then my best guess would be that the problem is with the Babylon mesh's registration point, which in principle can be anywhere. When you make meshes with its built in methods (Babylon.Mesh.CreateBox or whatever) I think the registration points are always in the center, but if the mesh was imported from somewhere then there's no way of knowing.

But I could just be misunderstanding what's going on.

@Nesh108
Copy link
Contributor Author

Nesh108 commented Jun 26, 2017

Yeah, I think that's it. Do you know of any way to reset those points to the center?

I am using this: https://github.com/Nesh108/babylon-voxel-critter , to import a mesh from the VoxelBuilder. Is there anything you can see that might misbehave when added to your engine?

@fenomas
Copy link
Owner

fenomas commented Jun 26, 2017

Well, the key should be changing the [0,0,0] offset when you add the entity, but the correct values depend totally on how the mesh was made.

That is, a mesh's "center" is baked into its vertex data, right? Like, if a mesh has vertices whose x coordinates are all between -1 and 1, then that mesh is more or less centered on the x axis. But if you went through and added 5 to every x vertex, you'd have the exact same mesh, but centered around x=5, so then you'd want a mesh offset of [-5,0,0] when adding it to an entity.

But the vertex values in your mesh are down to whatever tool created them. Probably the tool has some idea of where the "origin" is, and it generates vertex data that are relative to that origin.

I expect you could investigate this by importing the mesh into the scene, and then looking at mesh.getBoundingInfo().boundingBox - e.g. the maximum and minimum properties, which I think should be the extents of the mesh's data in local coordinates.

Hope this helps!

@fenomas fenomas closed this as completed Aug 29, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants