Ray tracing obj files diffuse shading issue - c++

The repository (GitHub)
I'm having issues with my diffuse shading for my models (It does not arise when rendering primitives.
What's interesting here to note is I think that when you look at the left reflective sphere, the shading appears normal (I might be wrong on this, basing on observation).
Low poly bunny and a triangle
Low poly bunny and 2 reflective spheres
Cube and 2 reflective spheres
I'm not sure what I'm doing wrong, as the normals are calculated each time I create a triangle in the constructor. I am using tinyobjloader to load my models, here is the TriangleMesh intersection algorithm.
FPType TriangleMesh::GetIntersection(const Ray &ray)
{
for(auto &shape : shapes)
{
size_t index_offset = 0;
for(size_t f = 0; f < shape.mesh.num_face_vertices.size(); ++f) // faces (triangles)
{
int fv = shape.mesh.num_face_vertices[f];
tinyobj::index_t &idx0 = shape.mesh.indices[index_offset + 0]; // v0
tinyobj::index_t &idx1 = shape.mesh.indices[index_offset + 1]; // v1
tinyobj::index_t &idx2 = shape.mesh.indices[index_offset + 2]; // v2
Vec3d &v0 = Vec3d(attrib.vertices[3 * idx0.vertex_index + 0], attrib.vertices[3 * idx0.vertex_index + 1], attrib.vertices[3 * idx0.vertex_index + 2]);
Vec3d &v1 = Vec3d(attrib.vertices[3 * idx1.vertex_index + 0], attrib.vertices[3 * idx1.vertex_index + 1], attrib.vertices[3 * idx1.vertex_index + 2]);
Vec3d &v2 = Vec3d(attrib.vertices[3 * idx2.vertex_index + 0], attrib.vertices[3 * idx2.vertex_index + 1], attrib.vertices[3 * idx2.vertex_index + 2]);
Triangle tri(v0, v1, v2);
if(tri.GetIntersection(ray))
return tri.GetIntersection(ray);
index_offset += fv;
}
}
}
The Triangle Intersection algorithm.
FPType Triangle::GetIntersection(const Ray &ray)
{
Vector3d v0v1 = v1 - v0;
Vector3d v0v2 = v2 - v0;
Vector3d pvec = ray.GetDirection().Cross(v0v2);
FPType det = v0v1.Dot(pvec);
// ray and triangle are parallel if det is close to 0
if(abs(det) < BIAS)
return false;
FPType invDet = 1 / det;
FPType u, v;
Vector3d tvec = ray.GetOrigin() - v0;
u = tvec.Dot(pvec) * invDet;
if(u < 0 || u > 1)
return false;
Vector3d qvec = tvec.Cross(v0v1);
v = ray.GetDirection().Dot(qvec) * invDet;
if(v < 0 || u + v > 1)
return false;
FPType t = v0v2.Dot(qvec) * invDet;
if(t < BIAS)
return false;
return t;
}
I think this is because when I'm handling all my object intersections, the triangle mesh is regarded as 1 object only, so it only returns 1 normal, when I'm trying to get the object normals: code
Color Trace(const Vector3d &origin, const Vector3d &direction, const std::vector<std::shared_ptr<Object>> &sceneObjects, const int indexOfClosestObject,
const std::vector<std::shared_ptr<Light>> &lightSources, const int &depth = 0)
{
if(indexOfClosestObject != -1 && depth <= DEPTH) // not checking depth for infinite mirror effect (not a lot of overhead)
{
std::shared_ptr<Object> sceneObject = sceneObjects[indexOfClosestObject];
Vector3d normal = sceneObject->GetNormalAt(origin);
screenshot of debug
EDIT: I have solved the issue and now shading works properly: https://github.com/MrCappuccino/Tracey/blob/testing/src/TriangleMesh.cpp#L35-L48

If you iterate all your faces and return on the first face you hit, it may happen that you hit faces which are behind other faces and therefore not really the face you want to hit, so you would have to measure the length of your ray and return the intersection for the shortest ray.

Related

Problem with PathTracing ShadowRay, Spheres all black

So i'm making a raytracer in OpenGL, fully shader based, and i'm struggling to know where the problem is with my Shadow rays. If i multiply the radiance of the object by the shadowRay output, it seems like only the "edge" of the sphere is lighten up
I verified multiple times the code without finding where the problem comes from.
This is what i get:
vec3 TraceShadowRay(vec3 hitPoint, vec3 normal, Object objects[3])
{
Light pointLight;
pointLight.position = vec3(0, 80, 0);
pointLight.intensity = 2;
Ray ShadowRay;
ShadowRay.origin = hitPoint + normal * 1e-4;
ShadowRay.dir = normalize(pointLight.position - ShadowRay.origin);
ShadowRay.t = 100000;
//ShadowRay.dir = vec3(0, 1, 0);
for(int i = 0; i < 3; ++i)
{
if(objects[i].type == 0)
{
if(interectSphere(objects[i].position, objects[i].radius, ShadowRay))
{
return vec3(0);
}
}
if(objects[i].type == 1)
{
if(intersectPlane(objects[i].normal, objects[i].position, ShadowRay))
{
return vec3(0);
}
}
}
float AngleNormalShadow = dot(ShadowRay.dir, normal);
clamp(AngleNormalShadow, 0, 1);
return GetLight(ShadowRay.origin, pointLight);// * AngleNormalShadow;
}
The getLight function:
vec3 GetLight(vec3 origin, Light light)
{
return vec3(1, 1, 1) * light.intensity;
//float dist = sqrt( ((origin.x - light.position.x) * (origin.x - light.position.x)) + ((origin.y - light.position.y) * (origin.y - light.position.y)));
//return (vec3(1, 1, 1) * light.intensity) / (4 * M_PI * ((origin - light.position).length * (origin - light.position).length));
}
The intersectSphere function:
bool interectSphere(const vec3 center, float radius, inout Ray r)
{
vec3 o = r.origin;
vec3 d = r.dir;
vec3 v = o - center;
float b = 2 * dot(v, d);
float c = dot(v, v) - radius*radius;
float delta = b*b - 4 * c;
if(delta < 1e-4)
return false;
float t1 = (-b - sqrt(delta))/2;
float t2 = (-b + sqrt(delta))/2;
if(t1 < t2)
{
r.t = t1;
r.t2 = t2;
}
else if(t2 < t1)
{
r.t = t2;
r.t2 = t1;
}
r.reflectionNormal = normalize((r.origin + r.dir * r.t) - center);
return true;
}
The result expected is a nice shaded sphere with light coming from the top of the spheres
Could it be a missing negation? Looks like interectSphere() returns true when there is a collision, but the calling code in TraceShadowRay() bails out when it returns true.
old:
if(interectSphere(objects[i].position, objects[i].radius, ShadowRay))
{
return vec3(0);
}
new:
if(!interectSphere(objects[i].position, objects[i].radius, ShadowRay))
{
return vec3(0);
}

Ray vs ellipsoid intersection

I am trying to implement ray vs ellipsoid intersection by "squishing" space and doing ray vs sphere:
create mat3 S with ellipsoid radius at diagonal
squish ray by multiplying start and direction by an inverse of S
intersect ray with sphere of radius 1.0 in local space
multiply hitPoint by S to unsquish it.
Here is ray vs sphere:
float P = glm::dot(dir, sphereCenter-start);
float L = glm::distance(start, sphereCenter);
float d = sqrt(L*L - P*P);
if (d < radius) {
float x0 = sqrt(1.f - d*d);
hitPoint = start + dir*(P - x0);
hitNormal = glm::normalize(hitPoint - sphereCenter);
}
else if (d == radius) {
hitPoint = start + dir*P;
hitNormal = glm::normalize(hitPoint - sphereCenter);
}
else {
return false;
}
if (glm::distance(start, hitPoint) > dist) return false;
return true;
Here is the squishing part:
glm::vec3 S = start;
glm::vec3 Dir = dir;
auto sphereCenter = thisEntity()->transform()->getPosition();
auto scale = thisEntity()->transform()->getScale();
glm::mat3 q = glm::mat3(0);
float x = _radius.x * scale.x;
float y = _radius.y * scale.y;
float z = _radius.z * scale.z;
q[0][0] = x;
q[1][1] = y;
q[2][2] = z;
glm::mat3 qI = glm::inverse(q);
S = qI * S;
Dir = qI * Dir;
//calculate hit point in world space squished
glm::vec3 hitPoint, hitNormal;
if (!IntersectionsMath::instance()->segmentVsSphere(sphereCenter, S, Dir, dist, 1.f, hitPoint, hitNormal)) return;
hitPoint = q * hitPoint;
hit.pushHit(hitPoint, hitNormal, this);
Current ray sphere code is for world position, i'm trying to make it work at the origin so it shouldn't matter. Ray vs regular sphere works fine, ellipsoid is the problem.
I spent a lot of time on this and something somewhere is wrong.
Problem:
The center of scaling matters.
Solution:
Perform the scaling about the center of the ellipsoid.
... and not the origin as you are doing right now. This is because, although the direction of the ray will be the same (it is just a directional vector), the relative displacement between the scaled source and center of the sphere will be different:
Scaling about origin (current code):
Source S' = qI * S, center C' = qI * C --- S' - C' = qI * (S - C)
Scaling about ellipsoid center (correct procedure):
Source S" = qI * (S - C), center C" = C --- S" - C" = qI * (S - C) - C
The two displacements differ by the position of the original ellipsoid; thus your current ray will likely miss / give false positives.
Corrected code:
// scale about the ellipsoid's position by subtracting before multiplying
// more appropriate name would be "ellipseCenter" to avoid confusion
S_ = qI * (S - sphereCenter);
// this ::normalize should really be in the intersection function
Dir_ = glm::normalize(qI * Dir);
// calculate hit point in world space squished
// ... but around the origin in the squashed coordinate system
glm::vec3 hitPoint, hitNormal;
if (!IntersectionsMath::instance()->segmentVsSphere(
glm::vec3::ZERO, S_, Dir_,
dist, 1.f,
hitPoint, hitNormal)) return;
// re-apply the offset
hitPoint = q * hitPoint + sphereCenter
// problem: hitNormal will not be correct for the ellipsoid when scaled
// solution: divide through each component by square of respective semi-axis
// (will provide proof upon request)
hitNormal.x /= (x * x); hitNormal.y /= (y * y); hitNormal.z /= (z * z);

DirectX/C++: Marching Cubes Indexing

I've implemented the Marching Cube algorithm in a DirectX environment (To test and have fun). Upon completion, I noticed that the resulting model looks heavily distorted, as if the indices were off.
I've attempted to extract the indices, but I think the vertices are ordered correctly already, using the lookup tables, examples at http://paulbourke.net/geometry/polygonise/ . The current build uses a 15^3 volume.
Marching cubes iterates over the array as normal:
for (float iX = 0; iX < CellFieldSize.x; iX++){
for (float iY = 0; iY < CellFieldSize.y; iY++){
for (float iZ = 0; iZ < CellFieldSize.z; iZ++){
MarchCubes(XMFLOAT3(iX*StepSize, iY*StepSize, iZ*StepSize), StepSize);
}
}
}
The MarchCube function is called:
void MC::MarchCubes(){
...
int Corner, Vertex, VertexTest, Edge, Triangle, FlagIndex, EdgeFlags;
float Offset;
XMFLOAT3 Color;
float CubeValue[8];
XMFLOAT3 EdgeVertex[12];
XMFLOAT3 EdgeNorm[12];
//Local copy
for (Vertex = 0; Vertex < 8; Vertex++) {
CubeValue[Vertex] = (this->*fSample)(
in_Position.x + VertexOffset[Vertex][0] * Scale,
in_Position.y + VertexOffset[Vertex][1] * Scale,
in_Position.z + VertexOffset[Vertex][2] * Scale
);
}
FlagIndex = 0;
Intersection calculations:
...
//Test vertices for intersection.
for (VertexTest = 0; VertexTest < 8; VertexTest++){
if (CubeValue[VertexTest] <= TargetValue)
FlagIndex |= 1 << VertexTest;
}
//Find which edges are intersected by the surface.
EdgeFlags = CubeEdgeFlags[FlagIndex];
if (EdgeFlags == 0){
return;
}
for (Edge = 0; Edge < 12; Edge++){
if (EdgeFlags & (1 << Edge)) {
Offset = GetOffset(CubeValue[EdgeConnection[Edge][0]], CubeValue[EdgeConnection[Edge][1]], TargetValue); // Get offset function definition. Needed!
EdgeVertex[Edge].x = in_Position.x + VertexOffset[EdgeConnection[Edge][0]][0] + Offset * EdgeDirection[Edge][0] * Scale;
EdgeVertex[Edge].y = in_Position.y + VertexOffset[EdgeConnection[Edge][0]][1] + Offset * EdgeDirection[Edge][1] * Scale;
EdgeVertex[Edge].z = in_Position.z + VertexOffset[EdgeConnection[Edge][0]][2] + Offset * EdgeDirection[Edge][2] * Scale;
GetNormal(EdgeNorm[Edge], EdgeVertex[Edge].x, EdgeVertex[Edge].y, EdgeVertex[Edge].z); //Need normal values
}
}
And the original implementation gets pushed into a holding struct for DirectX.
for (Triangle = 0; Triangle < 5; Triangle++) {
if (TriangleConnectionTable[FlagIndex][3 * Triangle] < 0) break;
for (Corner = 0; Corner < 3; Corner++) {
Vertex = TriangleConnectionTable[FlagIndex][3 * Triangle + Corner];3 * Triangle + Corner]);
GetColor(Color, EdgeVertex[Vertex], EdgeNorm[Vertex]);
Data.VertexData.push_back(XMFLOAT3(EdgeVertex[Vertex].x, EdgeVertex[Vertex].y, EdgeVertex[Vertex].z));
Data.NormalData.push_back(XMFLOAT3(EdgeNorm[Vertex].x, EdgeNorm[Vertex].y, EdgeNorm[Vertex].z));
Data.ColorData.push_back(XMFLOAT4(Color.x, Color.y, Color.z, 1.0f));
}
}
(This is the same ordering as the original GL implementation)
Turns out, I missed a parenthesis showing operator precedence.
EdgeVertex[Edge].x = in_Position.x + (VertexOffset[EdgeConnection[Edge][0]][0] + Offset * EdgeDirection[Edge][0]) * Scale;
EdgeVertex[Edge].y = in_Position.y + (VertexOffset[EdgeConnection[Edge][0]][1] + Offset * EdgeDirection[Edge][1]) * Scale;
EdgeVertex[Edge].z = in_Position.z + (VertexOffset[EdgeConnection[Edge][0]][2] + Offset * EdgeDirection[Edge][2]) * Scale;
Corrected, obtained Visine; resumed fun.

Sphere Collision Algorithm-Overal Velocities Keep increacing (C++, OpenGL)

Im having trouble with my "Box Full of Sphere Particles" project. ive created a spheres[number_of_spheres][position,radius,velocity..] array. A bounding box (sides parallel to orthonormal axis).
Spheres that collide with the box have their velocity reversed on the dimention of the collision. that works ok.
For the sphere-to-sphere collision its pretty simple, i detect the collision by comparing distance to sum of radii. Now im trying to make them simply exchange velocities on the axis of collision. But the simulation runs fine for a while then the speeds keep increasing till its out of control.
bool CollisionDetect(int i, int j)
{
if(i==j)
{
return false;
}
float xx = (spheres[i][0]-spheres[j][0])*(spheres[i][0]-spheres[j][0]);
float yy = (spheres[i][1]-spheres[j][1])*(spheres[i][1]-spheres[j][1]);
float zz = (spheres[i][2]-spheres[j][2])*(spheres[i][2]-spheres[j][2]);
if( (xx + yy + zz) <= (spheres[i][3] + spheres[j][3])*(spheres[i][3] + spheres[j][3]) )
{
return true;
}
else
{
return false;
}
}
void CollisionSolve(int i, int j)
{
float m1,m2,m21;
Vec3 vel1,vel2,pos1,pos2; //spheres info
Vec3 nv1n,nv1b,nv1t;
Vec3 nv2n,nv2b,nv2t;
Vec3 N,T,B,X,Y,Z; //collision plane and world orthonormal basis
X.x=1;
X.y=0;
X.z=0;
Y.x=0;
Y.y=1;
Y.z=0;
Z.x=0;
Z.y=0;
Z.z=1;
pos1.x = spheres[i][0];
pos2.x = spheres[j][0];
pos1.y = spheres[i][1];
pos2.y = spheres[j][1];
pos1.z = spheres[i][2];
pos2.z = spheres[j][2];
vel1.x = spheres[i][4];
vel2.x = spheres[j][4];
vel1.y = spheres[i][5];
vel2.y = spheres[j][5];
vel1.z = spheres[i][6];
vel2.z = spheres[j][6];
m1 = spheres[i][8];
m2 = spheres[j][8]; //mass (for later)
N = minus(pos2,pos1); //get N vector (connecting centers of spheres)
N = Normalize(N);
T=X;
B = crossProduct(N,T); //find first perpendicular axis to N
if (B.x==0 && B.y==0 && B.z==0) //then vector parallel to X axis -
{
T=Y; //try Y axis
B = crossProduct(N,T);
}
T = crossProduct(N,B); //find second perpendicular axis to N
T = Normalize(T);
B = Normalize(B);
if (simplespherecollision)
{
nv1n = projectUonV (vel1 , N);
nv2n = projectUonV (vel2 , N);
vel1 = minus (vel1,nv1n);
vel2 = minus (vel2,nv2n);
vel1 = plus (vel1,nv2n);
vel2 = plus (vel2,nv1n);
//simply switch speed (for test)
}
/*/---THIS IS COMMENTED OUT (FIRST METHOD USED - DIDNT WORK)--------------------------------------------
nv1n = projectUonV (vel1 , N);
nv2n = projectUonV (vel2 , N);
nv1t = projectUonV (vel1 , T);
nv2t = projectUonV (vel2 , T);
nv1b = projectUonV (vel1 , B);
nv2b = projectUonV (vel2 , B); //project velocities on new orthonormal basis
vel1 = plus(nv1t , plus(nv1b , nv2n)); //project velocities back to world basis
vel2 = plus(nv2t , plus( nv2b , nv1n)); //by adding the sub vectors with swiched Xn
/*/----------------------------------------------------------------------
spheres[i][4] = vel1.x;
spheres[i][5] = vel1.y;
spheres[i][6] = vel1.z;
spheres[j][4] = vel2.x;
spheres[j][5] = vel2.y;
spheres[j][6] = vel2.z; //reasign velocities to spheres
}
}
And this is the Vec3 struct (just in case)
struct Vec3
{
float x, y, z;
};
Vec3 crossProduct(const Vec3& v1, const Vec3& v2)
{
Vec3 r;
r.x = (v1.y*v2.z) - (v1.z*v2.y);
r.y = (v1.z*v2.x) - (v1.x*v2.z);
r.z = (v1.x*v2.y) - (v1.y*v2.x);
return r;
}
Vec3 minus(const Vec3& v1, const Vec3& v2)
{
Vec3 r;
r.x = v1.x - v2.x;
r.y = v1.y - v2.y;
r.z = v1.z - v2.z;
return r;
}
Vec3 plus(const Vec3& v1, const Vec3& v2)
{
Vec3 r;
r.x = v1.x + v2.x;
r.y = v1.y + v2.y;
r.z = v1.z + v2.z;
return r;
}
double dotProduct(const Vec3& v1, const Vec3& v2)
{
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
Vec3 scale(const Vec3& v, double a)
{
Vec3 r;
r.x = v.x * a;
r.y = v.y * a;
r.z = v.z * a;
return r;
}
Vec3 projectUonV(const Vec3& u, const Vec3& v)
{
Vec3 r;
r = scale(v,dotProduct(u,v));
return r;
}
int distanceSquared(const Vec3& v1, const Vec3& v2)
{
Vec3 delta = minus(v2, v1);
return dotProduct(delta, delta);
}
Vec3 Normalize (const Vec3& v)
{
Vec3 r;
r=v;
int mag = sqrt(dotProduct(v,v));
r.x = v.x/mag;
r.y = v.y/mag;
r.z = v.z/mag;
return r;
}
And this is my Render function witch draws the spheres and the bounding box and calls the other functions
void Render()
{
//CLEARS FRAME BUFFER ie COLOR BUFFER& DEPTH BUFFER (1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clean up the colour of the window
// and the depth buffer
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(-MAX_X/2,-MAX_Y/2,-MAX_Z*4);
glTranslatef (dx,0,dz);
//------------------------------------------------------------------------------------------------
//-----------------------------BOUNDING BOX----------------------------------------------------
glPushMatrix();
glTranslatef(MAX_X/2,MAX_Y/2,MAX_Z/2);
glColor4f(0.9,0.6,0.8,0.3);
glScalef(MAX_X,MAX_Y,MAX_Z);
glutSolidCube(1);
glutWireCube(1);
glPopMatrix();
//-----------------------------WALL COLLISION DETECTION-----ALWAYS ON--------------------------------
for (int i = 0; i<PART_NUM; i++)
{
if(spheres[i][0] > (MAX_X - spheres[i][3]) && spheres[i][4] > 0)
{
spheres[i][4] = spheres[i][4]*-1;
wallcollcount++;
}
if(spheres[i][1] > (MAX_Y - spheres[i][3]) && spheres[i][5] > 0)
{
spheres[i][5] = spheres[i][5]*-1;
wallcollcount++;
}
if(spheres[i][2] > (MAX_Z - spheres[i][3]) && spheres[i][6] > 0)
{
spheres[i][6] = spheres[i][6]*-1;
wallcollcount++;
}
if(spheres[i][0] < spheres[i][3] && spheres[i][4] < 0)
{
spheres[i][4] = spheres[i][4]*-1;
wallcollcount++;
}
if(spheres[i][1] < spheres[i][3] && spheres[i][5] < 0)
{
spheres[i][5] = spheres[i][5]*-1;
wallcollcount++;
}
if(spheres[i][2] < spheres[i][3] && spheres[i][6] < 0)
{
spheres[i][6] = spheres[i][6]*-1;
wallcollcount++;
}
//--------------------------------------Sphere ColDit -------------------------
if (spherecollision || simplespherecollision)
{
for(int j=i+1; j<PART_NUM; j++)
{
if(CollisionDetect(i,j))
{
spherecollcount++;
CollisionSolve(i,j);
}
}
}
//-----------------------------------------------DRAW--------------------------------
glPushMatrix();
glTranslatef(spheres[i][0],spheres[i][1],spheres[i][2]);
glColor3f( (float) (i+1)/(PART_NUM) , 1-(float)(i+1)/(PART_NUM) , 0.5);
glutSolidSphere(spheres[i][3], 18,18);
glPopMatrix();
}
//---------------------------------------------------------------------------------------------------------------/
glutSwapBuffers(); // All drawing commands applied to the
// hidden buffer, so now, bring forward
// the hidden buffer and hide the visible one
}
There are two things conserved in a collision:
momentum
and
energy
So far your calculations just consider the momentum (a simply speed swap assumes a so called central collision and identical masses of the spheres). If computers were infinitely precise this would work out perfectly. But computers have only limited precisions and so roundoff errors will creep in.
The simple solution for that problem is to correct the momentum part (which is mass · velocity) for the deviation in energy (1/2 mass · velocity²). This works because energy depends by the square of the velocity so small deviations on the speeds involved will create large energy deviations which you can use for correcting the calculation.
I.e. you calculate the total energy before the collision and then after the collision. Then you take the ratio sqrt(E_before / E_after) and scale the speeds after the calculation with that.
If you want to be really accurate you could do relativistic momentum and energy transfer ;)

Realtime object painting

I am trying to perform a realtime painting to the object texture. Using Irrlicht for now, but that does not really matter.
So far, i've got the right UV coordinates using this algorithm:
find out which object's triangle user selected (raycasting, nothing
really difficult)
find out the UV (baricentric) coordinates of intersection point on
that triangle
find out the UV (texture) coordinates of each triangle vertex
find out the UV (texture) coordinates of intersection point
calculate the texture image coordinates for intersection point
But somehow, when i am drawing in the point i got in the 5th step on texture image, i get totally wrong results. So, when drawing a rectangle in cursor point, the X (or Z) coordinate of its is inverted:
Here's the code i am using to fetch texture coordinates:
core::vector2df getPointUV(core::triangle3df tri, core::vector3df p)
{
core::vector3df
v0 = tri.pointC - tri.pointA,
v1 = tri.pointB - tri.pointA,
v2 = p - tri.pointA;
float dot00 = v0.dotProduct(v0),
dot01 = v0.dotProduct(v1),
dot02 = v0.dotProduct(v2),
dot11 = v1.dotProduct(v1),
dot12 = v1.dotProduct(v2);
float invDenom = 1.f / ((dot00 * dot11) - (dot01 * dot01)),
u = (dot11 * dot02 - dot01 * dot12) * invDenom,
v = (dot00 * dot12 - dot01 * dot02) * invDenom;
scene::IMesh* m = Mesh->getMesh(((scene::IAnimatedMeshSceneNode*)Model)->getFrameNr());
core::array<video::S3DVertex> VA, VB, VC;
video::SMaterial Material;
for (unsigned int i = 0; i < m->getMeshBufferCount(); i++)
{
scene::IMeshBuffer* mb = m->getMeshBuffer(i);
video::S3DVertex* vertices = (video::S3DVertex*) mb->getVertices();
for (unsigned long long v = 0; v < mb->getVertexCount(); v++)
{
if (vertices[v].Pos == tri.pointA)
VA.push_back(vertices[v]); else
if (vertices[v].Pos == tri.pointB)
VB.push_back(vertices[v]); else
if (vertices[v].Pos == tri.pointC)
VC.push_back(vertices[v]);
if (vertices[v].Pos == tri.pointA || vertices[v].Pos == tri.pointB || vertices[v].Pos == tri.pointC)
Material = mb->getMaterial();
if (VA.size() > 0 && VB.size() > 0 && VC.size() > 0)
break;
}
if (VA.size() > 0 && VB.size() > 0 && VC.size() > 0)
break;
}
core::vector2df
A = VA[0].TCoords,
B = VB[0].TCoords,
C = VC[0].TCoords;
core::vector2df P(A + (u * (C - A)) + (v * (B - A)));
core::dimension2du Size = Material.getTexture(0)->getSize();
CursorOnModel = core::vector2di(Size.Width * P.X, Size.Height * P.Y);
int X = Size.Width * P.X, Y = Size.Height * P.Y;
// DRAWING SOME RECTANGLE
Material.getTexture(0)->lock(true);
Device->getVideoDriver()->setRenderTarget(Material.getTexture(0), true, true, 0);
Device->getVideoDriver()->draw2DRectangle(video::SColor(255, 0, 100, 75), core::rect<s32>((X - 10), (Y - 10),
(X + 10), (Y + 10)));
Device->getVideoDriver()->setRenderTarget(0, true, true, 0);
Material.getTexture(0)->unlock();
return core::vector2df(X, Y);
}
I just wanna make my object paintable in realtime. My current problems are: wrong texture coordinate calculation and non-unique vertex UV coordinates (so, drawing something on the one side of the dwarfe's axe would draw the same on the other side of that axe).
How should i do this?
I was able to use your codebase and get it to work for me.
Re your second problem "non-unique vertex UV coordinates":
Well you are absolutely right, you need unique vertexUVs to get this working, which means that you have to unwrap you models and don't make use of shared uv-space for e.g. mirrored elements and stuff. (e.g. left/right boot - if they use the same uv-space, you'll paint automatically on both, where you want the one to be red and the other to be green). You can check out "uvlayout" (tool) or the uv-unwrap modifier ind 3ds max.
Re the first and more important problem: "**wrong texture coordinate calculation":
the calculation of your baycentric coordinates is correct, but as i suppose your input-data is wrong. I assume you get the triangle and the collisionPoint by using irrlicht's CollisionManager and TriangleSelector. The problem is, that the positions of the triangle's vertices (which you get as returnvalue from the collisionTest) is in WorldCoordiates. But you'll need them in ModelCoordinates for the calculation, so here's what you need to do:
pseudocode:
add the node which contains the mesh of the hit triangle as parameter to getPointUV()
get the inverse absoluteTransformation-Matrix by calling node->getAbsoluteTransformation() [inverse]
transform the vertices of the triangle by this inverse Matrix and use those values for the rest of the method.
Below you'll find my optimized method wich does it for a very simple mesh (one mesh, only one meshbuffer).
Code:
irr::core::vector2df getPointUV(irr::core::triangle3df tri, irr::core::vector3df p, irr::scene::IMeshSceneNode* pMeshNode, irr::video::IVideoDriver* pDriver)
{
irr::core::matrix4 inverseTransform(
pMeshNode->getAbsoluteTransformation(),
irr::core::matrix4::EM4CONST_INVERSE);
inverseTransform.transformVect(tri.pointA);
inverseTransform.transformVect(tri.pointB);
inverseTransform.transformVect(tri.pointC);
irr::core::vector3df
v0 = tri.pointC - tri.pointA,
v1 = tri.pointB - tri.pointA,
v2 = p - tri.pointA;
float dot00 = v0.dotProduct(v0),
dot01 = v0.dotProduct(v1),
dot02 = v0.dotProduct(v2),
dot11 = v1.dotProduct(v1),
dot12 = v1.dotProduct(v2);
float invDenom = 1.f / ((dot00 * dot11) - (dot01 * dot01)),
u = (dot11 * dot02 - dot01 * dot12) * invDenom,
v = (dot00 * dot12 - dot01 * dot02) * invDenom;
irr::video::S3DVertex A, B, C;
irr::video::S3DVertex* vertices = static_cast<irr::video::S3DVertex*>(
pMeshNode->getMesh()->getMeshBuffer(0)->getVertices());
for(unsigned int i=0; i < pMeshNode->getMesh()->getMeshBuffer(0)->getVertexCount(); ++i)
{
if( vertices[i].Pos == tri.pointA)
{
A = vertices[i];
}
else if( vertices[i].Pos == tri.pointB)
{
B = vertices[i];
}
else if( vertices[i].Pos == tri.pointC)
{
C = vertices[i];
}
}
irr::core::vector2df t2 = B.TCoords - A.TCoords;
irr::core::vector2df t1 = C.TCoords - A.TCoords;
irr::core::vector2df uvCoords = A.TCoords + t1*u + t2*v;
return uvCoords;
}