[Closed] SDK Renderer – normals + smoothing group problems
The code I use – is simple enough, it almost works as it should.
Here you see the ‘cleanest’ normal I’m able to get, notice the glitch that runs down the center.
http://bullet4max.com/owncloud/index.php/s/CfstAIcEywZp9TR
Here I have only modified the smoothing groups, and I get this mess here:
(all though the viewport mesh looks fine).
http://bullet4max.com/owncloud/index.php/s/YmNNfXHhgyNuGwu
So, I’m trying to wrap my head around how I can solve this.
I have not worked with smoothing groups before, so I’m just not sure how to deal with it.
Do anyone of you have any idea on what I can do with this?
I just want a smooth mesh, that’s all
The code used for both images (for getting the normals) is this:
//currNode is the current node I'm preparing for rendering.
IGameNode *gmNode = pIgame->GetIGameNode(currNode);
IGameMesh * gm = (IGameMesh*)gmNode->GetIGameObject();
gm->InitializeData();
int numNorms = gm->GetNumberOfNormals();
for (int i = 0; i< numNorms; i++)
{
Point3 igamen;
if (gm->GetNormal(i, igamen, true))
{
igamen = igamen;
igamen = Normalize(igamen);
n[i].x = igamen.x;
n[i].y = igamen.y;
n[i].z = igamen.z;
}
else
{
n[i].x = 0.0f;
n[i].y = 0.0f;
n[i].z = 1.0f;
}
}
is this some kind of export ? why would you need to do this for the max renderer ?
No, it’s not a exporter. it’s a renderer for max (like mentalray\iray) that I’m coding.
And you define the mesh with the library that does the rendering.
It needs vert pos, vertex normal, and face indicies.
Everything is working – except the issue I have with the smooting as you see in the images I’ve linked to.
so it’s like a “game” vertex buffer renderer then ?
smoothing groups are not the issue for you here, in max they are a “convenient” way to set normals. It’s the way you are collecting the normals from the igame mesh they are very much like uv verts in the correlation comes from the relationship to the tri face corner position not their indexed order e.g.
vert[10] may not equal normal[10]
but
face[10]->vert[2] has normal face[10]->norm[2]
if that makes sense
It sounds like the opposite of what I wanted, but I’ll do anything to make it work, because I’m really tired of dealing with that now (spent a lot of time debugging it).
I’m running out of time for today (Getting late), but I have printed out some more debug info, this is what the current code is giving me, when I set up rendering for a cube that 1x1x1m centered in the scene.
Info: Igame normals: 24
Info: Igame verts: 8
Info: Igame faces: 12
Info: Igame uvs: 12
Writing Normal Position for igame normal at Index 0= x:0.000000 y:0.000000 z:-1.000000
Writing Normal Position for igame normal at Index 1= x:0.000000 y:0.000000 z:-1.000000
Writing Normal Position for igame normal at Index 2= x:0.000000 y:0.000000 z:-1.000000
Writing Normal Position for igame normal at Index 3= x:0.000000 y:0.000000 z:-1.000000
Writing Normal Position for igame normal at Index 4= x:0.000000 y:0.000000 z:1.000000
Writing Normal Position for igame normal at Index 5= x:0.000000 y:0.000000 z:1.000000
Writing Normal Position for igame normal at Index 6= x:0.000000 y:0.000000 z:1.000000
Writing Normal Position for igame normal at Index 7= x:0.000000 y:0.000000 z:1.000000
Writing Normal Position for igame normal at Index 8= x:0.000000 y:-1.000000 z:0.000000
Writing Normal Position for igame normal at Index 9= x:0.000000 y:-1.000000 z:0.000000
Writing Normal Position for igame normal at Index 10= x:0.000000 y:-1.000000 z:0.000000
Writing Normal Position for igame normal at Index 11= x:0.000000 y:-1.000000 z:0.000000
Writing Normal Position for igame normal at Index 12= x:1.000000 y:0.000000 z:0.000000
Writing Normal Position for igame normal at Index 13= x:1.000000 y:0.000000 z:0.000000
Writing Normal Position for igame normal at Index 14= x:1.000000 y:0.000000 z:0.000000
Writing Normal Position for igame normal at Index 15= x:1.000000 y:0.000000 z:0.000000
Writing Normal Position for igame normal at Index 16= x:0.000000 y:1.000000 z:0.000000
Writing Normal Position for igame normal at Index 17= x:0.000000 y:1.000000 z:0.000000
Writing Normal Position for igame normal at Index 18= x:0.000000 y:1.000000 z:0.000000
Writing Normal Position for igame normal at Index 19= x:0.000000 y:1.000000 z:0.000000
Writing Normal Position for igame normal at Index 20= x:-1.000000 y:0.000000 z:0.000000
Writing Normal Position for igame normal at Index 21= x:-1.000000 y:0.000000 z:0.000000
Writing Normal Position for igame normal at Index 22= x:-1.000000 y:0.000000 z:0.000000
Writing Normal Position for igame normal at Index 23= x:-1.000000 y:0.000000 z:0.000000
Writing vertex Position for igame vert at Index 0= x:-0.500000 y:-0.500000 z:0.000000
Writing vertex Position for igame vert at Index 1= x:0.500000 y:-0.500000 z:0.000000
Writing vertex Position for igame vert at Index 2= x:-0.500000 y:0.500000 z:0.000000
Writing vertex Position for igame vert at Index 3= x:0.500000 y:0.500000 z:0.000000
Writing vertex Position for igame vert at Index 4= x:-0.500000 y:-0.500000 z:1.000000
Writing vertex Position for igame vert at Index 5= x:0.500000 y:-0.500000 z:1.000000
Writing vertex Position for igame vert at Index 6= x:-0.500000 y:0.500000 z:1.000000
Writing vertex Position for igame vert at Index 7= x:0.500000 y:0.500000 z:1.000000
Writing face indicies igame face at Index 0 = x:0 y:2 z:3
Writing face indicies igame face at Index 1 = x:3 y:1 z:0
Writing face indicies igame face at Index 2 = x:4 y:5 z:7
Writing face indicies igame face at Index 3 = x:7 y:6 z:4
Writing face indicies igame face at Index 4 = x:0 y:1 z:5
Writing face indicies igame face at Index 5 = x:5 y:4 z:0
Writing face indicies igame face at Index 6 = x:1 y:3 z:7
Writing face indicies igame face at Index 7 = x:7 y:5 z:1
Writing face indicies igame face at Index 8 = x:3 y:2 z:6
Writing face indicies igame face at Index 9 = x:6 y:7 z:3
Writing face indicies igame face at Index 10 = x:2 y:0 z:4
Writing face indicies igame face at Index 11 = x:4 y:6 z:2
The array’s of points,normals and uv’s are the size igame is creating them, I’m kida feeding it the mesh directly as you see here.
The one thing I liked with Igame was the optimized vertex list, instead of ‘bruteforcing’ the export like this:
for each face
{
for each vert
{
get vert
}
}
which would give you 36 verts for a cube, that’s the main reason why I went for the Igame code instead, but if I understand you correctly then I need it similar to the psudo code above here?
I had a nightmare with the indicies doing it that way (which igame solved for me), but I could probably work around it again and give it one more go, because I have to get it solved.
I do not have time to look into it more until I get home from work tomorrow afternoon.
I wasn’t aware that igame did any vert buffer optimization, just a common interface for all object with a “mesh” (poly,tris, patches, nurbs and procedurals)
here’s an mxs obj exporter example code demo i created, you can use the same methods to create your mesh buffer either using igame or direct mesh access.
--**************************************************************************************
-- example of a simple game vertex buffer exporter
--**************************************************************************************
-- can't use built in hash as it hashes mirror points as the same, and we can't cycle the hash either
-- as we need the same hash for equivalent verts & normals so we use xor and large primes
fn hashPoint3 pnt = (bit.xor (bit.xor (pnt.x * 73856093) (pnt.y * 19349663)) (pnt.z * 83492791) ; )
fn hashPoint4 pnt = (bit.xor (bit.xor (bit.xor (pnt.x * 73856093) (pnt.y * 19349663)) (pnt.z * 83492791)) (pnt.w * 19393667) ; )
--*****************************************************************************************
-- our game vertex format
struct vertex
(
pos,
normal,
col,
uvw1,
uvw2,
hash,
fn hashit =
(
hash = bit.xor ((hashPoint3 pos) * 93944371) ((hashPoint3 normal) * 36311839);
hash = bit.xor hash ((hashPoint4 col) * 82895123);
hash = bit.xor hash ((hashPoint3 uvw1) * 19393541);
hash = bit.xor hash ((hashPoint3 uvw2) * 78149551);
),
fn printf = (format "% % % % % %
" pos normal col uvw1 uvw2 hash);
)
--**************************************************************************************
-- sort and search compares, the sort order is unimportant just as long as it's consitent, it would be
-- a nice optimization to use plain subtraction here as the values are integers unfortunately they are
-- very large integers and theres no guarantee they won't pass MIN_INT after the subtraction and
-- give an incorrect result. You could take the hash prime multipliers down to 3,5,7,11 etc I suppose
-- the gains aren't that great when stacked against the losses from the edit normals modifier.
fn compareVertexfn i j values: =
(
v1 = values[i].hash;
v2 = values[j].hash;
if v1 < v2 then 1 else if v1 > v2 then -1 else 0;
)
--***********************************************************************************
-- compares the raw against the optimized
fn compareVertexBinfn i j raw: opt: =
(
v1 = raw[i].hash;
v2 = opt[j].hash;
if v1 < v2 then 1 else if v1 > v2 then -1 else 0;
)
--***********************************************************************************
-- collects all the unique material IDs within a mesh
fn GetMeshMatIDs mObj =
(
matid = #{}
for f = 1 to mObj.numfaces do matid[(getfaceMatID mObj f)] = true;
matid as array;
)
--***********************************************************************************
-- collects all the faces with the same material ID
fn GetFacesWithMatID mObj matid =
(
faces = #{}
faces.count = mObj.numfaces;
for f in mObj.faces as bitarray do faces[f] = ((getfaceMatID mObj f) == matid);
faces;
)
--**************************************************************************************
-- collect 3 normals per face
fn CollectNormals msh faces nMod =
(
normals = #();
normals.count = faces.numberSet * 3;
ni = 1
for f in faces do
(
for v = 1 to 3 do
(
normals[ni] = nMod.GetNormal (nMod.GetNormalID f v);
ni += 1;
)
)
normals;
)
--*****************************************************************************************
-- generate the worst case scenerio, 3 verts per face every face
fn CollectRawVerts msh faces nMod tm =
(
getnormalfn = nMod.GetNormal;
getnormalIDfn = nMod.GetNormalID;
verts = #();
verts.count = faces.numberSet * 3;
vi = 1;
-- check for vertex alpha and second uvws
valpha = meshop.getMapSupport msh -2;
uvw2 = meshop.getMapSupport msh 2;
for f in faces do
(
geo_verts = getface msh f;
tex_verts = getTVFace msh f;
cpv_verts = getVCFace msh f;
val_verts = [0,0,0];
if valpha then val_verts = meshop.getmapface msh -2 f;
uvw2_verts = [0,0,0];
if uvw2 then uvw2_verts = meshop.getmapface msh 2 f;
for v = 1 to 3 do
(
gvert = (getvert msh geo_verts[v]) * tm;
uvw1_vert = getTVert msh tex_verts[v];
cvert = ((getVertColor msh cpv_verts[v]) as Point4);
if valpha then cvert.w = (meshop.getmapvert msh -2 val_verts[v]).x;
uvw2_vert = [0,0,0];
if uvw2 then uvw2_vert = meshop.getmapvert msh 2 uvw2_verts[v];
normal = getnormalfn (getnormalIDfn f v);
-- create the vertex and generate it's hash
verts[vi] = vertex gvert normal cvert uvw1_vert uvw2_vert;
verts[vi].hashit();
vi += 1;
)
)
verts;
)
--*****************************************************************************************
-- collect the orignal mesh indexing
fn CollectRawFaceIndices msh faces =
(
indices = #();
indices.count = faces.numberSet * 3;
vi = 1
for f in faces do
(
geo_verts = getface msh f;
for v = 1 to 3 do
(
indices[vi] = geo_verts[v];
vi += 1;
)
)
indices;
)
--**************************************************************************************
-- create the optimized version of the raw verts
fn OptimizeVerts raw =
(
numverts = raw.count;
-- create the index array
rawi = #{1..numverts} as array;
-- sort int indexes based on the raw data
qsort rawi compareVertexfn values:raw;
-- collect the indices of all the "unique" verts
--copylist = for i = 1 to numverts where i == bsearch i rawi compareVertexfn values:raw collect i;
copylist = #();
copylist.count = numverts;
ci = ri = 1;
copylist[ci] = rawi[ri]
while ri != numverts do
(
ri += 1;
if raw[copylist[ci]].hash != raw[rawi[ri]].hash then
(
ci += 1;
copylist[ci] = rawi[ri];
)
)
copylist.count = ci;
-- extract them from the raw verts
for v in copylist collect raw[v]
)
--***********************************************************************************
-- reconstruct the face indexing, the raw verts are in the correct order so we find where
-- each raw vert is in the optimised array and thats our index.
fn CreateFaces raw optimized =
(
opti = #{1..optimized.count} as array;
qsort opti compareVertexfn values:optimized;
for i = 1 to raw.count collect (bsearch i opti compareVertexBinfn raw:raw opt:optimized ; )
)
--**************************************************************************************
-- generates an array of vertex tangents from the vertex stream in face indices
-- binormal can then be found with B = (N × T).Tw
-- code courtesy of Eric Lengyel
fn ComputeTangents verts faces =
(
nverts = verts.count;
nfaces = faces.count/3;
tan1 = #();
tan1.count = nverts;
tan2 = #();
tan2.count = nverts;
for v = 1 to nverts do tan1[v] = tan2[v] = [0,0,0];
for f = 1 to faces.count by 3 do
(
c1 = faces[f]; c2 = faces[f+1]; c3 = faces[f+2];
a = verts[c1].pos; b = verts[c2].pos; c = verts[c3].pos;
w1 = verts[c1].uvw1; w2 = verts[c2].uvw1; w3 = verts[c3].uvw1;
ab = b - a;
ac = c - a;
s1 = w2.x - w1.x; s2 = w3.x - w1.x;
t1 = w2.y - w1.y; t2 = w3.y - w1.y;
r = 1.0;
denom = (s1 * t2 - s2 * t1);
if denom != 0.0 then r = 1.0 / denom;
sdir = [(t2 * ab.x - t1 * ac.x) * r, (t2 * ab.y - t1 * ac.y) * r, (t2 * ab.z - t1 * ac.z) * r];
tdir = [(s1 * ac.x - s2 * ab.x) * r, (s1 * ac.y - s2 * ab.y) * r, (s1 * ac.z - s2 * ab.z) * r];
tan1[c1] += sdir; tan1[c2] += sdir; tan1[c3] += sdir;
tan2[c1] += tdir; tan2[c2] += tdir; tan2[c3] += tdir;
)
tangents = #();
tangents.count = nverts;
for v = 1 to nverts do
(
n = verts[v].normal;
t = tan1[v];
tangents[v] = (Normalize (t - n * (dot n t))) as point4;
tangents[v].w = (if dot (cross n t) tan2[v] < 0 then -1.0 else 1.0);
)
tangents;
)
--*****************************************************************************************
-- get binormals array
fn ComputeBinormals verts tangents =
(
for v = 1 to verts.count collect
(
t = tangents[v];
normalize ((cross verts[v].normal t) * t.w);
)
)
--**************************************************************************************
-- test the routine by building a new mesh based on the data
fn BuildMesh verts faces =
(
nverts = verts.count;
nfaces = faces.count/3;
msh = mesh numverts:nverts numfaces:nfaces;
-- geometry
for v = 1 to nverts do setvert msh v verts[v].pos;
fi = 1;
for f = 1 to faces.count by 3 do
(
setface msh fi faces[f] faces[f+1] faces[f+2];
setEdgeVis msh fi 1 true;
setEdgeVis msh fi 2 true;
setEdgeVis msh fi 3 true;
fi += 1;
)
-- color per vertex
meshop.setMapSupport msh 0 true;
meshop.setNumMapVerts msh 0 nverts;
meshop.setNumMapFaces msh 0 nfaces;
for v = 1 to nverts do meshop.setMapVert msh 0 v (verts[v].col);
fi = 1;
for f = 1 to faces.count by 3 do
(
meshop.setMapFace msh 0 fi [faces[f], faces[f+1], faces[f+2]]
fi += 1;
)
-- texture verts
meshop.setNumMaps msh 3; -- vcolor, map1 map2
meshop.setMapSupport msh 1 true;
meshop.setNumMapVerts msh 1 nverts;
meshop.setNumMapFaces msh 1 nfaces;
for v = 1 to nverts do meshop.setMapVert msh 1 v verts[v].uvw1;
fi = 1;
for f = 1 to faces.count by 3 do
(
meshop.setMapFace msh 1 fi [faces[f], faces[f+1], faces[f+2]]
fi += 1;
)
meshop.setMapSupport msh 2 true;
meshop.setNumMapVerts msh 2 nverts;
meshop.setNumMapFaces msh 2 nfaces;
for v = 1 to nverts do meshop.setMapVert msh 2 v verts[v].uvw2;
fi = 1;
for f = 1 to faces.count by 3 do
(
meshop.setMapFace msh 2 fi [faces[f], faces[f+1], faces[f+2]]
fi += 1;
)
update msh;
-- add the normals
select msh;
modpanel.addmodtoselection (Nmod = edit_normals displayLength:0)
for v = 1 to nverts do
(
Nmod.SetNormal v verts[v].normal;
Nmod.SetNormalExplicit v explicit:true;
)
fi = 1;
for f = 1 to faces.count by 3 do
(
for c = 1 to 3 do Nmod.SetNormalID fi c faces[f + c - 1];
fi += 1;
)
ConverttoMesh msh;
)
--***********************************************************************************
-- quick and dirty OBJ exporter, btw no vertex coloring support in the obj file format was a serious
-- oversight, what were they thinking !
fn ExportAsObj objName verts find =
(
numverts = verts.count;
fname = (GetDir #export) + "/" + objName + ".obj";
fstream = openFile fname mode:"w+";
if fstream != undefined then
(
format "# 3ds Max Wavefront OBJ Exporter v100001 - (c)whenever klunkware
" to:fstream;
format "# File Created:%
" localTime to:fstream;
format "
# verts
" to:fstream;
for v = 1 to numverts do format "v % % %
" verts[v].pos.x verts[v].pos.y verts[v].pos.z to:fstream;
format "
# normals
" to:fstream;
for v = 1 to numverts do format "vn % % %
" verts[v].normal.x verts[v].normal.y verts[v].normal.z to:fstream;
format "
# uvw
" to:fstream;
for v = 1 to numverts do format "vt % % %
" verts[v].uvw.x verts[v].uvw.y verts[v].uvw.z to:fstream;
format "
# faces
" to:fstream
format "g %
" objName to:fstream;
for f = 1 to find.count by 3 do
format "f %/%/% %/%/% %/%/%
" find[f] find[f] find[f] find[f+1] find[f+1] find[f+1] find[f+2] find[f+2] find[f+2] to:fstream;
close fstream;
)
)
--**************************************************************************************
fn testtangents verts tangents =
(
for v = 1 to verts.count do
(
p = verts[v].pos;
n = verts[v].normal;
t = tangents[v];
b = normalize ((cross n t) * t.w);
tm = matrix3 (t as point3) b n p;
point transform:tm size:2 axistripod:on cross:off;
)
)
--*****************************************************************************************
-- main export loop
fn ExportMesh mObj = if canConvertTo mObj Editable_Mesh then
(
-- grab the mesh
msh = snapshot mObj;
-- compute the inverse tm so we can get positions in local space
localTM = (inverse (msh.transform));
-- check for color per vertex if not there create a default
if getNumCPVVerts msh == 0 then defaultVCFaces msh;
-- get the number of mat id's
matids = GetMeshMatIDs msh;
-- get normals
select msh;
setCommandPanelTaskMode mode:#modify
modpanel.addmodtoselection (norm = edit_normals displayLength:0);
for m in matids do
(
faces = GetFacesWithMatID msh m;
raw = CollectRawVerts msh faces norm localTM;
--rawi = CollectRawFaceIndices msh faces;
opt = OptimizeVerts raw;
indices = CreateFaces raw opt;
tangents = ComputeTangents opt indices;
binormals = ComputeBinormals opt tangents;
--testtangents opt tangents;
BuildMesh opt indices;
--ExportAsObj (mObj.name + "_" + m as string) opt indices;
select msh;
)
delete msh;
)
delete objects
sph = sphere segs:16 mapcoords:on pos:[75,0,0]
ConverttoPoly sph
polyop.setFaceSmoothGroup sph #{49..80} 4;
polyop.SetFaceColor sph 0 #{9..16, 25..32, 41..48, 57..64, 73..80, 89..96, 105..112, 121..128} (color 128 128 128)
update sph;
clearlistener()
ts = timestamp();
ExportMesh sph;
print (timestamp() - ts);
in c++ something like (theres not error checking btw)…
static int hashPoint3(Point3& p) { return ((int)p.x * 73856093)^((int)p.y * 19349663)^((int)p.z * 83492791); }
struct vertex : public MaxHeapOperators
{
static const int hashTable[];
Point3 p;
Point3 n;
Point3 uv;
unsigned int index;
int hash;
vertex() : index(0), hash(0) {}
bool operator==(const vertex& v) const { return hash == v.hash; }
bool operator!=(const vertex& v) const { return hash != v.hash; }
bool operator>(const vertex& v) const { return hash > v.hash; }
bool operator<(const vertex& v) const { return hash < v.hash; }
void hashit() { hash = hashPoint3(p) * hashTable[0] ^ hashPoint3(n) * hashTable[1] ^ hashPoint3(uv) * hashTable[2]; }
};
const int vertex::hashTable[] = { 36311839,82895123,19393541 };
typedef vertex* vertexPtr;
int CompareVertexFn(const void* i, const void* j)
{
if(**(vertexPtr*)i < **(vertexPtr*)j)
return -1;
if(**(vertexPtr*)i > **(vertexPtr*)j)
return 1;
return 0;
}
void GetFaceRNormals(Mesh& mesh, int fi, Point3* normals)
{
Face& face = mesh.faces[fi];
DWORD fsmg = face.getSmGroup();
if(fsmg == 0)
{
normals[0] = normals[1] = normals[2] = mesh.getFaceNormal(fi);
return;
}
MtlID fmtl = face.getMatID();
DWORD* fverts = face.getAllVerts();
for(int v = 0; v < 3; ++v)
{
RVertex& rvert = mesh.getRVert(fverts[v]);
int numNormals = (int)(rvert.rFlags & NORCT_MASK);
if(numNormals == 1)
normals[v] = rvert.rn.getNormal();
else
{
for(int n = 0; n < numNormals; ++n)
{
RNormal& rn = rvert.ern[n];
if((fsmg & rn.getSmGroup()) && fmtl == rn.getMtlIndex())
{
normals[v] = rn.getNormal();
break;
}
}
}
}
}
vertexPtr CollectRawVerts(Mesh& mesh, int rawcount)
{
int numfaces = mesh.numFaces;
vertexPtr rawverts = new vertex[rawcount];
mesh.checkNormals(TRUE);
Face* faces = mesh.faces;
Point3* verts = mesh.verts;
TVFace* tvfaces = mesh.tvFace;
Point3* uvverts = mesh.tVerts;
for(int f = 0, i = 0; f < numfaces; ++f, ++faces, ++tvfaces)
{
Point3 fnormals[3];
GetFaceRNormals(mesh,f,fnormals);
for(int v = 0; v < 3; ++v)
{
vertex& rv = rawverts[i++];
rv.p = verts[faces->v[v]];
rv.n = fnormals[v];
rv.uv = uvverts[tvfaces->t[v]];
rv.hashit();
}
}
return rawverts;
}
vertexPtr CreateOptimizeVertexList(vertexPtr rawverts, int numverts, int& numoutverts)
{
vertexPtr* vptrs = new vertexPtr[numverts];
vertexPtr vptr = rawverts;
for(int i = 0; i < numverts; ++i, ++vptr)
vptrs[i] = vptr;
qsort(vptrs, numverts, sizeof(vertexPtr), CompareVertexFn);
int* copylist = new int[numverts];
unsigned int cc = 0, ri = 0;
copylist[cc] = vptrs[ri] - rawverts;
while(++ri < numverts)
{
int index = vptrs[ri] - rawverts;
if(rawverts[copylist[cc]] != rawverts[index])
copylist[++cc] = index;
}
numoutverts = cc + 1;
vertexPtr optverts = new vertex[numoutverts];
for(int i = 0; i < numoutverts; ++i)
optverts[i] = rawverts[copylist[i]];
delete [] copylist;
delete [] vptrs;
return optverts;
}
unsigned int* CreateOptimizeFaceIndices(vertexPtr raw, int rawcount, vertexPtr opt, int optcount)
{
vertexPtr* vptrs = new vertexPtr[optcount];
unsigned int* faces = new unsigned int[rawcount];
vertexPtr vptr = opt;
for(int i = 0; i < optcount; ++i, ++vptr)
vptrs[i] = vptr;
qsort(vptrs, optcount,sizeof(vertexPtr), CompareVertexFn);
for(int i = 0;i < rawcount; ++i)
{
vertexPtr key = &raw[i];
// find the correct index of a raw vert in the optimized array
vertexPtr* result = (vertexPtr*)bsearch(&key, vptrs, optcount, sizeof(vertexPtr), CompareVertexFn);
if(result)
faces[i] = *result - opt;
}
delete [] vptrs;
return faces;
}
called like this
int rawcount = mesh->numFaces * 3;
int optcount = 0;
vertexPtr rawverts = CollectRawVerts(*mesh, rawcount);
vertexPtr optverts = CreateOptimizeVertexList(rawverts, rawcount, optcount);
unsigned int* indices = CreateOptimizeFaceIndices(rawverts, rawcount, optverts, optcount);
then cleaned with
delete [] rawverts;
delete [] optverts;
delete [] indices;
I cannot thank you enough, I appreciate this help so much!
I will start to implement this when the kids has gone to bed tonight.
expanding the vertex is pretty simple say to adding vertex colour or mapping 2 add them to the vertex struct, hash and collection routine. Also the unused index param in vertex is there to collect the original base vert index.
you’ll probably find that the renderer will be just as fast rendering the raw verts (worse case) compare to the optimized… gfx cards are so fast at running through the vertex streams these days it takes a lot to notice any difference (we canned stripping a long time ago because any improvement was too small to measure) the hit would be in memory usage (might be a problem is games where fix ram and gfx mem can be an issue not so much on a desktop machine/workstation)
there was a error in the c++ collection code fixed now
also had to change the point3 hash function