Getting intersecting points of 2 mesh

Hi,

Plz see below images. there is snow ground and character. What I want to do is when character enters in to snow area, I want to capture the exact vertices of snow where character’s feet touches. and than I want to push down that selected area. The snow ground area is added on top of low poly ground as non physical layer.
using following code I can identify if the character enters in snow area. but how to capture exact vertices of the snow where character touches.

BoundingBox snowBox = (BoundingBox) snowNode.getWorldBound();
BoundingBox charBox = (BoundingBox) characterNode.getChild(“Sinbad-geom-7”).getWorldBound();
if (clothBox.intersects(snowBox)) {
// dead end… :frowning:
}


When the player enters the snow area, you calculate its position relative to the snow, then you decide a radius and for each vertex you check if it’s inside said radius. I would do it in the vertex shader though.

But the problem here is I want to do it for each foot. is it possible to attach ray to character’s each foot so that I can activate the ray whenever character enters such area. if yes then how to do it?

You can track the position of the foot bone in the skeleton…
Would be easier imo. And then use what Riccardo suggested.

1 Like

Why I didn’t think it earlier… :confused:

this is easiest way I guess…

Thanks nehon and Riccardo.

I tried it, see below code. but its not working. i tried to place sphere at exact x,z coordinates where toe bone is located.
In code toeL and toeR are bons. I tried to get world location of bone but it seems I missed some thing. see screen shots below.
In screen 1 while the character was on ideal state I placed spheres.
In screen 2, 3 I placed spheres while character was on run state.

               Vector3f locToWorld1 = new Vector3f();
            System.out.println("Toe Right :- " + characterNode.localToWorld(toeR.getModelSpacePosition(), locToWorld1));
            locToWorld1.setY(1f);
            addPointAt(locToWorld1, ColorRGBA.Red);
            
            Vector3f locToWorld2 = new Vector3f();
            System.out.println("Toe Left :- " + characterNode.localToWorld(toeL.getModelSpacePosition(), locToWorld2));
            locToWorld2.setY(1f);
            addPointAt(locToWorld2, ColorRGBA.Cyan);

private void addPointAt(Vector3f pos, ColorRGBA col) {

    Sphere sphereMesh = new Sphere(32, 32, .5f);
    Geometry shinyGeo = new Geometry("Shiny rock", sphereMesh);

    Material shinyMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
    shinyMat.setBoolean("UseMaterialColors", true);  // needed for shininess
    shinyMat.setColor("Specular", col); // needed for shininess
    shinyMat.setColor("Diffuse", col); // needed for shininess
    shinyMat.setFloat("Shininess", 8f); // shininess from 1-128
    shinyGeo.setMaterial(shinyMat);
    shinyGeo.setLocalTranslation(pos.clone());
    rootNode.attachChild(shinyGeo);
}



I tried it, see below code. but its not working. i tried to place sphere at exact x,z coordinates where toe bone is located.
In code toeL and toeR are bons. I tried to get world location of bone but it seems I missed some thing. see screen shots below.
In screen 1 while the character was on ideal state I placed spheres.
In screen 2, 3 I placed spheres while character was on run state.

               Vector3f locToWorld1 = new Vector3f();
            System.out.println("Toe Right :- " + characterNode.localToWorld(toeR.getModelSpacePosition(), locToWorld1));
            locToWorld1.setY(1f);
            addPointAt(locToWorld1, ColorRGBA.Red);
            
            Vector3f locToWorld2 = new Vector3f();
            System.out.println("Toe Left :- " + characterNode.localToWorld(toeL.getModelSpacePosition(), locToWorld2));
            locToWorld2.setY(1f);
            addPointAt(locToWorld2, ColorRGBA.Cyan);

private void addPointAt(Vector3f pos, ColorRGBA col) {

    Sphere sphereMesh = new Sphere(32, 32, .5f);
    Geometry shinyGeo = new Geometry("Shiny rock", sphereMesh);

    Material shinyMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
    shinyMat.setBoolean("UseMaterialColors", true);  // needed for shininess
    shinyMat.setColor("Specular", col); // needed for shininess
    shinyMat.setColor("Diffuse", col); // needed for shininess
    shinyMat.setFloat("Shininess", 8f); // shininess from 1-128
    shinyGeo.setMaterial(shinyMat);
    shinyGeo.setLocalTranslation(pos.clone());
    rootNode.attachChild(shinyGeo);
}



I hate math… you should too.
Cheat!

Node n = skelControl.getAttachmentsNode("toeL");
Vector3f footWorldPos = n.getWorldTranslation();

skelControl being the SkeletonControl of the model

It seems the issue was with creating geometry and mesh while moving scene OR camera or animation. The issue is solved it by creating mesh and geometry outside of method at class level and used it by cloning each time. see below code and screen shot.

But I still didn’t understand why that sphere had tail in earlier implementation? is it bug in JME?

                     private Sphere sphereMesh = new Sphere(32, 32, .5f);
                     private Geometry shinyGeo = new Geometry("Shiny rock", sphereMesh);

private void addPointAt(Vector3f pos, ColorRGBA col) {
    /**
     * Illuminated bumpy rock with shiny effect. Uses Texture from
     * jme3-test-data library! Needs light source!
     */
    Material shinyMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
    Geometry geom = shinyGeo.clone();
    geom.setLocalTranslation(pos.clone());
    shinyMat.setColor("Specular", col); // needed for shininess
    shinyMat.setColor("Diffuse", col); // needed for shininess
    shinyMat.setBoolean("UseMaterialColors", true);  // needed for shininess
    shinyMat.setFloat("Shininess", 8f); // shininess from 1-128
    geom.setMaterial(shinyMat);
    rootNode.attachChild(geom);
}

7 Likes

heheheh nice :wink:

I am ZERO in shader, can you tell me how to do it, any sample example I can refer?

You can copy the Lighting shader and start from it, you find all the documentation you need on the official opengl wiki.

But on second thought, if your current solution works for you, I think you’d better to keep it.

Because doing that on shaders will lead to one main issue: Sending the data (character feet pos, radius… < from now on i will call this footspot for short) to the shader.

You could do it using an array, but then you will be limited to a fixed number of footspots and eventually you will have to take care of replacing oldest ones with newer.

You could use a texture or a grid but then you will be limited by the resolution of said texture/grid and a good resolution might be too slow.

You could store the data inside a vertex buffer (like Color or one of the TexCoordX) for each vertex, but with the current algorithm i doubt you will have any benefit by doing that.

What I am trying to achieve is in below video clip of the “Rise of the Tomb Raider” game. See the seen at 2:53 in clip for the effect when she jump and fall on ground. I want to wright common code for Mud, Sand and Snow.

I don’t know if the current solution is good. What I am doing in code is getting FloatBuffer from VertexBuffer updating it and setting back to it, I guess its a bad idea. See the code below the video clip.

    public void simpleUpdate(float tpf) {
    
    if (walk) {
        BoundingBox snowBox = (BoundingBox) snowNode.getWorldBound();
        BoundingBox clothBox = (BoundingBox) characterNode.getChild("Sinbad-geom-7").getWorldBound();
        if (clothBox.intersects(snowBox)) {

            Vector3f center = clothBox.getCenter();

            Vector3f toeRPos = toeRNode.getWorldTranslation().clone();
            Vector3f toeLPos = toeLNode.getWorldTranslation().clone();

            if (toeRPos.z < center.z) {
                updateSnowBase(toeRPos, tpf);
            }
            if (toeLPos.z < center.z) {
                updateSnowBase(toeLPos, tpf);
            }
        }

    }

}
 
    private void updateSnowBase(Vector3f pos, float tpf) {
    Geometry snowGeom = (Geometry) snowNode.getChild("SnowGeom");
    Mesh mesh = snowGeom.getMesh();

    BoundingBox box = (BoundingBox) snowNode.getWorldBound();
    Vector3f boxCenter = box.getCenter();
    VertexBuffer vertexBuffer = mesh.getBuffer(VertexBuffer.Type.Position);
    if (vertexBuffer.getData() instanceof FloatBuffer) {
        FloatBuffer fb = (FloatBuffer) vertexBuffer.getData();

        Vector3f[] data = BufferUtils.getVector3Array(fb);

        float min = .5f / 3;
        float midIn = .65f / 2.5f;
        float mid = .85f / 2;
        float max = 1.0f / 2;
        float factor = max;
        Vector3f localPos = snowNode.worldToLocal(pos, new Vector3f());
        float maxY = localPos.y;
        Vector2f center = new Vector2f(localPos.x, localPos.z);
        for (Vector3f point : data) {
            Vector2f point2D = new Vector2f(point.x, point.z);
            if (isInRange(center, point2D, factor)) {
                if (point.y > localPos.y) {
                    float y = getHeight(center, point2D, min, max, mid, midIn, point.y, maxY);
                    point.setY(y);
                }
            }
        }
        FloatBuffer ufb = BufferUtils.createFloatBuffer(data);
        vertexBuffer.updateData(ufb);

    }
}

private boolean isInRange(Vector2f center, Vector2f point2D, float radious) {
    float dist = center.distance(point2D);
    return dist <= radious;
}

private float getHeight(final Vector2f pos2D, final Vector2f point2D, final float min,    final float max, final float mid, final float midIn, final float height, final float maxY) {
    float result = height;

    float dist = pos2D.distance(point2D);
    float typeA = maxY;
    float typeB = height - (height - maxY);
    float typeC = height - ((height - maxY) / 4);
    float typeD = height - ((height - maxY) / 2);
    if (dist <= min && height > typeA) { // Inner most circle 
        result = typeA;
    } else if (dist > min && dist <= midIn && height > typeB) { // Second Inner circle 
        result = typeB;
    } else if (dist > midIn && dist <= mid && height > typeC) { // Middel circle 
        result = typeC;
    } else if (dist > mid && dist <= max && height > typeD) { // Outer most circle 
        result = typeD;
    }
    return result;
}

I saw the video, you are right, if you want to achieve something similar, you can’t use the current code since your terrain would need an insanely amount of vertex to get a nice result.

In tomb rider they might be using tessellation to increase the number of vertex near to the camera, and then displace them with an height map, but i think this is quite unlikely.

What they actually do is probably just changing the normalmap and make the terrain looks like the snow was moved where the character walked, while it’s, in truth, still flat. This is a lot faster and high quality, since you can have one normal per fragment (~pixel) instead of one displacement per vertex, but it’s fake and totally a different effect from the one you are using at the moment.

FYI you can directly link to a part of a video in youtube if you use their share thingy…

but in video for initial 15 seconded it doesn’t seems they used fake effect.

Mhh true, i skipped that part. Then, they might use tessellation and displacement for bigger/nearest details.

But, even though it’s really fast in newer cards, with tessellation you affect performances and introduce a lot of problems (ie. cracks, calculation of tessellation levels … possibly more…) that you will have to solve, so unless you want to have high snow or close camera angles, it’s easier and better to use only normalmaps maybe combined with parallax if you want more noticeable bumps.

I am trying to do it with shader but facing issue… Posted new topic. please help.