HeapsIO / heaps

Heaps : Haxe Game Framework
http://heaps.io
MIT License
3.19k stars 338 forks source link

FBX Collider/Interactive problems #596

Open mistress-of-loft-and-spire opened 5 years ago

mistress-of-loft-and-spire commented 5 years ago

(using haxe 4.0.0-rc.2+77068e10c, heaps: [1.6.1], tried HL and JS target)

--

I'm having some issues with Colliders not working for any FBX model I try to use.

I made a gif that shows the problem: collision problems (gif)

I'm just moving a sphere based on a movement Vector and doing .rayIntersection() from each object's Collider. My model is the middle one, where the Sphere does not detect collision. The skeleton FBX model (from the heaps samples) and a primitive cube added for comparison. Both work as intended. Here is the model I'm using: testcube-fbx.zip

The problem persists no matter what geometry and no matter what export method I use (blender binary fbx || save as obj and then conversion to binary or ascii fbx). I also tried exporting an ascii & binary fbx with Maya, but same issue.

--

I also tried following the h3d Interactive sample (checking for onOver), which also gives some strange results: interactive fbx problem (gif)

It works fine at first, but only if the Camera is a certain distance away from the model. I couldn't really follow how the Interactive class mouse onOver works, so I could not figure out why it works better than the Collider collision.

Here the other two objects for comparison, both work without issue (As you can see, I can walk inside the skeleton and onOver still gets triggered): interactive fbx (sample skeleton) (gif) interactive primitive cube (gif)

--

And here is my code (simplified a little bit):

Main, add objects to s3d:

        var cache = new h3d.prim.ModelCache();
//Skeleton fbx
        var obj1 = cache.loadModel(hxd.Res.gfx.Model);  
//My own model
        var obj2 = cache.loadModel(hxd.Res.gfx.testcube);  

    var prim = new h3d.prim.Cube();

    prim.translate( -0.5, -0.5, -0.5);
    prim.unindex();
    prim.addNormals();
    prim.addUVs();

    var tex = hxd.Res.gfx.earlysprites.toTexture();
    var mat = h3d.mat.Material.create(tex);
//Primitive cube
    var obj3 = new Mesh(prim, mat);

    Main.inst.s3d.addChild(obj1);
    Main.inst.s3d.addChild(obj2);
    Main.inst.s3d.addChild(obj3);

Interactive code, based on heaps sample:

    var beacon = null;

    var interactive = new Interactive(thisObject.getCollider(), Main.inst.s3d);
    interactive.bestMatch = true;

    interactive.onOver = function(e : hxd.Event) {
              graphic.getMaterials()[0].color.set(0, 1, 0);
              var s = new h3d.prim.Sphere(1, 32, 32);
              s.addNormals();
              beacon = new h3d.scene.Mesh(s, Main.inst.s3d);
              beacon.material.mainPass.enableLights = false;
              beacon.material.color.setColor(0xffff0000);
              beacon.scale(0.03);
              beacon.x = e.relX;
              beacon.y = e.relY;
              beacon.z = e.relZ;
    };
    interactive.onMove = interactive.onCheck = function(e:hxd.Event) {
              if( beacon == null ) return;
              beacon.x = e.relX;
              beacon.y = e.relY;
              beacon.z = e.relZ;
    };
    interactive.onOut = function(e : hxd.Event) {
              graphic.getMaterials()[0].color.set(1, 1, 1);
              beacon.remove();
              beacon = null;
    };

Sphere collider code:

//myObjects is an array containing all 3 objects
for(i in myObjects)
{
    //x,y,z is the Spheres current position,  vec is the movement vector
    var ray = Ray.fromValues(x, y, z, vec.x, vec.y, vec.z);
    var checkCol = i.getCollider().rayIntersection(ray, true);

    if(checkCol != -1 && checkCol <= vec.length())
    {
        Debug.log("COL: " + i.type);
    }

}
trethaller commented 5 years ago

That's odd, I just tested with your model (latest code) seems like I get pretty accurate detection:

collider

Code used (modification of Interactive sample):

    var i = new h3d.scene.Interactive(obj.getCollider(), s3d);
    i.bestMatch = true;
    i.onOver = function(_) {
        for(m in obj.getMaterials()) {
            m.color.setColor(0x00ff00);
        }
    }
    i.onOut = function(_) {
        for(m in obj.getMaterials()) {
            m.color.setColor(0xffffff);
        }
    }
trethaller commented 5 years ago

Just realized that interactives are not your issue. Since Interactives are also using a ray intersection (see h3d/scene/Scene.hx):

var hit = i.shape.rayIntersection(r, i.bestMatch);

shape, being the collider, it ought to work if your ray is constructed correctly. Are you sure about your checkCol <= vec.length() test?

mistress-of-loft-and-spire commented 5 years ago

Thanks for the response, and sorry for getting back so late!

I think the problem with your own test is that the camera is too far away. I've uploaded an example here: https://voec.github.io/collideTest/bin/ (WASD - to move closer and rotate around the center. You have to click into the black background once for it to register input) source here (modified Interactive sample): https://github.com/voec/collideTest/blob/master/Main.hx

From further away it works fine, only if you get a little closer to the model (try W) it stops working and only with my own fbx, not the primitives or the sample skeleton.

I could understand that it's just a quirk with how Blender exports geometry data, but as I said I tried so many different models, even from Maya and even some downloaded from external sources.

I could also understand that it might be an issue with my collision check or with my camera code, but then it should fail for all objects, not only for custom fbx models?

I'd have to say that this must be an issue with how heaps calculates Colliders from some fbx files. I just can't find out what makes my tested models different somehow.

EDIT: This issue might not be evident for top-down perspective games where the camera is further away. But for my project I'm using a first-person perspective where the camera is inside the model boundaries

EDIT2: I also just tried the current git heaps version, doesn't make a difference.

trethaller commented 5 years ago

To me it looks like it starts failing when the raycast starts from within the model bounding box. I think it happens more on your model because it is concave and has a larger bounding box - harder to reproduce with the skeleton, impossible with the box. Maybe try starting the raycast from a few meters behind the camera ?

On Sat, Apr 27, 2019, at 15:15, Niccccccccccccclllllllllllllllle wrote:

Thanks for the response, and sorry for getting back so late!

I think the problem with your own test, is that the camera is too far away. I've uploaded an example here: https://voec.github.io/collideTest/bin/ (WASD - to move closer and rotate around the center. You have to click into the black background once for it to register input) source here (modified Interactive sample): https://github.com/voec/collideTest/blob/master/Main.hx

From further away it works fine, only if you get a little closer to the model (try W) it stops working, and only with my own fbx, not the primitives or the sample skeleton.

I could understand that it's just a quirk with how Blender exports geometry data, but as I said I tried so many different models, even from Maya and even some downloaded from external sources.

I could also understand that it might be an issue with my collision check or with my camera code, but then it should fail for all objects, not only for custom fbx models?

I'd have to say that this must be an issue with how heaps calculates Colliders from some fbx files. I just can't find out what makes my tested models different somehow.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/HeapsIO/heaps/issues/596#issuecomment-487285398, or mute the thread https://github.com/notifications/unsubscribe-auth/AALOBUECC32MYV3ZHL2LZBLPSRGX5ANCNFSM4HF3MOYQ.

mistress-of-loft-and-spire commented 5 years ago

Ok, so in h3d.scene.Scene.hx line 123-127 I've changed it from:

var screenX = (event.relX / window.width - 0.5) * 2;
var screenY = -(event.relY / window.height - 0.5) * 2;
var p0 = camera.unproject(screenX, screenY, 0);
var p1 = camera.unproject(screenX, screenY, 1);
var r = h3d.col.Ray.fromPoints(p0.toPoint(), p1.toPoint());

to this:

var screenX = (event.relX / window.width - 0.5) * 2;
var screenY = -(event.relY / window.height - 0.5) * 2;
var p0 = camera.unproject(screenX, screenY, 0);
var p1 = camera.unproject(screenX, screenY, 1);

var r = h3d.col.Ray.fromPoints(p0.toPoint(), p1.toPoint());

var newpoint = r.getPoint(-3); //calc new point 3 times further behind camera

r = h3d.col.Ray.fromPoints(newpoint, p1.toPoint());

And now the ray intersection works with close distances. (But for closer and closer distances or larger objects -3 needs to be something even further behind the camera)

This works for my personal use somewhat as a workaround. But I'd guess that it now might collide with some objects behind the camera?

trethaller commented 5 years ago

In the code posted above you were raycasting manually, you could construct your ray similarly with Ray.fromValues but with an offset that works for you ?

ncannasse commented 5 years ago

That's interesting problem. Actually when building a Bounds for eager check, the Bounds should return true when asks if the ray interests if we are already within it. I guess this would require a change in Collider API to tells explicitely to check "within" or not.

The optimized collider would then check with the first Collider "within" and not with the second (precise) one.