Question

I'm first going to start by saying, yes I have looked for not even days or hours, but literally months in to this problem. I like to tackle my problems head on, but today I'm absolutely stuck.

I'm trying to determine if a point is within a triangle in a 3D space, and I have the following code in place to attempt to detect it.

The test:

bool D3DHandler::IsCollidingWithTerrain(D3DXVECTOR3 pos){
    for (unsigned int i = 1; i < chunk.at(0).GetWidth()-1; i++){
        for (unsigned int ii = 1; ii < chunk.at(0).GetWidth()-1; ii++){
            if (Physics::PolyPointCollision(
                NORMALVERTEX{ chunk.at(0).GetVertex(i, ii).X, chunk.at(0).GetVertex(i, ii).Y, chunk.at(0).GetVertex(i, ii).Z, { 0, 0, 0 } },
                NORMALVERTEX{ chunk.at(0).GetVertex(i + 1, ii).X, chunk.at(0).GetVertex(i + 1, ii).Y, chunk.at(0).GetVertex(i + 1, ii).Z, { 0, 0, 0 } },
                NORMALVERTEX{ chunk.at(0).GetVertex(i, ii + 1).X, chunk.at(0).GetVertex(i, ii + 1).Y, chunk.at(0).GetVertex(i, ii + 1).Z, { 0, 0, 0 } },
                NORMALVERTEX{ pos.x, pos.y, pos.z, { 0, 0, 0 } })
                ||
                Physics::PolyPointCollision(
                NORMALVERTEX{ chunk.at(0).GetVertex(i, ii).X, chunk.at(0).GetVertex(i, ii).Y, chunk.at(0).GetVertex(i, ii).Z, { 0, 0, 0 } },
                NORMALVERTEX{ chunk.at(0).GetVertex(i - 1, ii).X, chunk.at(0).GetVertex(i - 1, ii).Y, chunk.at(0).GetVertex(i - 1, ii).Z, { 0, 0, 0 } },
                NORMALVERTEX{ chunk.at(0).GetVertex(i, ii - 1).X, chunk.at(0).GetVertex(i, ii - 1).Y, chunk.at(0).GetVertex(i, ii - 1).Z, { 0, 0, 0 } },
                NORMALVERTEX{ pos.x, pos.y, pos.z, { 0, 0, 0 } })){
                return true;
            }
        }
    }
    return false;
}

The function in question:

bool Physics::PolyPointCollision(NORMALVERTEX a, NORMALVERTEX b, NORMALVERTEX c, NORMALVERTEX p){
    NORMALVERTEX v0 =  { c.X - a.X, c.Y - a.Y, c.Z - a.Z, { 0, 0, 0 } };
    NORMALVERTEX v1 =  { b.X - a.X, b.Y - a.Y, b.Z - a.Z, { 0, 0, 0 } };
    NORMALVERTEX v2 =  { p.X - a.X, p.Y - a.Y, p.Z - a.Z, { 0, 0, 0 } };
    float dot00 = (v0.X*v0.X) + (v0.Y*v0.Y) + (v0.Z * v0.Z);
    float dot01 = (v0.X*v1.X) + (v0.Y*v1.Y) + (v0.Z * v1.Z);
    float dot02 = (v0.X*v2.X) + (v0.Y*v2.Y) + (v0.Z * v2.Z);
    float dot11 = (v1.X*v1.X) + (v1.Y*v1.Y) + (v1.Z * v1.Z);
    float dot12 = (v1.X*v2.X) + (v1.Y*v2.Y) + (v1.Z * v2.Z);
    float invDenom = 1 / ((dot00*dot11) - (dot01*dot01));
    float u = ((dot11*dot02) - (dot01*dot12))*invDenom;
    float v = ((dot00*dot12) - (dot01*dot02))*invDenom;
    if (u >= 0 && v >= 0 && (v + u)<1){
        return true;
    }
    return false;
}

What I am doing here is converting the 3 points in a mesh for a terrain to be converted in to triangles, then sending that to a function to convert that in to a test to determine if a point is within the triangle using barycentric coordinates. However, it works to an extent, as if I enter the X-Z plane of the triangle, the test will return true, however it does NOT detect anything along the Y axis. My question is how do I get my function to detect the Z axis so I know for certain that the point is within that triangle's bounds in all axis.

Here is a screenshot of the engine I am making with the exact situation I'm trying to explain.

http://i.stack.imgur.com/kfqnL.png

EDIT:

D3DHandler.h class:

class D3DHandler{
public:
    D3DHandler(HWND hWnd,int WINX,int WINY,HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow);
    ~D3DHandler();
    bool IsCollidingWithTerrain(D3DXVECTOR3 pos);
    bool DeviceCheck();
    void UnlockBuffer();
    void LockBuffer();
    void print2DPixel(int x,int y,int r,int g,int b);
    void print2DPixel(int x,int y,D3DCOLOR c);
    void printVertexArray(NORMALVERTEX *vertex,int length);
    void printIndexArray(NORMALVERTEX *vertex,short *indicies,int vLength,int iLength);
    void printVertexArrayWireframe(NORMALVERTEX *vertex,int length);
    void printIndexArrayWireframe(NORMALVERTEX *vertex,short *indicies,int vLength,int iLength);
    void PrepareFrame(Player p);
    void SetupLight();
    void AddObjectToRoom(NORMALVERTEX* model,int mLength,short *modelIndices,int iLength,D3DXCOLOR dCol,D3DXCOLOR aCol);
    void AddObjectToRoom(TEXVERTEX* model,int mLength,short *modelIndices,int iLength,D3DXCOLOR dCol,D3DXCOLOR aCol,int texId);
    void AddObjectToRoom(float xPos,float yPos,float zPos,wchar_t meshName);
    boolean CheckRoom();
    void RenderRoom();
    void RenderTerrain();
    void ClearBuffer();
    void SetRenders();
    void LoadRoom();
    void PresentParams();
    void NewFont(LPCTSTR faceName,int size,int thickness, bool italic);
    IDirect3DDevice9* GetDevice();
    LPDIRECT3DTEXTURE9 grabTexture(int index);
    void PresentFrame();
    D3DMesh* GrabMesh(wchar_t meshName);
    void RenderFont(int fontId,char* text,int x,int y,D3DCOLOR colour,LPD3DXSPRITE sprite,DWORD alignment);
    void ResetDevice(HWND hWnd);
    void RebootDevice(HWND hWnd);
    bool isActive;
private:
    D3DVIEWPORT9        viewPort;
    D3DPRESENT_PARAMETERS d3dpp;
    D3DRoom *room;
    D3DXMATRIX matRotateX,matRotateY,matRotateZ,matTranslate,matScale,matView,matProjection;
    D3DLIGHT9 light;
    D3DMATERIAL9 material;
    IDirect3D9*         pDirect3D;
    IDirect3DDevice9*   pDevice;
    IDirect3DSurface9*  pBackBuffer;
    D3DLOCKED_RECT      backRect;
    LPDIRECT3DVERTEXBUFFER9 vBuffer;
    LPDIRECT3DINDEXBUFFER9 iBuffer;
    HWND                            hWind;
    std::vector<D3DTerrain>         chunk;
    std::vector<D3DFont>            font;
    std::vector<LPDIRECT3DTEXTURE9> texture;
    std::vector<D3DMesh>            mesh;
    int WINX,WINY;

    HINSTANCE hInst;
    HINSTANCE hPrev;
    LPSTR lpCmdLne;
    int nCmdShw;
};

D3DTerrain class (it is what the "chunk" is):

class D3DTerrain{
public:
    D3DTerrain(float xPos,float yPos,float zPos);
    ~D3DTerrain();
    void Render(LPDIRECT3DDEVICE9 pDevice);
    void Load(LPDIRECT3DDEVICE9 pDevice);
    void Load(LPDIRECT3DDEVICE9 PDevice,char* fName,wchar_t* tName);
    void Release(){vBuffer->Release();iBuffer->Release();m_texture->Release();m_textureDetail->Release();m_vertecies=NULL;m_indices=NULL;m_height=NULL;}
    TEX2VERTEX GetVertex(unsigned int x,unsigned int y){return m_vertex[x*(int)m_width+y];}
    UINT GetWidth(){return m_width;}
private:
    IDirect3DVertexBuffer9  *vBuffer;
    IDirect3DIndexBuffer9   *iBuffer;
    TEX2VERTEX              *m_vertex;
    UCHAR*                  m_height;
    UINT                    m_vertecies;
    UINT                    m_indices;
    UINT                    m_width;
    char                    m_fileLoc[128];
    wchar_t                 m_textureName[128];
    LPDIRECT3DTEXTURE9      m_texture,m_textureDetail;
    D3DXMATRIX m_rotX,m_rotY,m_rotZ,m_translate,m_scale,m_textureCords; 
};

Vertex types:

struct NORMALVERTEX{
    FLOAT X, Y, Z;
    D3DVECTOR NORMAL;
};
struct T_NORMALVERTEX{
    FLOAT X, Y, Z;
    D3DVECTOR NORMAL;
    FLOAT U, V;
};
struct TEXVERTEX{
    FLOAT X, Y, Z;
    FLOAT U, V;
};
struct TEX2VERTEX{
    FLOAT X, Y, Z;
    FLOAT U1, U2, V1, V2;
};

I'm not here for critique on my programming, it's messy as I am only trying to get a major function working and then I will optimize and reorganize my code, it's a personal project, and I know it's rough.

Was it helpful?

Solution

Alright, so whatever the reason the previous way I approached that was incorrect, so I looked for the mathematical definition of how to find the barycentric coordinates, which involved the vector lengths and the cross product. While the square rooting is slow, it works, I will be tweaking the way this function works in the future and trying to clean and optimize it to my best ability, however this is how I went about fixing this issue.

UtilityFunctions.h

#include "DataTypes.h"
namespace Util{
    float map(float in, float minA, float maxA, float minB, float maxB);
    float dot(float ax, float ay, float az, float bx, float by, float bz);
    float dot(Point3D a, Point3D b);
    float length(Point3D p);
    Point3D sub(Point3D a, Point3D b);
    Point3D toBarycentric(Point3D a, Point3D b, Point3D c, Point3D p);
    Point3D cross(Point3D a, Point3D b);
};

UtilityFunctions.cpp

#include <vector>
#include <stdio.h>
#include <math.h>
#include "UtilityFunctions.h"

float Util::map(float in, float minA, float maxA, float minB, float maxB){
    float ratio = 0.0f;
    if (abs(maxA - minA)>0){
        ratio = (maxB - minB) / (maxA - minA);
    }
    return (in - minA)*ratio + minB;
}

float Util::dot(float ax, float ay, float az, float bx, float by, float bz){ return (ax*bx) + (ay*by) + (az*bz); }
float Util::dot(Point3D a, Point3D b){ return (a.X*b.X) + (a.Y*b.Y) + (a.Z*b.Z); }
float Util::length(Point3D p){
    return ((p.X*p.X) + (p.Y*p.Y) + (p.Z*p.Z));
}
Point3D Util::cross(Point3D a, Point3D b){
    return{ ((a.Y*b.Z) - (a.Z*b.Y)), ((a.X*b.Z) - (a.Z*b.X)), ((a.X*b.Y) - (a.Y*b.X)) };
}
Point3D Util::sub(Point3D a, Point3D b){
    return{ a.X - b.X, a.Y - b.Y, a.Z - b.Z };
}
Point3D Util::toBarycentric(Point3D a, Point3D b, Point3D c, Point3D p){
    Point3D v0 = sub(c, a);
    Point3D v1 = sub(b, a);
    Point3D v2 = sub(p, a);

    Point3D v12 = cross(v1, v2);
    Point3D v10 = cross(v1, v0);

    if (dot(v12, v10) < 0){ return{ 0, 0, -1 }; }

    Point3D v02 = cross(v0, v2);
    Point3D v01 = cross(v0, v1);

    if (dot(v02, v01) < 0){ return{ 0, 0, -1 }; }

    float denom = length(v01);
    return{ length(v12) / denom, length(v02) / denom, 0 };
}

PhysicsFunctions.cpp

#include "PhysicsHandler.h"
bool Physics::PolyPointCollision(Point3D a,Point3D b,Point3D c,Point3D p){
    Point3D w = Util::toBarycentric(a, b, c, p);
    if (w.X+w.Y<=1&&w.Z>=0){
        return true;
    }
    return false;
}

The topmost function:

bool D3DHandler::IsCollidingWithTerrain(D3DXVECTOR3 pos){
    Point3D pointPos = { pos.x, pos.y, pos.z };
    for (unsigned int i = 1; i < chunk.at(0).GetWidth() - 1; i++){
        for (unsigned int ii = 1; ii < chunk.at(0).GetWidth() - 1; ii++){
            Point3D originVertex = chunk.at(0).GetVertex(i, ii);
            if (Physics::PolyPointCollision(originVertex,chunk.at(0).GetVertex(i + 1, ii),
                chunk.at(0).GetVertex(i, ii + 1),pointPos)){
                return true;
            }
            if (Physics::PolyPointCollision(originVertex,chunk.at(0).GetVertex(i - 1, ii),
                chunk.at(0).GetVertex(i, ii - 1),pointPos)){
                return true;
            }
        }
    }
    return false;
}

Proof / test results: http://imgur.com/a/HH2pp

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top