This project has moved and is read-only. For the latest updates, please go here.

Drawing lines in 3D using primitive batch

Dec 7, 2014 at 6:40 PM
Hi All,
I'm trying to override Bullet physics Debug drawer and use DirectXTK line drawer functions. I'm getting random drawn lines when I try to use bullet debug drawer in combination with DXTK.

I tried to draw a simple aline in 3D but it never gets a perspective in the scene, even If I supplied the shader with the correct projection view matrix of the camera.

Here is my setup:
BulletDebugDrawer::BulletDebugDrawer(cGraphics *graphics)
{
    if (graphics != nullptr)
    {
        m_pGraphics = graphics;
    }

    line = std::unique_ptr<PrimitiveBatch<VertexPositionColor>>(new PrimitiveBatch<VertexPositionColor>(m_pGraphics->getContext()));
    basicEffect = std::unique_ptr<BasicEffect>(new BasicEffect(m_pGraphics->getDevice()));

    basicEffect->SetProjection(m_pGraphics->getViewProjectionMatrix());
    basicEffect->SetVertexColorEnabled(true);

    void const* shaderByteCode;
    size_t byteCodeLength;

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

    m_pGraphics->getDevice()->CreateInputLayout(VertexPositionColor::InputElements,
        VertexPositionColor::InputElementCount,
        shaderByteCode, byteCodeLength,
        inputLayout.GetAddressOf());

}
void BulletDebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& lineColor)
{
     
    basicEffect->Apply(m_pGraphics->getContext());
    m_pGraphics->getContext()->IASetInputLayout(inputLayout.Get());
    
    line->Begin();
    line->DrawLine(VertexPositionColor(XMFLOAT3(from.x(), from.y(), from.z()), XMFLOAT4(1, 0, 1, 0)),
        VertexPositionColor(XMFLOAT3(to.x(), to.y(), to.z()), XMFLOAT4(1, 0, 1, 0)));
     
    line->End();
 
}
Dec 7, 2014 at 8:22 PM
Edited Dec 7, 2014 at 9:07 PM
Debugging graphics problems from partial code is difficult, so you should try a graphics debugger of some kind to check out the state and intermediate results at the time of the invalid draws. And of course, be sure to enable the DEBUG device and check for warnings & errors.

The BasicEffect family of shaders don't care about the distinction between SetView and SetProjection so you can combine the view*projection and just use SetProjection in most cases--some DGSLEffect shaders need the matrices distinct, however. They do support three distinct matrices but the others default to identity so that should work assuming getViewProjectionMatrix is returning the full transform needed from world -> projection in row-major form, and you are giving your from and to values in world coordinates.

BasicEffect and PrimitiveBatch do not set other state objects such as blend state, depth stencil state, of rasterizer state so you need to be sure those are set appropriately for your scene. Something like:
CommonStates states( device );
deviceContext->OMSetBlendState( states.Opaque(), nullptr, 0xFFFFFFFF );
deviceContext->OMSetDepthStencilState( states.DepthNone(), 0 );
deviceContext->RSSetState( states.CullCounterClockwise() );
Your color setup is using an alpha of 0 (transparent), when it perhaps should default to 1 (opaque)? It's generally safest when giving RGB colors as XMFLOAT4 to set alpha to 1 just in case...
line->DrawLine(
    VertexPositionColor(XMFLOAT3(from.x(), from.y(), from.z()), XMFLOAT4(1, 0, 1, 1)),
   VertexPositionColor(XMFLOAT3(to.x(), to.y(), to.z()), XMFLOAT4(1, 0, 1, 1)));
Dec 8, 2014 at 4:42 PM
Edited Dec 8, 2014 at 4:44 PM
Thanks so much for your quick reply.
I tried your suggestions and I tried to draw a simple line
CommonStates states(m_pGraphics->getDevice());
    m_pGraphics->getContext()->OMSetBlendState(states.Opaque(), nullptr, 0xFFFFFFFF);
    m_pGraphics->getContext()->OMSetDepthStencilState(states.DepthNone(), 0);
    m_pGraphics->getContext()->RSSetState(states.CullCounterClockwise());
    basicEffect->Apply(m_pGraphics->getContext());
    m_pGraphics->getContext()->IASetInputLayout(inputLayout.Get());
    
    line->Begin();
    line->DrawLine(VertexPositionColor(XMFLOAT3(10, 20, 205), XMFLOAT4(lineColor.getX(), lineColor.getY(), lineColor.getZ(), 1)),
        VertexPositionColor(XMFLOAT3(50, 90, 10), XMFLOAT4(lineColor.getX(), lineColor.getY(), lineColor.getZ(), 1)));
        line->End(); 
When I tried to walk to the camera in to Z axis, it looks like the line is 2D projected on the screen and its never drawn in 3D space... and that's the problem, because bullet should draw a wireframe model in 3D world co-ordinates
Image
Dec 8, 2014 at 6:25 PM
The projection view matrix is calculated by the following :
XMMATRIX cCamera::ViewProjectionMatrix()
{
    XMMATRIX viewMatrix = XMLoadFloat4x4(&m_ViewMatrix);
    XMMATRIX projectionMatrix = XMLoadFloat4x4(&m_ProjectionMatrix);

    return XMMatrixMultiply(viewMatrix, projectionMatrix);
}
Dec 8, 2014 at 7:36 PM
Edited Dec 8, 2014 at 8:00 PM
Where is m_ProjectionMatrix created? This seems critical to understanding why your projection isn't working as expected.
Dec 8, 2014 at 9:35 PM
Thanks for your help. Here is how I create the m_ProjectionMatrix.
void cCamera::SetPerspective(float fov, float aspect, float nearPlane, float farPlane)
{
    m_FieldOfView = fov;
    m_AspectRatio = aspect;
    m_NearPlaneDistance = nearPlane;
    m_FarPlaneDistance = farPlane;

    XMMATRIX projectionMatrix = XMMatrixPerspectiveFovLH(m_FieldOfView, m_AspectRatio, m_NearPlaneDistance, m_FarPlaneDistance);
    XMStoreFloat4x4(&m_ProjectionMatrix, projectionMatrix);
}
The View Matrix:

void cCamera::UpdateViewMatrix()
{
    XMVECTOR eyePosition = XMLoadFloat3(&m_Position);
    XMVECTOR direction = XMLoadFloat3(&m_Forward);
    XMVECTOR upDirection = XMLoadFloat3(&m_Up);

    XMMATRIX viewMatrix = XMMatrixLookToLH(eyePosition, direction, upDirection);
    XMStoreFloat4x4(&m_ViewMatrix, viewMatrix);

}
Dec 9, 2014 at 4:47 PM
Edited Dec 9, 2014 at 4:48 PM
Hmm... Nothing obviously wrong. Are these matrices being used to draw the other objects in the scene?

You should try running the VS Graphics Diagnostics on it to inspect the state and intermediates. It is included with VS 2012 Pro+, VS 2013 Pro+, VS 2013 Community, and VS 2013 Express for Windows (requires Windows 8.1).

PS: Also be sure to create CommonStates as an object like PrimitiveBatch with a std::unique_ptr rather than having it be a local variable every frame.
Dec 9, 2014 at 7:09 PM
I don't have windows 8.1 is there any other option ?

For other objects I use those matrices and I use your Geometry Classes for drawing the cubes as follow:
    m_Cube->Draw(World_Matrix, m_Graphics.getViewMatrix(), m_Graphics.getProjectionMatrix());
It's so weird, can't I draw a line in 3D ? Can you show a sample please ?
Dec 9, 2014 at 7:23 PM
Edited Dec 9, 2014 at 7:29 PM
Take a look at SimpleSample which draws a 3D grid using PrimitiveBatch.

BTW, you should take a look at the new VS 2013 Community SKU to see if it fits your needs as it is more full-featured than the Express SKUs. It's basically a version of the Professional SKU for individuals, students, and open-source projects.
Dec 9, 2014 at 8:28 PM
Edited Dec 9, 2014 at 8:43 PM
Thanks for your help. Sorry about disturbing you.
I'm really interested to know where is the problem.. here is the full source code
https://www.mediafire.com/?7pm4c1bh9h7i3n6
The sample is BulletPhysicsDemo.. You can compile it with DirectX 11.
Dec 11, 2014 at 12:13 AM
Edited Dec 11, 2014 at 12:15 AM
I tried to isolate everything and I found that the view matrix is the problem
here is the one that is copied from your demo
    XMVECTOR Eye = XMVectorSet(0.0f, 3.0f, -6.0f, 0.0f);
    XMVECTOR At = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
    XMVECTOR Up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
    XMMATRIX g_View = XMMatrixLookAtLH(Eye, At, Up);

    
    basicEffect->SetView(g_View);
it is working.

But when I use my own view matrix, the result is a 2d project of the line.. I really can't figure out where is the problem, here is my camera class

void cCamera::Initialize()
{
Reset();
}


void cCamera::Reset()
{
// config for left hand camera system
m_Position = XMFLOAT3(0.0f, 0.0f, 0.0f);
m_Forward = XMFLOAT3(0.0f, 0.0f, 1.0f);
m_Up = XMFLOAT3(0.0f, 1.0f, 0.0f);
m_Right = XMFLOAT3(1.0f, 0.0f, 0.0f);

UpdateViewMatrix();
}

const XMFLOAT3& cCamera::Position() const
{
return m_Position;
}

const XMFLOAT3& cCamera::Forward() const
{
return m_Forward;
}

const XMFLOAT3& cCamera::Up() const
{
return m_Up;
}

const XMFLOAT3& cCamera::Right() const
{
return m_Right;
}

float cCamera::AspectRatio() const
{
return m_AspectRatio;
}

float cCamera::FieldOfView() const
{
return m_FieldOfView;
}

float cCamera::NearPlaneDistance() const
{
return m_NearPlaneDistance;
}

float cCamera::FarPlaneDistance() const
{
return m_FarPlaneDistance;
}

XMMATRIX cCamera::ViewMatrix()
{
return XMLoadFloat4x4(&m_ViewMatrix);
}

XMMATRIX cCamera::ProjectionMatrix()
{
return XMLoadFloat4x4(&m_ProjectionMatrix);
}

XMMATRIX cCamera::ViewProjectionMatrix()
{
XMMATRIX viewMatrix = XMLoadFloat4x4(&m_ViewMatrix);
XMMATRIX projectionMatrix = XMLoadFloat4x4(&m_ProjectionMatrix);

return XMMatrixMultiply(viewMatrix, projectionMatrix);
}


void cCamera::SetPosition(const XMFLOAT3& position)
{
m_Position = position;
}

void cCamera::SetPerspective(float fov, float aspect, float nearPlane, float farPlane)
{
m_FieldOfView = fov;
m_AspectRatio = aspect;
m_NearPlaneDistance = nearPlane;
m_FarPlaneDistance = farPlane;

XMMATRIX projectionMatrix = XMMatrixPerspectiveFovLH(m_FieldOfView, m_AspectRatio, m_NearPlaneDistance, m_FarPlaneDistance);
XMStoreFloat4x4(&m_ProjectionMatrix, projectionMatrix);
}
void cCamera::SetOrthographic(float viewWidth, float viewHeight, float zn, float zf)
{
XMMATRIX projectionMatrix = XMMatrixOrthographicLH(viewWidth, viewHeight, zn, zf);
XMStoreFloat4x4(&m_ProjectionMatrix, projectionMatrix);
}
void cCamera::LookAt(const XMVECTOR &target)
{
XMVECTOR p = XMLoadFloat3(&m_Position);
XMVECTOR look = target - p;
XMStoreFloat3(&m_Forward, look);
}

void cCamera::UpdateViewMatrix()
{
XMVECTOR eyePosition = XMLoadFloat3(&m_Position);
XMVECTOR direction = XMLoadFloat3(&m_Forward);
XMVECTOR upDirection = XMLoadFloat3(&m_Up);

XMMATRIX viewMatrix = XMMatrixLookToLH(eyePosition, direction, upDirection);
XMStoreFloat4x4(&m_ViewMatrix, viewMatrix);
}


void cCamera::Update(float dt)
{
if (GetAsyncKeyState('W') & 0x8000)
    Walk(45 * dt);

if (GetAsyncKeyState('S') & 0x8000)
    Walk(-45 * dt);

if (GetAsyncKeyState('A') & 0x8000)
    Strafe(-45 * dt);

if (GetAsyncKeyState('D') & 0x8000)
    Strafe(45 * dt);

if (GetAsyncKeyState('Z') & 0x8000)
    MoveUp(-45 * dt);

if (GetAsyncKeyState('X') & 0x8000)
    MoveUp(45 * dt);

UpdateViewMatrix();
}

void cCamera::MoveUp(float d)
{
// mPosition += d*Up
XMVECTOR s = XMVectorReplicate(d);
XMVECTOR u = XMLoadFloat3(&m_Up);
XMVECTOR p = XMLoadFloat3(&m_Position);
XMStoreFloat3(&m_Position, XMVectorMultiplyAdd(s, u, p));
}

void cCamera::Strafe(float d)
{
// mPosition += d*Right
XMVECTOR s = XMVectorReplicate(d);
XMVECTOR r = XMLoadFloat3(&m_Right);
XMVECTOR p = XMLoadFloat3(&m_Position);
XMStoreFloat3(&m_Position, XMVectorMultiplyAdd(s, r, p));
}

void cCamera::Walk(float d)
{
// mPosition += d*Forward
XMVECTOR s = XMVectorReplicate(d);
XMVECTOR l = XMLoadFloat3(&m_Forward);
XMVECTOR p = XMLoadFloat3(&m_Position);
XMStoreFloat3(&m_Position, XMVectorMultiplyAdd(s, l, p));
}

void cCamera::Pitch(float angle)
{
// Rotate up and forward vector about the right vector.

XMMATRIX R = XMMatrixRotationAxis(XMLoadFloat3(&m_Right), angle);

XMStoreFloat3(&m_Up, XMVector3TransformNormal(XMLoadFloat3(&m_Up), R));
XMStoreFloat3(&m_Forward, XMVector3TransformNormal(XMLoadFloat3(&m_Forward), R));
}

void cCamera::RotateY(float angle)
{
// Rotate the basis vectors about the world y-axis.

XMMATRIX R = XMMatrixRotationY(angle);

XMStoreFloat3(&m_Right, XMVector3TransformNormal(XMLoadFloat3(&m_Right), R));
XMStoreFloat3(&m_Up, XMVector3TransformNormal(XMLoadFloat3(&m_Up), R));
XMStoreFloat3(&m_Forward, XMVector3TransformNormal(XMLoadFloat3(&m_Forward), R));
}
Dec 12, 2014 at 1:23 AM
Edited Dec 12, 2014 at 1:27 AM
Your problem is the difference between XMMatrixLookToLH and XMMatrixLookAtLH.

The DirectX TK SimpleSample is using XMMatrixLookAtLH which is computing:
XMVECTOR Eye = XMVectorSet(0.0f, 3.0f, -6.0f, 0.0f);
XMVECTOR At = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
XMVECTOR Up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
XMMATRIX g_View = XMMatrixLookAtLH(Eye, At, Up);
Here "At" is the position 0, 1, 0 and is used as the focus position (i.e. it computes the eye direction from Eye and At).

Your camera code is using XMMatrixLookToLH, which is failing to get the right result because you are passing (0,1,0) as the EyeDirection and UpDirection which naturally fails.
Marked as answer by walbourn on 12/11/2014 at 5:23 PM
Dec 13, 2014 at 7:40 PM
Edited Dec 13, 2014 at 7:41 PM
Thanks!

Now The lines are still not projected correctly when I use the passed values to the function SetPosition of the camera, but when I hard code the values as commented in the function UpdateViewMatrix, the projects works and everything is viewed clearly. That's so weird :S

I'm trying to set the camera eye position by the following code:
m_Camera.SetPosition(0.0f, 3.0f, -50);
where:
void cCamera::SetPosition(FLOAT x, FLOAT y, FLOAT z)
{
XMVECTOR position = XMVectorSet(x, y, z, 1.0f);
XMStoreFloat3(&m_Position, position);
}
void cCamera::UpdateViewMatrix()
{
/*m_Position.x = 0;
m_Position.y = 3;
m_Position.z = -50;*/

XMVECTOR eyePosition = XMLoadFloat3(&m_Position);
XMVECTOR direction = XMLoadFloat3(&m_Forward);
XMVECTOR upDirection = XMLoadFloat3(&m_Up);

XMMATRIX viewMatrix = XMMatrixLookToLH(eyePosition, direction, upDirection);
XMStoreFloat4x4(&m_ViewMatrix, viewMatrix);
}
Dec 14, 2014 at 11:24 AM
I have fixed it... Now I can see the wireframe boxes of bullet physics but when I rotate or move the camera they get the same orientation of the camera and they are not tied with the original box.. I use your XTK box draw functions with the same view matrix, projection..

Image
Dec 14, 2014 at 1:02 PM
The ViewMatrix should be set each frame. it's fixed.
Marked as answer by walbourn on 1/9/2015 at 1:42 PM