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

bounding sphere of a modal mesh #33

Closed
AndreAhmed opened this issue May 22, 2016 · 13 comments
Closed

bounding sphere of a modal mesh #33

AndreAhmed opened this issue May 22, 2016 · 13 comments
Labels

Comments

@AndreAhmed
Copy link

AndreAhmed commented May 22, 2016

Hi,

I'm trying to get a bounding sphere of a modal mesh and transform it to world coordinates. but the bounding sphere without any transformation is not center at 0,0,0.

here is how I do the transformation```

 m_bounding = m_Tree->meshes.at(0)->boundingSphere;
 XMMATRIX sphereTranslate = XMMatrixTranslation(m_bounding.Center.x, m_bounding.Center.y,
     m_bounding.Center.z);
sphereTranslate = sphereTranslate*treeTranslate;

XMMATRIX boundingSphereScale = XMMatrixScaling(120.0f, 120.0f, 120.0f);
worldSphere = boundingSphereScale*sphereTranslate;
m_bounding.Transform(m_bounding, sphereTranslate* boundingSphereScale);

m_BoundingSphere->Draw(worldSphere, m_Camera.ViewMatrix(), m_Camera.ProjectionMatrix());
@walbourn
Copy link
Member

walbourn commented May 22, 2016

The first thing to do is check that your ModelMesh has a non-zero bounding volume. The modern SDKMESH exporter and CMO mesh pipeline in Visual Studio should be creating a valid sphere bound, but there are some older SDKMESH models that are missing valid bounds. Because the SDKMESH does not contain a bounding sphere, one is created at load time from the bounding box.

Note that the center will not necessarily be (0,0,0) in model coordinates because this is not the most efficient sphere bound. The algorithm in DirectXMath's BoundingSphere::CreateFromPoints for example is from Ritter, Jack, An Efficient Bounding Sphere, Graphics Gems, Academic Press, 1990, p. 301-303, code: p. 723-725.

The code you provide is doing some really strange stuff with the center of the bounding box. You create a translation based on the bounding sphere center, and then transform the bounding box by that translation. The result is meaningless.

All you really need to do is transform the BoundingSphere by the same transform as you use for rendering the model. If you want to do collision in world-space, that would just be the 'world' matrix, not the camera's view & projection matrices.

XMMATRIX modelWorld = ...;
BoundingSphere worldSphere;
m_bounding.Transform(worldSphere, modelWorld);

BTW, for debugging collision geometry, you should take a look at DebugDraw

m_batch = std::make_unique<PrimitiveBatch<VertexPositionColor>>(context);
m_effect = std::make_unique<BasicEffect>(device);
m_effect->SetVertexColorEnabled(true);
{
    void const* shaderByteCode;
    size_t byteCodeLength;

    m_effect->GetVertexShaderBytecode(&shaderByteCode, &byteCodeLength);

    DX::ThrowIfFailed(
        device->CreateInputLayout(
            VertexPositionColor::InputElements, VertexPositionColor::InputElementCount,
           shaderByteCode, byteCodeLength,
            m_layout.ReleaseAndGetAddressOf()));
}

...

XMMATRIX modelWorld = ...;
XMMATRIX view = m_Camera.ViewMatrix();
XMMATRIX proj = m_Camera.ProjectionMatrix();
m_Tree->Draw( context, *m_states, modelWorld, view, proj);

#ifdef _DEBUG
// Draws an outline of the bounding volume over the model
context->OMSetBlendState(m_states->Opaque(), nullptr, 0xFFFFFFFF);
context->OMSetDepthStencilState(m_states->DepthNone(), 0);
context->RSSetState(m_states->CullNone());
m_effect->Apply(context);
context->IASetInputLayout(m_layout.Get());
m_batch->Begin();
m_effect->SetView(view);
m_effect->SetProjection(proj);

BoundingSphere worldSphere;
m_bounding.Transform(worldSphere, modelWorld);

DX::Draw(m_batch.get(), worldSphere);
m_batch->End();
#endif

@walbourn
Copy link
Member

It's in the link I gave you. DebugDraw.h and DebugDraw.cpp.

@AndreAhmed
Copy link
Author

AndreAhmed commented May 22, 2016

I have the following code, but the sphere is translated and its not surrounding the tree. its translated behind it.

here is how I'm rendering it

XMMATRIX treeTranslate = XMMatrixTranslation(100, -100, 112);

    // Convert rotation into radians.
    float angle = atan2(100 - m_Camera.Position().x, 112 - m_Camera.Position().z) * (180.0 / XM_PI);
    float treeRot = (float)angle * 0.0174532925f;
    XMMATRIX treeRotation = XMMatrixRotationY(treeRot);
    XMMATRIX treeWorld = treeRotation*treeTranslate; // THIS IS THE IMPORTANT WORLD, THE ROTATION IS IMPORTANT TO INCLUDE

    m_Tree->Draw(m_Graphics.getContext(), states, treeWorld, m_Graphics.getViewMatrix(),
        m_Camera.ProjectionMatrix());

    m_bounding = m_Tree->meshes.at(0)->boundingSphere;

    m_batch->Begin();

    m_bounding.Transform(m_bounding, treeWorld);

    m_effect->SetView(m_Camera.ViewMatrix());
    m_effect->SetProjection(m_Camera.ProjectionMatrix());
    Draw(m_batch.get(), m_bounding, Colors::Red);
    m_batch->End();

here is the picture
untitled

@walbourn
Copy link
Member

What's the original values in m_Tree->meshes.at(0)->boundingSphere?

@AndreAhmed
Copy link
Author

m_bounding {Center={x=0.00674819946 y=72.5237503 z=-0.932399750 } Radius=110.144882 } DirectX::BoundingSphere

@walbourn
Copy link
Member

Ok. It looks valid at least.

That said, m_bounding.Transform(m_bounding, treeWorld); means you are modifying the bounding sphere every frame you render which is not what you want to do. Make a temporary:

BoundingSphere treeSphere;
m_bounding.Transform(treeSphere, treeWorld);

m_effect->SetView(m_Camera.ViewMatrix());
m_effect->SetProjection(m_Camera.ProjectionMatrix());
Draw(m_batch.get(), treeSphere, Colors::Red);
m_batch->End();

@AndreAhmed
Copy link
Author

AndreAhmed commented May 22, 2016

still the sphere is in wrong place

@walbourn
Copy link
Member

Sure, but we are getting closer.

Check the values of m_bounding, treeSphere, and treeWorld to make sure they make sense to you.

Also, what's the value of m_Tree->meshes.at(0)->boundingBox?

What format is your model loading from?

@AndreAhmed
Copy link
Author

I use the directx sdkmesh, converted from an obj file.
m_bounding = {Center={x=0.00674819946 y=72.5237503 z=-0.932399750 } Radius=110.144882 }
treeSphere = {Center={x=99.1690521 y=-27.4762497 z=111.576981 } Radius=110.144882 }

tree world

untitled

@AndreAhmed
Copy link
Author

Bounding box :

box = {Center={x=0.00674819946 y=72.5237503 z=-0.932399750 } Extents={x=58.6604500 y=73.0773544 z=57.8838997 } }

@AndreAhmed
Copy link
Author

here is how I do the picking, which is incorrect due to the bounding sphere calculation is wrong, do you find a problem with it ?


void GameApp::Pick(int sx, int sy, XMVECTOR & pickRayInWorldSpacePos, XMVECTOR &pickRayInWorldSpaceDir)
{
    XMVECTOR pickRayInViewSpaceDir = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
    XMVECTOR pickRayInViewSpacePos = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);

    float PRVecX, PRVecY, PRVecZ;
    XMFLOAT4X4 tmp;
    XMStoreFloat4x4(&tmp, m_Camera.ProjectionMatrix());
    //Transform 2D pick position on screen space to 3D ray in View space
    PRVecX = (((2.0f * sx) / 784) - 1) / tmp.m[0][0];
    PRVecY = -(((2.0f * sy) / 562) - 1) / tmp.m[1][1];
    PRVecZ = 1.0f;  //View space's Z direction ranges from 0 to 1, so we set 1 since the ray goes "into" the screen

    pickRayInViewSpaceDir = XMVectorSet(PRVecX, PRVecY, PRVecZ, 0.0f);

    //Uncomment this line if you want to use the center of the screen (client area)
    //to be the point that creates the picking ray (eg. first person shooter)
    //pickRayInViewSpaceDir = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f);

    // Transform 3D Ray from View space to 3D ray in World space
    XMMATRIX pickRayToWorldSpaceMatrix;
    XMVECTOR matInvDeter;   //We don't use this, but the xna matrix inverse function requires the first parameter to not be null

    pickRayToWorldSpaceMatrix = XMMatrixInverse(&matInvDeter, m_Camera.ViewMatrix());   //Inverse of View Space matrix is World space matrix

    pickRayInWorldSpacePos = XMVector3TransformCoord(pickRayInViewSpacePos, pickRayToWorldSpaceMatrix);
    pickRayInWorldSpaceDir = XMVector3TransformNormal(pickRayInViewSpaceDir, pickRayToWorldSpaceMatrix);
    pickRayInWorldSpaceDir = XMVector3Normalize(pickRayInWorldSpaceDir);

    float dist;
    if (m_bounding.Intersects(pickRayInWorldSpacePos, pickRayInWorldSpaceDir, dist) == true)
    {
        m_Picked++;
    }

}

@AndreAhmed
Copy link
Author

its fixed, I was not applying the effect.
untitled

but I have another problem, as shown in the image the sphere is large and doesn't surround the tree, how would I scale it ?

@walbourn
Copy link
Member

That makes more sense :)

The issue here is that the bounding sphere is not a particularly tight bound because of the way it is created. Try picking against the boundingBox instead (there's a DebugDraw for the box) and see if that's a better bound for your model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants