I am working on a simple raytracer in c++. I am currently implementing an intersection function but have encountered some issues.
For some reason, the collision detection only works for a tiny rectangle in my image. In the image below you can see that it draws the room quite fine for a small part of the screen but fails to do so for the rest of the scene. Only a small section gets drawn correctly.
Why does my intersection detection not work? I have included the code for the intersection and draw function below.
LoadTestModel(m_Model);
m_Light.position = glm::vec3(0.0f, -1.0f, 0.0);
m_Light.color = glm::vec3(0.f, 0.f, 0.f);
m_Light.ambient = glm::vec3(0.5f, 0.5f, 0.5f);
m_Camera.position = glm::vec3(0.0, 0.0, -2.0);
m_Camera.yaw = 0.0f;
}
void Lab2Scene::Draw(Window& window)
{
if (!m_RenderNext) return;
m_RenderNext = false;
for (uint32_t y = 0; y < window.GetHeight(); ++y)
{
for (uint32_t x = 0; x < window.GetWidth(); ++x)
{
Ray ray = {};
glm::vec3 d(x - (window.GetWidth() / 2), y - (window.GetHeight() / 2), (window.GetHeight() / 2));
d = glm::normalize(d);
ray.direction = d * m_Camera.GetRotationY();
ray.start = m_Camera.position;
// Find the closest intersection of the casted ray.
Intersection nearest_intersection = {};
if (ClosestIntersection(ray, m_Model, nearest_intersection))
{
//window.PutPixel(x, y, glm::vec3(1.f, 0.f, 0.f));
window.PutPixel(x, y, DirectLight(m_Light, nearest_intersection, m_Model) + m_Model[nearest_intersection.triangleIndex].color * m_Light.ambient); // DirectLight(m_Light, intersection, m_Model)
}
else
{
window.PutPixel(x, y, m_Light.color);
}
}
}
}
bool Lab2Scene::ClosestIntersection(const Ray& ray, const std::vector<Triangle>& triangles, Intersection& intersection)
{
float m = std::numeric_limits<float>::max();
intersection.distance = m;
bool inters = false;
for (int i = 0; i < triangles.size(); ++i) {
float dot = glm::dot(ray.direction, triangles[i].normal);
if (dot != 0) {
using glm::vec3;
using glm::mat3;
vec3 v0 = triangles[i].v0;
vec3 v1 = triangles[i].v1;
vec3 v2 = triangles[i].v2;
vec3 e1 = v1 - v0;
vec3 e2 = v2 - v0;
vec3 b = ray.start - v0;
mat3 A(-ray.direction, e1, e2);
vec3 x = glm::inverse(A) * b;
if (x[1] >= 0 && x[2] >= 0 && x[1] + x[2] <= 1 && x[0] >= 0) {
vec3 intersect = ray.start + (x[0] * ray.direction);
if (glm::distance(ray.start, intersect) <= intersection.distance) {
intersection.position = intersect;
intersection.distance = glm::distance(ray.start, intersect);
intersection.triangleIndex = i;
inters = true;
}
}
}
}
return inters;
}
Related
I'm trying to use bullet physics to draw a ray to hit a game object in the scene so I can select it, i am using the camera matrix to draw a ray and then pick an object in space and then look through a list of game objects and look for the same location.
On Mouse press I have the following code, it seems to be off and only picks the items some times:
glm::vec4 lRayStart_NDC(
((float)lastX / (float)RECT_WIDTH - 0.5f) * 2.0f,
((float)lastY / (float)RECT_HEIGHT - 0.5f) * 2.0f,
-1.0,
1.0f
);
glm::vec4 lRayEnd_NDC(
((float)lastX / (float)RECT_WIDTH - 0.5f) * 2.0f,
((float)lastY / (float)RECT_HEIGHT - 0.5f) * 2.0f,
0.0,
1.0f
);
projection = glm::perspective(glm::radians(SceneManagement::getInstance()->MainCamera->GetVOW()), (float)RECT_WIDTH / (float)RECT_HEIGHT, 0.1f, 100.0f);
glm::mat4 InverseProjectionMatrix = glm::inverse(projection);
view = SceneManagement::getInstance()->MainCamera->GetViewMatrix();
glm::mat4 InverseViewMatrix = glm::inverse(view);
glm::vec4 lRayStart_camera = InverseProjectionMatrix * lRayStart_NDC;
lRayStart_camera /= lRayStart_camera.w;
glm::vec4 lRayStart_world = InverseViewMatrix * lRayStart_camera;
lRayStart_world /= lRayStart_world.w;
glm::vec4 lRayEnd_camera = InverseProjectionMatrix * lRayEnd_NDC;
lRayEnd_camera /= lRayEnd_camera.w;
glm::vec4 lRayEnd_world = InverseViewMatrix * lRayEnd_camera;
lRayEnd_world /= lRayEnd_world.w;
glm::vec3 lRayDir_world(lRayEnd_world - lRayStart_world);
lRayDir_world = glm::normalize(lRayDir_world);
glm::vec3 out_end = SceneManagement::getInstance()->MainCamera->GetCamPosition() + SceneManagement::getInstance()->MainCamera->GetCamFront() * 1000.0f;
btCollisionWorld::ClosestRayResultCallback RayCallback(
btVector3(SceneManagement::getInstance()->MainCamera->GetCamPosition().x, SceneManagement::getInstance()->MainCamera->GetCamPosition().y, SceneManagement::getInstance()->MainCamera->GetCamPosition().z),
btVector3(out_end.x, out_end.y, out_end.z)
);
PhysicsManager::getInstance()->dynamicsWorld->rayTest(
btVector3(SceneManagement::getInstance()->MainCamera->GetCamPosition().x, SceneManagement::getInstance()->MainCamera->GetCamPosition().y, SceneManagement::getInstance()->MainCamera->GetCamPosition().z),
btVector3(out_end.x, out_end.y, out_end.z),
RayCallback
);
if (RayCallback.hasHit())
{
btTransform position = RayCallback.m_collisionObject->getInterpolationWorldTransform();
printf("Collision \n");
for (int i = 0; i < SceneManagement::getInstance()->gObjects.size(); i++)
{
if (SceneManagement::getInstance()->gObjects.at(i)->transform.Position.x == position.getOrigin().getX() &&
SceneManagement::getInstance()->gObjects.at(i)->transform.Position.y == position.getOrigin().getY() &&
SceneManagement::getInstance()->gObjects.at(i)->transform.Position.z == position.getOrigin().getZ())
{
int select = i;
SceneManagement::getInstance()->SelectedGameObject = SceneManagement::getInstance()->gObjects.at(select);
SceneManagement::getInstance()->SelectedGameObject->DisplayInspectorUI();
return;
}
}
}
This check
SceneManagement::getInstance()->gObjects.at(i)->transform.Position.x == position.getOrigin().getX() &&
SceneManagement::getInstance()->gObjects.at(i)->transform.Position.y == position.getOrigin().getY() &&
SceneManagement::getInstance()->gObjects.at(i)->transform.Position.z == position.getOrigin().getZ()
fails due to numerical precision issues.
You should check the "equality" of vectors only up to some precision.
Please, use some distance function and the following check instead of your if() condition:
const double EPSILON = 1e-4; // experiment with this value
auto objPos = SceneManagement::getInstance()->gObjects.at(i)->transform.Position;
bool isWithinRange = distance3D(objPos, position.getOrigin()) < EPSILON;
if (isWithinRange)
{
int select = i;
...
}
The distance3D function is simply the euclidean distance in 3D. The glm::distance function should do, just convert both vectors to glm::vec3 format.
i need to implement arcball camera. I got something similar, but it works very crookedly (the angle changes sharply, when turning to the right / left, the camera raises up / down strongly).
Here is my source code, can you tell me where I went wrong:
bool get_arcball_vec(double x, double y, glm::vec3& a)
{
glm::vec3 vec = glm::vec3((2.0 * x) / window.getWidth() - 1.0, 1.0 - (2.0 * y) / window.getHeight(), 0.0);
if (glm::length(vec) >= 1.0)
{
vec = glm::normalize(vec);
}
else
{
vec.z = sqrt(1.0 - pow(vec.x, 2.0) - pow(vec.y, 2.0));
}
a = vec;
return true;
}
...
void onMouseMove(double x, double y) {
if (rightMouseButtonPressed) {
glm::vec3 a,b;
cur_mx = x;
cur_my = y;
if (cur_mx != last_mx || cur_my != last_my)
if (get_arcball_vec(last_mx, last_my, a) && get_arcball_vec(cur_mx, cur_my, b))
viewport.getCamera().orbit(a,b);
last_mx = cur_mx;
last_my = cur_my;
...
void Camera::orbit(glm::vec3 a, glm::vec3 b)
{
forward = calcForward();
right = calcRight();
double alpha = acos(glm::min(1.0f, glm::dot(b, a)));
glm::vec3 axis = glm::cross(a, b);
glm::mat4 rotationComponent = glm::mat4(1.0f);
rotationComponent[0] = glm::vec4(right, 0.0f);
rotationComponent[1] = glm::vec4(up, 0.0f);
rotationComponent[2] = glm::vec4(forward, 0.0f);
glm::mat4 toWorldCameraSpace = glm::transpose(rotationComponent);
axis = toWorldCameraSpace * glm::vec4(axis, 1.0);
glm::mat4 orbitMatrix = glm::rotate(glm::mat4(1.0f), (float)alpha, axis);
eye = glm::vec4(target, 1.0) + orbitMatrix * glm::vec4(eye - target, 1.0f);
up = orbitMatrix * glm::vec4(up, 1.0f);
}
I use this code to map 2D mouse position to the sphere:
Vector3 GetArcBallVector(const Vector2f & mousePos) {
float radiusSquared = 1.0; //squared radius of the sphere
//compute mouse position from the centre of screen to interval [-half, +half]
Vector3 pt = Vector3(
mousePos.x - halfScreenW,
halfScreenH - mousePos.y,
0.0f
);
//if length squared is smaller than sphere diameter
//point is inside
float lengthSqr = pt.x * pt.x + pt.y * pt.y;
if (lengthSqr < radiusSquared){
//inside
pt.z = std::sqrtf(radiusSquared - lengthSqr);
}
else {
pt.z = 0.0f;
}
pt.z *= -1;
return pt;
}
To calculate rotation, I use the last (startPt) and current (endPt) mapped position and do:
Quaternion actRot = Quaternion::Identity();
Vector3 axis = Vector3::Cross(endPt, startPt);
if (axis.LengthSquared() > MathUtils::EPSILON) {
float angleCos = Vector3::Dot(endPt, startPt);
actRot = Quaternion(axis.x, axis.y, axis.z, angleCos);
}
I prefer to use Quaternions over matrices since they are easy to multiply (for acumulated rotation) and interpolate (for some smooting).
I have a triangle defined by 3 points in 3d space. I also have a line segment defined by 2 points in 3d space. I want to know if they intersect. I don't really need to know the point of intersection.
I don't know any calculus but I know some trig. I know some about matrices but I understand vectors well (3d vectors specifically). Please keep it simple.
Can you walk me through the example problem:
triangle:
a: -4, 3, 0
b: 4, 3, 0
c: -3, -5, 4
line segment:
d: 1, -2, 0
e: -2, 6, 2
EDIT:
I am going to use this in a c++ physics engine.
One answer involved tetrahedron volume calculation from 4 vertices. Please provide formula or show it in code.
UPDATE:
As meowgoesthedog pointed out, I could try to use the Moller-Trumbore intersection algorithm. See my answer below for an alternate solution.
Here is one way to solve your problem. Compute the volume of the tetrahedron Td =
(a,b,c,d) and Te = (a,b,c,e). If either volume of Td or Te is zero, then one endpoint of the
segment de lies on the plane containing triangle (a,b,c). If the volumes of Td and Te have the same sign,
then de lies strictly to one side, and there is no intersection. If Td and Te have opposite
signs, then de crosses the plane containing (a,b,c).
From there there are several strategies. One is to compute the point p where de crosses
that plane. Then project down to 2D, and solve the point-in-triangle problem in 2D.
Another route is to compute the volumes of the tetrahedra (a,b,d,e), (b,c,d,e), and (c,a,d,e). Then only if all three have the same sign, does de intersect the triangle (a,b,c).
How to compute the volume of a tetrahedron from its corner coordinates is all over the
web, and also in Computational Geometry in C.
I implemented the great answer that Joseph gave in python and thought I would share. The function takes a set of line segments and triangles and computes for each line segment if it intersects any of the given triangles.
The first input to the function is a 2xSx3 array of line segments where the first index specifies the start or end point of the segment, the second index refers to the s^th line segment, and the third index points to the x, y,z coordinate of the line segment point.
The second input is a 3XTX3 array of triangle vertices, where the first index specifies one of the three vertices (which don't have to be in any particular order), the second index refers to the t^th triangle, and the third index points to the x,y,z coordinates the the triangle vertex.
The output of this function is a binary array of size S which tells you whether the s^th line segment intersects any of the triangles given. If you want to know which triangles the segments intersect, then just remove the summation of the last line of the function.
def signedVolume(a, b, c, d):
"""Computes the signed volume of a series of tetrahedrons defined by the vertices in
a, b c and d. The ouput is an SxT array which gives the signed volume of the tetrahedron defined
by the line segment 's' and two vertices of the triangle 't'."""
return np.sum((a-d)*np.cross(b-d, c-d), axis=2)
def segmentsIntersectTriangles(s, t):
"""For each line segment in 's', this function computes whether it intersects any of the triangles
given in 't'."""
# compute the normals to each triangle
normals = np.cross(t[2]-t[0], t[2]-t[1])
normals /= np.linalg.norm(normals, axis=1)[:, np.newaxis]
# get sign of each segment endpoint, if the sign changes then we know this segment crosses the
# plane which contains a triangle. If the value is zero the endpoint of the segment lies on the
# plane.
# s[i][:, np.newaxis] - t[j] -> S x T x 3 array
sign1 = np.sign(np.sum(normals*(s[0][:, np.newaxis] - t[2]), axis=2)) # S x T
sign2 = np.sign(np.sum(normals*(s[1][:, np.newaxis] - t[2]), axis=2)) # S x T
# determine segments which cross the plane of a triangle. 1 if the sign of the end points of s is
# different AND one of end points of s is not a vertex of t
cross = (sign1 != sign2)*(sign1 != 0)*(sign2 != 0) # S x T
# get signed volumes
v1 = np.sign(signedVolume(t[0], t[1], s[0][:, np.newaxis], s[1][:, np.newaxis])) # S x T
v2 = np.sign(signedVolume(t[1], t[2], s[0][:, np.newaxis], s[1][:, np.newaxis])) # S x T
v3 = np.sign(signedVolume(t[2], t[0], s[0][:, np.newaxis], s[1][:, np.newaxis])) # S x T
same_volume = np.logical_and((v1 == v2), (v2 == v3)) # 1 if s and t have same sign in v1, v2 and v3
return (np.sum(cross*same_volume, axis=1) > 0)
Thanks for the help! This is an alternate solution. The question was for c++ and as meowgoesthedog pointed out, I could try to use the Moller-Trumbore intersection algorithm. This is what I came up with:
#include <math.h>
class vec3 {
public:
float x, y, z;
float dot(const vec3 & b) {
return vec3::x * b.x + vec3::y * b.y + vec3::z * b.z;
}
vec3 cross(const vec3 & b) {
return vec3::vec3(
vec3::y * b.z - vec3::z * b.y,
vec3::z * b.x - vec3::x * b.z,
vec3::x * b.y - vec3::y * b.x
);
}
vec3 normalize() {
const float s = 1.0f / sqrtf(vec3::x * vec3::x + vec3::y * vec3::y + vec3::z * vec3::z);
return vec3::vec3(vec3::x * s, vec3::y * s, vec3::z * s);
}
vec3 operator+(const vec3 & b) {
return vec3::vec3(
vec3::x + b.x,
vec3::y + b.y,
vec3::z + b.z
);
}
vec3 operator+=(const vec3 & b) {
*this = vec3::operator+(b);
return *this;
}
vec3 operator-(const vec3 & b) {
return vec3::vec3(
vec3::x - b.x,
vec3::y - b.y,
vec3::z - b.z
);
}
vec3 operator-=(const vec3 & b) {
*this = vec3::operator-(b);
return *this;
}
vec3 operator*(const vec3 & b) {
return vec3::vec3(
vec3::x * b.x,
vec3::y * b.y,
vec3::z * b.z
);
}
vec3 operator*=(const vec3 & b) {
*this = vec3::operator*(b);
return *this;
}
vec3 operator*(float b) {
return vec3::vec3(
vec3::x * b,
vec3::y * b,
vec3::z * b
);
}
vec3 operator*=(float b) {
*this = vec3::operator*(b);
return *this;
}
vec3 operator/(const vec3 & b) {
return vec3::vec3(
vec3::x / b.x,
vec3::y / b.y,
vec3::z / b.z
);
}
vec3 operator/=(const vec3 & b) {
*this = vec3::operator/(b);
return *this;
}
vec3 operator/(float b) {
return vec3::vec3(
vec3::x * b,
vec3::y * b,
vec3::z * b
);
}
vec3 operator/=(float b) {
*this = vec3::operator/(b);
return *this;
}
vec3(float x, float y, float z) {
vec3::x = x;
vec3::y = y;
vec3::z = z;
}
vec3(float x) {
vec3::x = x;
vec3::y = x;
vec3::z = x;
}
vec3() {
//
}
~vec3() {
//
}
};
#define EPSILON 0.000001f
bool lineSegIntersectTri(
vec3 line[2],
vec3 tri[3],
vec3 * point
) {
vec3 e0 = tri[1] - tri[0];
vec3 e1 = tri[2] - tri[0];
vec3 dir = line[1] - line[0];
vec3 dir_norm = dir.normalize();
vec3 h = dir_norm.cross(e1);
const float a = e0.dot(h);
if (a > -EPSILON && a < EPSILON) {
return false;
}
vec3 s = line[0] - tri[0];
const float f = 1.0f / a;
const float u = f * s.dot(h);
if (u < 0.0f || u > 1.0f) {
return false;
}
vec3 q = s.cross(e0);
const float v = f * dir_norm.dot(q);
if (v < 0.0f || u + v > 1.0f) {
return false;
}
const float t = f * e1.dot(q);
if (t > EPSILON && t < sqrtf(dir.dot(dir))) { // segment intersection
if (point) {
*point = line[0] + dir_norm * t;
}
return true;
}
return false;
}
For running a few tests:
#include <stdio.h>
const char * boolStr(bool b) {
if (b) {
return "true";
}
return "false";
}
int main() {
vec3 tri[3] = {
{ -1.0f, -1.0f, 0.0f },
{ 1.0f, -1.0f, 0.0f },
{ 1.0f, 1.0f, 0.0f },
};
vec3 line0[2] = { // should intersect
{ 0.5f, -0.5f, -1.0f },
{ 0.5f, -0.5f, 1.0f },
};
vec3 line1[2] = { // should not intersect
{ -0.5f, 0.5f, -1.0f },
{ -0.5f, 0.5f, 1.0f },
};
printf(
"line0 intersects? : %s\r\n"
"line1 intersects? : %s\r\n",
boolStr(lineSegIntersectTri(line0, tri, NULL)),
boolStr(lineSegIntersectTri(line1, tri, NULL))
);
return 0;
}
C# version:
public class AlgoritmoMollerTrumbore
{
private const double EPSILON = 0.0000001;
public static bool lineIntersectTriangle(Point3D[] line,
Point3D[] triangle,
out Point3D outIntersectionPoint)
{
outIntersectionPoint = new Point3D(0, 0, 0);
Point3D rayOrigin = line[0];
Vector3D rayVector = Point3D.Subtract(line[1], line[0]);
rayVector.Normalize();
Point3D vertex0 = triangle[0];
Point3D vertex1 = triangle[1];
Point3D vertex2 = triangle[2];
Vector3D edge1 = Point3D.Subtract(vertex1, vertex0);
Vector3D edge2 = Point3D.Subtract(vertex2, vertex0);
Vector3D h = Vector3D.CrossProduct(rayVector, edge2);
double a = Vector3D.DotProduct(edge1, h);
if (a > -EPSILON && a < EPSILON)
{
return false; // This ray is parallel to this triangle.
}
double f = 1.0 / a;
Vector3D s = Point3D.Subtract(rayOrigin, vertex0);
double u = f * (Vector3D.DotProduct(s, h));
if (u < 0.0 || u > 1.0)
{
return false;
}
Vector3D q = Vector3D.CrossProduct(s, edge1);
double v = f * Vector3D.DotProduct(rayVector, q);
if (v < 0.0 || u + v > 1.0)
{
return false;
}
// At this stage we can compute t to find out where the intersection point is on the line.
double t = f * Vector3D.DotProduct(edge2, q);
if (t > EPSILON && t < Math.Sqrt(Vector3D.DotProduct(rayVector, rayVector))) // ray intersection
{
outIntersectionPoint = rayOrigin + rayVector * t;
return true;
}
else // This means that there is a line intersection but not a ray intersection.
{
return false;
}
}
}
I have a terrain with a specific length and width, which take the information from he file bmp. With Shade of grey i can modify my terrain. Fine, it works great.
Here comes the problem.
I need my terrain divided into physically smaller parts, joined together create the original terrain.
How can i do this? I know that its possible. Any sugestions? Where, why there?
Mys file:
HeightField.cpp
#include "HeightField.h"
HeightField::HeightField(Terrain terrain, PxPhysics& sdk, PxMaterial &material, int width, int height)
{
this->nrVertices = terrain.NumVertices;
this->terrain = terrain;
this->width = width;
this->height = height;
this->fillSamples();
this->fillDesc();
this->aHeightField = sdk.createHeightField(hfDesc);
this->hfGeom = new PxHeightFieldGeometry(aHeightField, PxMeshGeometryFlags(), this->terrain.dy / 255.0, this->terrain.dx, this->terrain.dz);
this->terrainPos = new PxTransform(PxVec3(-this->terrain.dx*(this->width - 1) / 2, 0.0f, this->terrain.dz*(this->height - 1) / 2), PxQuat(3.1415 / 2.0, PxVec3(0, 1, 0)));
this->g_pxHeightField = sdk.createRigidDynamic(*this->terrainPos);
this->g_pxHeightField->setRigidDynamicFlag(PxRigidDynamicFlag::eKINEMATIC, true);
PxShape* aHeightFieldShape = this->g_pxHeightField->createShape(*(this->hfGeom), material);
}
HeightField::~HeightField()
{
}
void HeightField::fillSamples()
{
this->samples = (PxHeightFieldSample*)malloc(sizeof(PxHeightFieldSample)*(this->nrVertices));
for (int i = 0; i < this->nrVertices; i++)
{
samples[i].height = this->terrain.hminfo.heightMap[i].y;
samples[i].clearTessFlag();
}
}
void HeightField::fillDesc()
{
this->hfDesc.format = PxHeightFieldFormat::eS16_TM;
this->hfDesc.nbColumns = this->width;
this->hfDesc.nbRows = this->height;
this->hfDesc.samples.data = this->samples;
this->hfDesc.samples.stride = sizeof(PxHeightFieldSample);
}
Terrain.cpp
#include "Terrain.h"
Terrain::Terrain(void)
{
v = NULL;
indices = NULL;
dx = dz = 1000; //odleg³oœæ miêdzy punktami grid'a
dy = 1000; //maksymalna wysokoϾ terenu
}
Terrain::~Terrain(void)
{
if (v != NULL) delete [] v;
if (indices != NULL) delete indices;
if (hminfo.heightMap != NULL) delete [] hminfo.heightMap;
}
bool Terrain::HeightMapLoad(char* filename, float sx, float sz, float maxy)
{
FILE *filePtr; // Point to the current position in the file
BITMAPFILEHEADER bitmapFileHeader; // Structure which stores information about file
BITMAPINFOHEADER bitmapInfoHeader; // Structure which stores information about image
int imageSize, index;
unsigned char height;
// Open the file
filePtr = fopen(filename,"rb");
if (filePtr == NULL)
return 0;
dx = sz;
dz = sz;
dy = maxy;
// Get the width and height (width and length) of the image
hminfo.terrainWidth = bitmapInfoHeader.biWidth;
hminfo.terrainHeight = bitmapInfoHeader.biHeight;
// Initialize the heightMap array (stores the vertices of our terrain)
hminfo.heightMap = new IntV3[hminfo.terrainWidth * hminfo.terrainHeight];
// We use a greyscale image, so all 3 rgb values are the same, but we only need one for the height
// So we use this counter to skip the next two components in the image data (we read R, then skip BG)
int k=0;
// Read the image data into our heightMap array
for(int j=0; j< hminfo.terrainHeight; j++)
{
for(int i=0; i< hminfo.terrainWidth; i++)
{
height = bitmapImage[k];
index = ( hminfo.terrainWidth * (hminfo.terrainHeight - 1 - j)) + i;
hminfo.heightMap[index].x = i - (hminfo.terrainWidth - 1)/2;
hminfo.heightMap[index].y = height;
hminfo.heightMap[index].z = j - (hminfo.terrainHeight - 1)/2;
k+=3;
}
k++;
}
int cols = hminfo.terrainWidth;
int rows = hminfo.terrainHeight;
//Create the grid
NumVertices = 2 * rows * cols;
NumFaces = (rows-1)*(cols-1)*2;
v = new struct HeightFieldVertex[NumVertices];
for(DWORD i = 0; i < rows; ++i)
{
for(DWORD j = 0; j < cols; ++j)
{
v[i*cols+j].pos.x = hminfo.heightMap[i*cols+j].x * dx;
v[i*cols+j].pos.y = (float(hminfo.heightMap[i*cols+j].y)/255.0) * dy;
v[i*cols+j].pos.z = hminfo.heightMap[i*cols+j].z * dz;
v[i*cols+j].texCoord = D3DXVECTOR2(j, i);
}
}
indices = new DWORD[NumFaces * 3];
k = 0;
for(DWORD i = 0; i < rows-1; i++)
{
for(DWORD j = 0; j < cols-1; j++)
{
indices[k] = i*cols+j; // Bottom left of quad
indices[k+1] = i*cols+j+1; // Bottom right of quad
indices[k+2] = (i+1)*cols+j; // Top left of quad
indices[k+3] = (i+1)*cols+j; // Top left of quad
indices[k+4] = i*cols+j+1; // Bottom right of quad
indices[k+5] = (i+1)*cols+j+1; // Top right of quad
k += 6; // next quad
}
}
//normals & tangents
std::vector<D3DXVECTOR3> tempNormal;
//normalized and unnormalized normals
D3DXVECTOR3 unnormalized(0.0f, 0.0f, 0.0f);
//tangent stuff
std::vector<D3DXVECTOR3> tempTangent;
D3DXVECTOR3 tangent(0.0f, 0.0f, 0.0f);
float tcU1, tcV1, tcU2, tcV2;
//Used to get vectors (sides) from the position of the verts
float vecX, vecY, vecZ;
//Two edges of our triangle
D3DXVECTOR3 edge1(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 edge2(0.0f, 0.0f, 0.0f);
//Compute face normals
//And Tangents
for(int i = 0; i < NumFaces; ++i)
{
//Get the vector describing one edge of our triangle (edge 0,2)
vecX = v[indices[(i*3)+1]].pos.x - v[indices[(i*3)]].pos.x;
vecY = v[indices[(i*3)+1]].pos.y - v[indices[(i*3)]].pos.y;
vecZ = v[indices[(i*3)+1]].pos.z - v[indices[(i*3)]].pos.z;
edge1 = D3DXVECTOR3(vecX, vecY, vecZ); //Create our first edge
//Get the vector describing another edge of our triangle (edge 2,1)
vecX = v[indices[(i*3)+2]].pos.x - v[indices[(i*3)]].pos.x;
vecY = v[indices[(i*3)+2]].pos.y - v[indices[(i*3)]].pos.y;
vecZ = v[indices[(i*3)+2]].pos.z - v[indices[(i*3)]].pos.z;
edge2 = D3DXVECTOR3(vecX, vecY, vecZ); //Create our second edge
//Cross multiply the two edge vectors to get the un-normalized face normal
D3DXVec3Cross(&unnormalized, &edge1, &edge2);
tempNormal.push_back(unnormalized);
//Find first texture coordinate edge 2d vector
tcU1 = v[indices[(i*3)+1]].texCoord.x - v[indices[(i*3)]].texCoord.x;
tcV1 = v[indices[(i*3)+1]].texCoord.y - v[indices[(i*3)]].texCoord.y;
//Find second texture coordinate edge 2d vector
tcU2 = v[indices[(i*3)+2]].texCoord.x - v[indices[(i*3)]].texCoord.x;
tcV2 = v[indices[(i*3)+2]].texCoord.y - v[indices[(i*3)]].texCoord.y;
//Find tangent using both tex coord edges and position edges
tangent.x = (tcV2 * edge1.x - tcV1 * edge2.x) / (tcU1 * tcV2 - tcU2 * tcV1);
tangent.y = (tcV2 * edge1.y - tcV1 * edge2.y) / (tcU1 * tcV2 - tcU2 * tcV1);
tangent.z = (tcV2 * edge1.z - tcV1 * edge2.z) / (tcU1 * tcV2 - tcU2 * tcV1);
tempTangent.push_back(tangent);
}
//Compute vertex normals (normal Averaging)
D3DXVECTOR4 normalSum(0.0f, 0.0f, 0.0f, 0.0f);
D3DXVECTOR4 tangentSum(0.0f, 0.0f, 0.0f, 0.0f);
int facesUsing = 0;
float tX, tY, tZ; //temp axis variables
//Go through each vertex
for(int i = 0; i < NumVertices; ++i)
{
//Check which triangles use this vertex
for(int j = 0; j < NumFaces; ++j)
{
if(indices[j*3] == i ||
indices[(j*3)+1] == i ||
indices[(j*3)+2] == i)
{
tX = normalSum.x + tempNormal[j].x;
tY = normalSum.y + tempNormal[j].y;
tZ = normalSum.z + tempNormal[j].z;
normalSum = D3DXVECTOR4(tX, tY, tZ, 0.0f); //If a face is using the vertex, add the unormalized face normal to the normalSum
facesUsing++;
}
}
//Get the actual normal by dividing the normalSum by the number of faces sharing the vertex
normalSum = normalSum / facesUsing;
facesUsing = 0;
//Check which triangles use this vertex
for(int j = 0; j < NumFaces; ++j)
{
if(indices[j*3] == i ||
indices[(j*3)+1] == i ||
indices[(j*3)+2] == i)
{
//We can reuse tX, tY, tZ to sum up tangents
tX = tangentSum.x + tempTangent[j].x;
tY = tangentSum.y + tempTangent[j].y;
tZ = tangentSum.z + tempTangent[j].z;
tangentSum = D3DXVECTOR4(tX, tY, tZ, 0.0f); //sum up face tangents using this vertex
facesUsing++;
}
}
//Get the actual normal by dividing the normalSum by the number of faces sharing the vertex
tangentSum = tangentSum / facesUsing;
//Normalize the normalSum vector and tangent
D3DXVec4Normalize(&normalSum, &normalSum);
D3DXVec4Normalize(&tangentSum, &tangentSum);
//Store the normal and tangent in our current vertex
v[i].normal.x = normalSum.x;
v[i].normal.y = normalSum.y;
v[i].normal.z = normalSum.z;
v[i].tangent.x = tangentSum.x;
v[i].tangent.y = tangentSum.y;
v[i].tangent.z = tangentSum.z;
D3DXVECTOR3 bit;
D3DXVec3Cross(&bit, &v[i].normal, &v[i].tangent);
v[i].bitangent = -1.0 * bit;
//Clear normalSum, tangentSum and facesUsing for next vertex
normalSum = D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f);
tangentSum = D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f);
facesUsing = 0;
}
////terrain AABB
//MinX = -1.0 * dx * (hminfo.terrainWidth - 1)/2;
//MinY = 0.0;
//MinZ = -1.0 * dz * (hminfo.terrainHeight - 1)/2;
//MaxX = dx * (hminfo.terrainWidth - 1)/2;
//MaxY = dy;
//MaxZ = dz * (hminfo.terrainHeight - 1)/2;
return true;
}
Fragment of Base.cpp
HeightField *hf = new HeightField(g_Terrain, *g_PhysicsSDK, *material, g_Terrain.hminfo.terrainWidth, g_Terrain.hminfo.terrainHeight);
g_PxScene->addActor(*(hf->g_pxHeightField));
I want to get something similar, but in order to work correctly
PVD - simulation Terrain
If you are familiar with the website rastertek for working with DirectX 10 or 11 in c++ within his updated DirectX 11 Terrain Series or version 2 a similar construct was created in tutorial 9 in which he calls Terrain Cells. The direct link to that tutorial can be found here: DirectX 11: Terrain Series2: Tutorial 9 - Terrain Cells. This should provide an excellent reference that I think is related toward the topic at hand and your initial question.
I have a square area on which I have to determine where the mouse pointing.
With D3DXIntersectTri I can tell IF the mouse pointing on it, but I have trouble calculating the x,y,z coordinates.
The drawing from vertex buffer, which initialized with the vertices array:
vertices[0].position = D3DXVECTOR3(-10, 0, -10);
vertices[1].position = D3DXVECTOR3(-10, 0, 10);
vertices[2].position = D3DXVECTOR3( 10, 0, -10);
vertices[3].position = D3DXVECTOR3( 10, 0, -10);
vertices[4].position = D3DXVECTOR3(-10, 0, 10);
vertices[5].position = D3DXVECTOR3( 10, 0, 10);
I have this method so far, this is not giving me the right coordinates (works only on a small part of the area, near two of the edges and more less accurate inside):
BOOL Area::getcoord( Ray& ray, D3DXVECTOR3& coord)
{
D3DXVECTOR3 rayOrigin, rayDirection;
rayDirection = ray.direction;
rayOrigin = ray.origin;
float d;
D3DXMATRIX matInverse;
D3DXMatrixInverse(&matInverse, NULL, &matWorld);
// Transform ray origin and direction by inv matrix
D3DXVECTOR3 rayObjOrigin,rayObjDirection;
D3DXVec3TransformCoord(&rayOrigin, &rayOrigin, &matInverse);
D3DXVec3TransformNormal(&rayDirection, &rayDirection, &matInverse);
D3DXVec3Normalize(&rayDirection,&rayDirection);
float u, v;
BOOL isHit1, isHit2;
D3DXVECTOR3 p1, p2, p3;
p1 = vertices[3].position;
p2 = vertices[4].position;
p3 = vertices[5].position;
isHit1 = D3DXIntersectTri(&p1, &p2, &p3, &rayOrigin, &rayDirection, &u, &v, &d);
isHit2 = FALSE;
if(!isHit1)
{
p1 = vertices[0].position;
p2 = vertices[1].position;
p3 = vertices[2].position;
isHit2 = D3DXIntersectTri(&p1, &p2, &p3, &rayOrigin, &rayDirection, &u, &v, &d);
}
if(isHit1)
{
coord.x = 1 * ((1-u-v)*p3.x + u*p3.y + v*p3.z);
coord.y = 0.2f;
coord.z = -1 * ((1-u-v)*p1.x + u*p1.y + v*p1.z);
D3DXVec3TransformCoord(&coord, &coord, &matInverse);
}
if(isHit2)
{
coord.x = -1 * ((1-u-v)*p3.x + u*p3.y + v*p3.z);
coord.y = 0.2f;
coord.z = 1 * ((1-u-v)*p1.x + u*p1.y + v*p1.z);
D3DXVec3TransformCoord(&coord, &coord, &matWorld);
}
return isHit1 || isHit2;
}
Barycentric coordinates don't work the way you used them. u and v define the weight of the source vectors. So if you want to calculate the hit point, you will have to compute
coord = u * p1 + v * p2 + (1 - u - v) * p3
Alternatively you can use the d ray parameter:
coord = rayOrigin + d * rDirection
Both ways should result in the same coordinate.