MuckyFoot-UrbanChaos/thrust/imp.cpp
2017-05-20 11:14:17 +10:00

1045 lines
18 KiB
C++

//
// Imports SEX files.
//
#include "always.h"
#include "imp.h"
//
// Returns TRUE if the normals are similar enough.
//
SLONG IMP_norm_similar(
float nx1,
float ny1,
float nz1,
float nx2,
float ny2,
float nz2)
{
float dprod;
dprod = nx1*nx2 + ny1*ny2 + nz1*nz2;
if (dprod > 0.999F)
{
return TRUE;
}
else
{
return FALSE;
}
}
//
// Normalises a vector.
//
void IMP_normalise_vector(
float *vx,
float *vy,
float *vz)
{
float len = sqrt(*vx * *vx + *vy * *vy + *vz * *vz);
if (len < 0.00001F)
{
*vx = 1.0F;
*vy = 1.0F;
*vz = 1.0F;
}
else
{
float overlen = 1.0F / len;
*vx *= overlen;
*vy *= overlen;
*vz *= overlen;
}
}
IMP_Mesh IMP_load(CBYTE *fname, float scale)
{
SLONG i;
SLONG j;
SLONG k;
SLONG l;
float x;
float y;
float z;
float r;
float g;
float b;
float u;
float v;
float sh;
float ss;
SLONG m;
SLONG v1;
SLONG v2;
SLONG p1;
SLONG p2;
SLONG p3;
SLONG t1;
SLONG t2;
SLONG t3;
SLONG e1;
SLONG e2;
SLONG e3;
SLONG p1o;
SLONG p2o;
SLONG v1o;
SLONG v2o;
CBYTE sided[16];
CBYTE alpha[16];
CBYTE tname[128];
CBYTE bname[128];
SLONG shared[3];
SLONG group;
SLONG match;
float pivot_x;
float pivot_y;
float pivot_z;
SLONG pivot_valid = FALSE;
SLONG offset_vert;
SLONG offset_tvert;
SLONG offset_mat;
SLONG max_mats;
SLONG max_verts;
SLONG max_tverts;
SLONG max_faces;
SLONG max_sverts;
SLONG max_quads;
SLONG max_edges;
Point3d fs_norm[3];
Point3d fs_u [3];
Point3d fs_v [3];
IMP_Svert fs[3];
IMP_Mat *im;
IMP_Vert *iv;
IMP_Tvert *it;
IMP_Face *ic;
IMP_Face *ico;
IMP_Svert *is;
IMP_Quad *iq;
IMP_Edge *ie;
IMP_Mesh ans;
CBYTE line[256];
CBYTE oname[32];
FILE *handle;
//
// Initialise the mesh.
//
max_mats = 4;
max_verts = 32;
max_tverts = 32;
max_faces = 32;
max_sverts = 32;
max_quads = 16;
max_edges = 32;
memset(&ans, 0, sizeof(ans));
ans.mat = (IMP_Mat *) malloc(sizeof(IMP_Mat ) * max_mats );
ans.vert = (IMP_Vert *) malloc(sizeof(IMP_Vert ) * max_verts );
ans.tvert = (IMP_Tvert *) malloc(sizeof(IMP_Tvert) * max_tverts);
ans.face = (IMP_Face *) malloc(sizeof(IMP_Face ) * max_faces );
ans.svert = (IMP_Svert *) malloc(sizeof(IMP_Svert) * max_sverts);
ans.quad = (IMP_Quad *) malloc(sizeof(IMP_Quad ) * max_quads );
ans.edge = (IMP_Edge *) malloc(sizeof(IMP_Edge ) * max_edges );
if (ans.mat == NULL ||
ans.vert == NULL ||
ans.tvert == NULL ||
ans.face == NULL ||
ans.svert == NULL ||
ans.quad == NULL ||
ans.edge == NULL)
{
goto file_error;
}
//
// Open the file.
//
handle = fopen(fname, "rb");
if (!handle)
{
goto file_error;
}
//
// Read the data.
//
while(fgets(line, 256, handle))
{
if (line[0] == '#')
{
//
// Ignore comments.
//
continue;
}
match = sscanf(line, "Triangle mesh: %s", oname);
if (match == 1)
{
if (ans.name[0] == '\000')
{
//
// This is the first mesh in the SEX file- use its name,
//
strcpy(ans.name, oname);
}
offset_vert = ans.num_verts;
offset_tvert = ans.num_tverts;
offset_mat = ans.num_mats;
continue;
}
match = sscanf(line, "Pivot: (%f,%f,%f)", &x, &y, &z);
if (match == 3)
{
//
// The pivot of the current object.
//
if (!pivot_valid)
{
pivot_x = x;
pivot_y = y;
pivot_z = z;
pivot_valid = TRUE;
}
else
{
//
// Only use the first pivot.
//
}
continue;
}
match = sscanf(line, "Material: DiffuseRGB (%f,%f,%f), shininess %f, shinstr %f, %s sided, %s alpha, diffuse %s bumpmap %s", &r, &b, &g, &sh, &ss, sided, alpha, tname, bname);
if (match == 9)
{
//
// Found a new material.
//
if (ans.num_mats >= max_mats)
{
//
// We have to allocate a larger materials array and copy
// over the data.
//
max_mats *= 2;
ans.mat = (IMP_Mat *) realloc(ans.mat, sizeof(IMP_Mat) * max_mats);
if (ans.mat == NULL)
{
goto file_error;
}
}
im = &ans.mat[ans.num_mats++];
im->r = r;
im->g = g;
im->b = b;
im->shininess = sh;
im->shinstr = ss;
im->alpha = (strcmp(alpha, "Filtered") == 0) ? IMP_ALPHA_FILTERED : IMP_ALPHA_ADDITIVE;
im->sided = (strcmp(sided, "Single") == 0) ? IMP_SIDED_SINGLE : IMP_SIDED_DOUBLE;
im->has_texture = (strcmp(tname, "none") != 0);
im->has_bumpmap = (strcmp(bname, "none") != 0);
strncpy(im->tname, tname, 32);
strncpy(im->bname, bname, 32);
continue;
}
match = sscanf(line, "Vertex: (%f,%f,%f)", &x, &y, &z);
if (match == 3)
{
//
// Found a point. Convert from 3ds orientation to a sensible one.
//
SWAP_FL(y, z);
z = -z;
x = -x;
x *= scale;
y *= scale;
z *= scale;
if (ans.num_verts >= max_verts)
{
//
// Lengthen the array.
//
max_verts *= 2;
ans.vert = (IMP_Vert *) realloc(ans.vert, sizeof(IMP_Vert) * max_verts);
if (ans.vert == NULL)
{
goto file_error;
}
}
iv = &ans.vert[ans.num_verts++];
iv->x = x;
iv->y = y;
iv->z = z;
continue;
}
match = sscanf(line, "Texture Vertex: (%f,%f)", &u, &v);
if (match == 2)
{
//
// Found a texture vertex.
//
if (ans.num_tverts >= max_tverts)
{
//
// Lengthen the array.
//
max_tverts *= 2;
ans.tvert = (IMP_Tvert *) realloc(ans.tvert, sizeof(IMP_Tvert) * max_tverts);
if (ans.tvert == NULL)
{
goto file_error;
}
}
it = &ans.tvert[ans.num_tverts++];
it->u = u;
it->v = v;
continue;
}
match = sscanf(line, "Face: Material %d xyz (%d,%d,%d) uv (%d,%d,%d) edge (%d,%d,%d) group %d", &m, &p1, &p2, &p3, &t1, &t2, &t3, &e1, &e2, &e3, &group);
if (match == 11)
{
//
// Found a face.
//
if (ans.num_faces >= max_faces)
{
//
// We have to allocate a larger faceerials array and copy
// over the data.
//
max_faces *= 2;
ans.face = (IMP_Face *) realloc(ans.face, sizeof(IMP_Face) * max_faces);
if (ans.face == NULL)
{
goto file_error;
}
}
p1 += offset_vert;
p2 += offset_vert;
p3 += offset_vert;
t1 += offset_tvert;
t2 += offset_tvert;
t3 += offset_tvert;
m += offset_mat;
ASSERT(WITHIN(p1, 0, ans.num_verts - 1));
ASSERT(WITHIN(p2, 0, ans.num_verts - 1));
ASSERT(WITHIN(p3, 0, ans.num_verts - 1));
ASSERT(WITHIN(t1, 0, ans.num_tverts - 1));
ASSERT(WITHIN(t2, 0, ans.num_tverts - 1));
ASSERT(WITHIN(t3, 0, ans.num_tverts - 1));
ASSERT(WITHIN(m, 0, ans.num_mats - 1));
if (!ans.mat[m].has_texture &&
!ans.mat[m].has_bumpmap)
{
//
// Set all the uv points to (0,0)- so that the sverts can
// be shared across all faces.
//
t1 = 0;
t2 = 0;
t3 = 0;
}
//
// Add the face to the mesh.
//
ic = &ans.face[ans.num_faces++];
ic->v[0] = p1;
ic->v[1] = p2;
ic->v[2] = p3;
ic->t[0] = t1;
ic->t[1] = t2;
ic->t[2] = t3;
ic->mat = m;
ic->group = group;
ic->flag = 0;
if (e1) {ic->flag |= IMP_FACE_FLAG_EDGE_A;}
if (e2) {ic->flag |= IMP_FACE_FLAG_EDGE_B;}
if (e3) {ic->flag |= IMP_FACE_FLAG_EDGE_C;}
//
// Calculate the face normal.
//
{
IMP_Vert *v1 = &ans.vert[p1];
IMP_Vert *v2 = &ans.vert[p2];
IMP_Vert *v3 = &ans.vert[p3];
float ax = v2->x - v1->x;
float ay = v2->y - v1->y;
float az = v2->z - v1->z;
float bx = v3->x - v1->x;
float by = v3->y - v1->y;
float bz = v3->z - v1->z;
float nx = ay*bz - az*by;
float ny = az*bx - ax*bz;
float nz = ax*by - ay*bx;
IMP_normalise_vector(
&nx,
&ny,
&nz);
ic->nx = nx;
ic->ny = ny;
ic->nz = nz;
}
//
// Calculate the vectors that lie along the u and v axis of the texture.
//
{
IMP_Vert *iv1 = &ans.vert[ic->v[0]];
IMP_Vert *iv2 = &ans.vert[ic->v[1]];
IMP_Vert *iv3 = &ans.vert[ic->v[2]];
IMP_Tvert *it1 = &ans.tvert[ic->t[0]];
IMP_Tvert *it2 = &ans.tvert[ic->t[1]];
IMP_Tvert *it3 = &ans.tvert[ic->t[2]];
float x1 = iv2->x - iv1->x;
float y1 = iv2->y - iv1->y;
float z1 = iv2->z - iv1->z;
float x2 = iv3->x - iv1->x;
float y2 = iv3->y - iv1->y;
float z2 = iv3->z - iv1->z;
float u1 = it2->u - it1->u;
float v1 = it2->v - it1->v;
float u2 = it3->u - it1->u;
float v2 = it3->v - it1->v;
float ucrossv = u1*v2 - u2*v1;
float overucrossv = 1.0F / ucrossv;
ic->dxdu = (x1*v2 - x2*v1) * overucrossv;
ic->dydu = (y1*v2 - y2*v1) * overucrossv;
ic->dzdu = (z1*v2 - z2*v1) * overucrossv;
ic->dxdv = (x1*u2 - x2*u1) * -overucrossv;
ic->dydv = (y1*u2 - y2*u1) * -overucrossv;
ic->dzdv = (z1*u2 - z2*u1) * -overucrossv;
IMP_normalise_vector(
&ic->dxdu,
&ic->dydu,
&ic->dzdu);
IMP_normalise_vector(
&ic->dxdv,
&ic->dydv,
&ic->dzdv);
}
continue;
}
}
//
// All ok.
//
fclose(handle);
//
// Construct the shared vertices.
//
for (i = 0; i < ans.num_faces; i++)
{
ic = &ans.face[i];
for (j = 0; j < 3; j++)
{
ASSERT(WITHIN(ic->v[j], 0, ans.num_verts - 1));
ASSERT(WITHIN(ic->t[j], 0, ans.num_tverts - 1));
//
// Create this shared vertex.
//
fs[j].vert = ic->v[j];
fs[j].u = ans.tvert[ic->t[j]].u;
fs[j].v = ans.tvert[ic->t[j]].v;
fs[j].mat = ic->mat;
fs[j].nx = 0.0F;
fs[j].ny = 0.0F;
fs[j].nz = 0.0F;
fs[j].dxdu = 0.0F;
fs[j].dydu = 0.0F;
fs[j].dzdu = 0.0F;
fs[j].dxdv = 0.0F;
fs[j].dydv = 0.0F;
fs[j].dzdv = 0.0F;
//
// Initialise the normal and the (u,v) vectors
//
fs_norm[j].x = 0;
fs_norm[j].y = 0;
fs_norm[j].z = 0;
fs_u[j].x = 0;
fs_u[j].y = 0;
fs_u[j].z = 0;
fs_v[j].x = 0;
fs_v[j].y = 0;
fs_v[j].z = 0;
shared[j] = 0;
}
//
// Build the normal of the shared vertices.
//
// Look through all the other faces to find faces of the
// same smoothing group that share this vertex.
//
for (j = 0; j < ans.num_faces; j++)
{
ico = &ans.face[j];
//
// A face can belong to no smoothing group- i.e. have
// a group of zero. Check for this case!
//
if ((ico->group & ic->group) || (ico == ic /* In case the face has no smoothing group */ ))
{
//
// These faces belong to the same smoothing group.
// Do they share any vertices?
//
for (k = 0; k < 3; k++)
for (l = 0; l < 3; l++)
{
if (ic->v[k] == ico->v[l])
{
//
// Update the normal at this shared vertex.
//
fs_norm[k].x += ico->nx;
fs_norm[k].y += ico->ny;
fs_norm[k].z += ico->nz;
//
// Update the u,v vectors at this shared vertex.
//
fs_u[k].x += ico->dxdu;
fs_u[k].y += ico->dydu;
fs_u[k].z += ico->dzdu;
fs_v[k].x += ico->dxdv;
fs_v[k].y += ico->dydv;
fs_v[k].z += ico->dzdv;
shared[k] += 1;
}
}
}
}
//
// Normalise the shared vertex normals and put the u,v vectors into
// the plane of the normal.
//
for (j = 0; j < 3; j++)
{
ASSERT(shared[j] > 0);
if (shared[j] > 1)
{
IMP_normalise_vector(
&fs_norm[j].x,
&fs_norm[j].y,
&fs_norm[j].z);
}
//
// Into the plane of the normal.
//
float dprod;
dprod =
fs_norm[j].x * fs_u[j].x +
fs_norm[j].y * fs_u[j].y +
fs_norm[j].z * fs_u[j].z;
fs_u[j].x -= dprod * fs_norm[j].x;
fs_u[j].y -= dprod * fs_norm[j].y;
fs_u[j].z -= dprod * fs_norm[j].z;
dprod =
fs_norm[j].x * fs_v[j].x +
fs_norm[j].y * fs_v[j].y +
fs_norm[j].z * fs_v[j].z;
fs_v[j].x -= dprod * fs_norm[j].x;
fs_v[j].y -= dprod * fs_norm[j].y;
fs_v[j].z -= dprod * fs_norm[j].z;
//
// Normalise...
//
IMP_normalise_vector(
&fs_u[j].x,
&fs_u[j].y,
&fs_u[j].z);
IMP_normalise_vector(
&fs_v[j].x,
&fs_v[j].y,
&fs_v[j].z);
}
//
// Look for sverts in the array that match.
//
for (j = 0; j < 3; j++)
{
for (k = ans.num_sverts - 1; k >= 0; k--)
{
is = &ans.svert[k];
if (is->vert == fs[j].vert &&
is->mat == fs[j].mat &&
is->u == fs[j].u &&
is->v == fs[j].v)
{
//
// Almost found a match! If the normal is the same, then we can use it.
//
if (IMP_norm_similar(
is->nx,
is->ny,
is->nz,
fs_norm[j].x,
fs_norm[j].y,
fs_norm[j].z))
{
//
// The normals are similar enough. Just use this shared vertex.
// Assume that the u,v vectors are going to be the same too...
//
ic->s[j] = k;
goto found_matching_svert;
}
}
}
//
// There is no svert in the array that matches our one. We
// have to create a new svert.
//
if (ans.num_sverts >= max_sverts)
{
//
// Lengthen the array.
//
max_sverts *= 2;
ans.svert = (IMP_Svert *) realloc(ans.svert, sizeof(IMP_Svert) * max_sverts);
if (ans.svert == NULL)
{
goto file_error;
}
}
fs[j].nx = fs_norm[j].x;
fs[j].ny = fs_norm[j].y;
fs[j].nz = fs_norm[j].z;
fs[j].dxdu = fs_u[j].x;
fs[j].dydu = fs_u[j].y;
fs[j].dzdu = fs_u[j].z;
fs[j].dxdv = fs_v[j].x;
fs[j].dydv = fs_v[j].y;
fs[j].dzdv = fs_v[j].z;
ans.svert[ans.num_sverts] = fs[j];
ic->s[j] = ans.num_sverts;
ans.num_sverts += 1;
found_matching_svert:;
}
}
//
// Finds the quads in the mesh.
//
for (i = 0; i < ans.num_faces; i++)
{
ic = &ans.face[i];
if ((ic->flag & (IMP_FACE_FLAG_EDGE_A|IMP_FACE_FLAG_EDGE_B|IMP_FACE_FLAG_EDGE_C)) == (IMP_FACE_FLAG_EDGE_A|IMP_FACE_FLAG_EDGE_B|IMP_FACE_FLAG_EDGE_C))
{
//
// This face is a triangle.
//
continue;
}
if (ic->flag & IMP_FACE_FLAG_QUADDED)
{
//
// Already part of a quad.
//
continue;
}
for (j = 0; j < 3; j++)
{
if (ic->flag & (IMP_FACE_FLAG_EDGE << j))
{
//
// This edge is okay.
//
}
else
{
//
// This is the shared edge of a quad. What two verts does it use?
//
p1 = j + 0;
p2 = j + 1;
if (p2 == 3) {p2 = 0;}
v1 = ic->v[p1];
v2 = ic->v[p2];
//
// Look for another triangle that uses this edge.
//
for (k = i + 1; k < ans.num_faces; k++)
{
ico = &ans.face[k];
if ((ico->flag & (IMP_FACE_FLAG_EDGE_A|IMP_FACE_FLAG_EDGE_B|IMP_FACE_FLAG_EDGE_C)) == (IMP_FACE_FLAG_EDGE_A|IMP_FACE_FLAG_EDGE_B|IMP_FACE_FLAG_EDGE_C))
{
//
// This face is a triangle.
//
continue;
}
if (ico->flag & IMP_FACE_FLAG_QUADDED)
{
//
// Already part of a quad.
//
continue;
}
//
// All quads must be co-planar
//
if (IMP_norm_similar(ic->nx, ic->ny, ic->nz, ico->nx, ico->ny, ico->nz))
{
continue;
}
for (l = 0; l < 3; l++)
{
if (ico->flag & (IMP_FACE_FLAG_EDGE << l))
{
//
// This is a real edge.
//
}
else
{
//
// This edge is the diagonal of a quad. Is it our one?
//
p1o = j + 0;
p2o = j + 1;
if (p2o == 3) {p2o = 0;}
v1o = ico->v[p1o];
v2o = ico->v[p2o];
if (v2o == v1 &&
v1o == v2)
{
//
// These two triangles are part of a quad togther.
//
if (ans.num_quads >= max_quads)
{
max_quads *= 2;
ans.quad = (IMP_Quad *) realloc(ans.quad, sizeof(IMP_Quad) * max_quads);
if (ans.quad == NULL)
{
goto file_error;
}
}
iq = &ans.quad[ans.num_quads++];
iq->v[0] = ic->v [(j + 1) % 3];
iq->v[1] = ic->v [(j + 2) % 3];
iq->v[2] = ic->v [(j + 0) % 3];
iq->v[3] = ico->v[(l + 2) % 3];
//
// Mark the two faces concerned as being part of a quad.
//
ic->flag |= IMP_FACE_FLAG_QUADDED;
ico->flag |= IMP_FACE_FLAG_QUADDED;
goto found_quad;
}
}
}
}
}
}
found_quad:;
}
//
// Find the edges of the mesh.
//
for (i = 0; i < ans.num_faces; i++)
{
ic = &ans.face[i];
//
// For each edge...
//
for (j = 0; j < 3; j++)
{
p1 = j + 0;
p2 = j + 1;
if (p2 == 3)
{
p2 = 0;
}
v1 = ic->v[p1];
v2 = ic->v[p2];
//
// Is there an edge between these two vertices already?
//
for (k = ans.num_edges - 1; k >= 0; k--)
{
ie = &ans.edge[k];
if (ie->v1 == v2 &&
ie->v2 == v1)
{
//
// We have found the other half of our edge!
//
if (ie->f2 == 0xffff)
{
//
// And the second edge hasn't been taken yet!
//
ie->f2 = i;
goto found_edge;
}
}
}
//
// We must create a new edge.
//
if (ans.num_edges >= max_edges)
{
max_edges *= 2;
ans.edge = (IMP_Edge *) realloc(ans.edge, sizeof(IMP_Edge) * max_edges);
if (ans.edge == NULL)
{
goto file_error;
}
}
ans.edge[ans.num_edges].v1 = v1;
ans.edge[ans.num_edges].v2 = v2;
ans.edge[ans.num_edges].f1 = i;
ans.edge[ans.num_edges].f2 = 0xffff; // 0xffff => No buddy found yet.
ans.num_edges += 1;
found_edge:;
}
}
ans.valid = TRUE;
return ans;
file_error:;
//
// Decallocate any memory.
//
IMP_free(&ans);
if (handle)
{
fclose(handle);
}
memset(&ans, 0, sizeof(ans));
ans.valid = FALSE;
return ans;
}
void IMP_free(IMP_Mesh *im)
{
if (im->mat ) {free(im->mat ); im->mat = NULL;}
if (im->vert ) {free(im->vert ); im->vert = NULL;}
if (im->tvert) {free(im->tvert); im->tvert = NULL;}
if (im->face ) {free(im->face ); im->face = NULL;}
if (im->svert) {free(im->svert); im->svert = NULL;}
if (im->quad ) {free(im->quad ); im->quad = NULL;}
}