r689 - trunk

lordhavoc at icculus.org lordhavoc at icculus.org
Fri Apr 7 20:45:03 EDT 2006


Author: lordhavoc
Date: 2006-04-07 20:45:02 -0400 (Fri, 07 Apr 2006)
New Revision: 689

Modified:
   trunk/util.c
   trunk/util.h
Log:
added a util_convertobj command
rewrote/replaced Util_WriteModel with Util_BuildModel system, which
allows easier construction of md5mesh files by featuring vertex lookups
and adaptive allocations (so you can simply add a triangle to the mesh
for instance)


Modified: trunk/util.c
===================================================================
--- trunk/util.c	2006-04-08 00:42:44 UTC (rev 688)
+++ trunk/util.c	2006-04-08 00:45:02 UTC (rev 689)
@@ -240,48 +240,377 @@
 #undef ReadNext
 }
 
-// FIXME: this is very unlikely to overflow but it should be made buffer size safe at some point
-void Util_WriteModel(const char *filename, NUint32 nummeshes, Util_Mesh *meshes)
+void Util_BuildModel_Begin(Util_BuildModel *b)
 {
+	memset(b, 0, sizeof(*b));
+}
+
+void Util_BuildModel_End(Util_BuildModel *b)
+{
+	NUint32 i;
+	Util_BuildModel_Surface *surface;
+	for (i = 0, surface = b->data_surfaces;i < b->num_surfaces;i++, surface++)
+	{
+		Mem_Free(&surface->data_element3i);
+		Mem_Free(&surface->data_vertex8f);
+	}
+	Mem_Free(&b->data_surfaces);
+	memset(b, 0, sizeof(*b));
+}
+
+NUint32 Util_BuildModel_AddSurface(Util_BuildModel *b, const char *materialname)
+{
+	Util_BuildModel_Surface *surface;
+	if (b->num_surfaces >= b->max_surfaces)
+	{
+		b->max_surfaces = Max(b->max_surfaces * 2, 16);
+		Mem_ReAlloc(Global_Zone, &b->data_surfaces, b->max_surfaces * sizeof(Util_BuildModel_Surface));
+	}
+	surface = b->data_surfaces + b->num_surfaces;
+	strncpy(surface->materialname, materialname, sizeof(surface->materialname) - 1);
+	b->num_surfaces++;
+	return b->num_surfaces - 1;
+}
+
+NUint32 Util_BuildModel_AddVertex(Util_BuildModel *b, NUint32 surfaceindex, float x, float y, float z, float nx, float ny, float nz, float s, float t)
+{
+	float *v;
+	Util_BuildModel_Surface *surface = b->data_surfaces + surfaceindex;
+	if (surface->num_vertices >= surface->max_vertices)
+	{
+		surface->max_vertices = Max(surface->max_vertices * 2, 1024);
+		Mem_ReAlloc(Global_Zone, &surface->data_vertex8f, surface->max_vertices * sizeof(float[8]));
+	}
+	v = surface->data_vertex8f + surface->num_vertices * 8;
+	v[0] = x;
+	v[1] = y;
+	v[2] = z;
+	v[3] = nx;
+	v[4] = ny;
+	v[5] = nz;
+	v[6] = s;
+	v[7] = t;
+	surface->num_vertices++;
+	return surface->num_vertices - 1;
+}
+
+NUint32 Util_BuildModel_FindOrAddVertex(Util_BuildModel *b, NUint32 surfaceindex, float x, float y, float z, float nx, float ny, float nz, float s, float t)
+{
+	NUint32 i;
+	float *v;
+	Util_BuildModel_Surface *surface = b->data_surfaces + surfaceindex;
+	for (i = 0, v = surface->data_vertex8f;i < surface->num_vertices;i++, v += 8)
+		if (fabs(v[0] - x) < 0.0001 && fabs(v[1] - y) < 0.0001 && fabs(v[2] - z) < 0.0001 && fabs(v[3] - nx) < 0.0001 && fabs(v[4] - ny) < 0.0001 && fabs(v[5] - nz) < 0.0001 && fabs(v[6] - s) < 0.0001 && fabs(v[7] - t) < 0.0001)
+			return i;
+	return Util_BuildModel_AddVertex(b, surfaceindex, x, y, z, nx, ny, nz, s, t);
+}
+
+NUint32 Util_BuildModel_AddTriangle(Util_BuildModel *b, NUint32 surfaceindex, int e0, int e1, int e2)
+{
+	Util_BuildModel_Surface *surface = b->data_surfaces + surfaceindex;
+	NUint32 *e;
+	if (surface->num_triangles >= surface->max_triangles)
+	{
+		surface->max_triangles = Max(surface->max_triangles * 2, 1024);
+		Mem_ReAlloc(Global_Zone, &surface->data_element3i, surface->max_triangles * sizeof(int[3]));
+	}
+	e = surface->data_element3i + surface->num_triangles * 3;
+	e[0] = e0;
+	e[1] = e1;
+	e[2] = e2;
+	surface->num_triangles++;
+	return surface->num_triangles - 1;
+}
+
+void Util_BuildModel_Write(Util_BuildModel *b, const char *filename)
+{
+	// FIXME: this is very unlikely to overflow but it should be made buffer size safe at some point
 	NUint32 i, num;
 	Nsize textsize, textmaxsize;
 	char *text;
+	Util_BuildModel_Surface *surface;
 	textmaxsize = 0x100000;
-	for (i = 0;i < nummeshes;i++)
-		textmaxsize += meshes[i].numvertices * 60 + meshes[i].numtriangles * 40;
+	for (i = 0, surface = b->data_surfaces;i < b->num_surfaces;i++, surface++)
+		textmaxsize += surface->num_vertices * 60 + surface->num_triangles * 40;
 	textsize = 0;
 	text = Mem_Alloc(Global_Zone, textmaxsize);
-	textsize += snprintf(text + textsize, textmaxsize - textsize, "MD5Version 10\ncommandline \"\"\n\nnumJoints 1\nnumMeshes %u\n\njoints {\n\t\"ROOT\"\t-1 ( 0.000000 0.000000 0.000000 ) ( 0.000000 0.000000 0.000000 ) // \n}\n", nummeshes);
-	for (i = 0;i < nummeshes;i++)
+	textsize += snprintf(text + textsize, textmaxsize - textsize, "MD5Version 10\ncommandline \"\"\n\nnumJoints 1\nnumMeshes %u\n\njoints {\n\t\"ROOT\"\t-1 ( 0.000000 0.000000 0.000000 ) ( 0.000000 0.000000 0.000000 ) // \n}\n", b->num_surfaces);
+	for (i = 0, surface = b->data_surfaces;i < b->num_surfaces;i++, surface++)
 	{
-		if (textmaxsize < textsize + meshes[i].numvertices * 100 + meshes[i].numtriangles * 100 + meshes[i].numvertices * 100 + 1000)
+		if (textmaxsize < textsize + surface->num_vertices * 100 + surface->num_triangles * 100 + surface->num_vertices * 100 + 1000)
 		{
-			textmaxsize = textsize + meshes[i].numvertices * 200 + meshes[i].numtriangles * 200 + meshes[i].numvertices * 200 + 2000;
+			textmaxsize = textsize + surface->num_vertices * 200 + surface->num_triangles * 200 + surface->num_vertices * 200 + 2000;
 			Mem_ReAlloc(Global_Zone, &text, textmaxsize);
 		}
-		textsize += snprintf(text + textsize, textmaxsize - textsize, "\nmesh {\n\tshader \"%s\"\n\n\tnumverts %u\n", meshes[i].materialname, meshes[i].numvertices);
-		for (num = 0;num < meshes[i].numvertices;num++)
-			textsize += snprintf(text + textsize, textmaxsize - textsize, "\tvert %u ( %.6f %.6f ) %u 1\n", num, meshes[i].texcoord2f[num*2+0], meshes[i].texcoord2f[num*2+1], num);
-		textsize += snprintf(text + textsize, textmaxsize - textsize, "\n\tnumtris %u\n", meshes[i].numtriangles);
-		for (num = 0;num < meshes[i].numtriangles;num++)
-			textsize += snprintf(text + textsize, textmaxsize - textsize, "\ttri %u %u %u %u\n", num, meshes[i].element3i[num*3+0], meshes[i].element3i[num*3+1], meshes[i].element3i[num*3+2]);
-		textsize += snprintf(text + textsize, textmaxsize - textsize, "\n\tnumweights %u\n", meshes[i].numvertices);
-		for (num = 0;num < meshes[i].numvertices;num++)
-			textsize += snprintf(text + textsize, textmaxsize - textsize, "\tweight %u 0 1.000000 ( %.6f %.6f %.6f )\n", num, meshes[i].vertex3f[num*3+0], meshes[i].vertex3f[num*3+1], meshes[i].vertex3f[num*3+2]);
+		textsize += snprintf(text + textsize, textmaxsize - textsize, "\nmesh {\n\tshader \"%s\"\n\n\tnumverts %u\n", surface->materialname, surface->num_vertices);
+		for (num = 0;num < surface->num_vertices;num++)
+			textsize += snprintf(text + textsize, textmaxsize - textsize, "\tvert %u ( %.6f %.6f ) %u 1\n", num, surface->data_vertex8f[num*8+6], surface->data_vertex8f[num*8+7], num);
+		textsize += snprintf(text + textsize, textmaxsize - textsize, "\n\tnumtris %u\n", surface->num_triangles);
+		for (num = 0;num < surface->num_triangles;num++)
+			textsize += snprintf(text + textsize, textmaxsize - textsize, "\ttri %u %u %u %u\n", num, surface->data_element3i[num*3+0], surface->data_element3i[num*3+1], surface->data_element3i[num*3+2]);
+		textsize += snprintf(text + textsize, textmaxsize - textsize, "\n\tnumweights %u\n", surface->num_vertices);
+		for (num = 0;num < surface->num_vertices;num++)
+			textsize += snprintf(text + textsize, textmaxsize - textsize, "\tweight %u 0 1.000000 ( %.6f %.6f %.6f )\n", num, surface->data_vertex8f[num*8+0], surface->data_vertex8f[num*8+1], surface->data_vertex8f[num*8+2]);
 		textsize += snprintf(text + textsize, textmaxsize - textsize, "}\n");
 	}
 	File_WriteFile(filename, text, textsize);
 	Mem_Free(&text);
 }
 
+#if 1
+void Util_BuildModel_ImportOBJ(Util_BuildModel *b, const char *inputname, const char *text, const char *textend)
+#else
+static NUint32 default_remapcoords[3] = {0, 1, 2};
+static float default_scalecoords[3] = {1, 1, 1};
+
+void Util_BuildModel_ImportOBJ(Util_BuildModel *b, const char *inputname, const char *text, const char *textend, Nbool flip, NUint32 remapcoords[3], float scalecoords[3])
+#endif
+{
+	NUint32 linenumber = 0;
+	NSint32 surfaceindex = -1;
+	NUint32 num_vertices = 0, max_vertices = 0;
+	NUint32 num_normals = 0, max_normals = 0;
+	NUint32 num_texcoords = 0, max_texcoords = 0;
+	float *data_vertex3f = NULL;
+	float *data_normal3f = NULL;
+	float *data_texcoord2f = NULL;
+#if 0
+	if (remapcoords == NULL)
+		remapcoords = default_remapcoords;
+	if (scalecoords == NULL)
+		scalecoords = default_scalecoords;
+#endif
+	while (text < textend)
+	{
+		Nbool comment = false;
+		NUint32 argc = 0;
+		char argv[64][256];
+		// parse the line
+		linenumber++;
+		while (text < textend && *text != '\r' && *text != '\n')
+		{
+			if (comment || *text <= ' ')
+				text++;
+			else if (*text == '#')
+				comment = true;
+			else
+			{
+				NUint32 charnum;
+				for (charnum = 0;text < textend && *text > ' ';text++)
+					if (charnum < sizeof(argv[0]) - 1)
+						argv[argc][charnum++] = *text;
+				argv[argc++][charnum] = 0;
+			}
+		}
+		if (*text == '\r' && text[1] == '\n')
+			text++;
+		if (*text == '\n')
+			text++;
+		// now we have a command
+		if (argc < 1)
+			continue;
+		if (!String_Compare(argv[0], "v"))
+		{
+			// v -4.99960e-2 0.250000 -1.00000
+			if (argc == 4)
+			{
+				if (num_vertices >= max_vertices)
+				{
+					max_vertices = Max(max_vertices * 2, 1024);
+					Mem_ReAlloc(Global_Zone, &data_vertex3f, max_vertices * sizeof(float[3]));
+				}
+#if 1
+				data_vertex3f[num_vertices*3+0] = atof(argv[1]);
+				data_vertex3f[num_vertices*3+2] = atof(argv[2]);
+				data_vertex3f[num_vertices*3+1] = atof(argv[3]);
+#else
+				data_vertex3f[num_vertices*3+remapcoords[0]] = atof(argv[1]) * scalecoords[0];
+				data_vertex3f[num_vertices*3+remapcoords[1]] = atof(argv[2]) * scalecoords[1];
+				data_vertex3f[num_vertices*3+remapcoords[2]] = atof(argv[3]) * scalecoords[2];
+#endif
+				num_vertices++;
+			}
+			else
+				Console_Printf("util_convertobj: %s:%i: command %s takes %i parameters\n", inputname, linenumber, argv[0], 3);
+		}
+		else if (!String_Compare(argv[0], "vn"))
+		{
+			// vn 1.99019e-6 2.65359e-5 -1.000000
+			if (argc == 4)
+			{
+				if (num_normals >= max_normals)
+				{
+					max_normals = Max(max_normals * 2, 1024);
+					Mem_ReAlloc(Global_Zone, &data_normal3f, max_normals * sizeof(float[3]));
+				}
+#if 1
+				data_normal3f[num_normals*3+0] = atof(argv[1]);
+				data_normal3f[num_normals*3+2] = atof(argv[2]);
+				data_normal3f[num_normals*3+1] = atof(argv[3]);
+#else
+				data_normal3f[num_normals*3+remapcoords[0]] = atof(argv[1]) * scalecoords[0];
+				data_normal3f[num_normals*3+remapcoords[1]] = atof(argv[2]) * scalecoords[1];
+				data_normal3f[num_normals*3+remapcoords[2]] = atof(argv[3]) * scalecoords[2];
+#endif
+				num_normals++;
+			}
+			else
+				Console_Printf("util_convertobj: %s:%i: command %s takes %i parameters\n", inputname, linenumber, argv[0], 3);
+		}
+		else if (!String_Compare(argv[0], "vt"))
+		{
+			// vt 0.00000e+0 1.99019e-6
+			if (argc == 3)
+			{
+				if (num_texcoords >= max_texcoords)
+				{
+					max_texcoords = Max(max_texcoords * 2, 1024);
+					Mem_ReAlloc(Global_Zone, &data_texcoord2f, max_texcoords * sizeof(float[2]));
+				}
+				data_texcoord2f[num_texcoords*2+0] = atof(argv[1]);
+				data_texcoord2f[num_texcoords*2+1] = -atof(argv[2]);
+				num_texcoords++;
+			}
+			else
+				Console_Printf("util_convertobj: %s:%i: command %s takes %i parameters\n", inputname, linenumber, argv[0], 2);
+		}
+		else if (!String_Compare(argv[0], "usemtl"))
+		{
+			// usemtl mtrl/chrome
+			if (argc == 2)
+				surfaceindex = Util_BuildModel_AddSurface(b, argv[1]);
+			else
+				Console_Printf("util_convertobj: %s:%i: command %s takes %i parameters\n", inputname, linenumber, argv[0], 1);
+		}
+		else if (!String_Compare(argv[0], "f"))
+		{
+			// f 1/11/5 18/56/71 17/58/67
+			NUint32 i;
+			NUint32 indices[64];
+			if (argc >= 4)
+			{
+				if (surfaceindex == -1)
+					surfaceindex = Util_BuildModel_AddSurface(b, "default");
+				for (i = 0;i < argc - 1;i++)
+				{
+					NUint32 j;
+					const char *s = argv[i+1];
+					float v[3];
+					float vn[3];
+					float tc[2];
+					// if there are 3 indices, they are vertex, texcoord, normal
+					// if there are 2 indices, they are vertex, texcoord
+					// if there is only one index, it is vertex
+					// so we just initialize the values to defaults,
+					// and update them as indices are read
+					VectorClear(v);
+					VectorClear(vn);
+					Vector2Clear(tc);
+					for (j = 0;j < 3;j++)
+					{
+						if (*s)
+						{
+							NSint32 k = atoi(s);
+							switch (j)
+							{
+							case 0:
+								if (k < 0)
+									k += num_vertices;
+								else
+									k--;
+								if (k >= 0 && k < num_vertices)
+									VectorCopy(data_vertex3f + k * 3, v);
+								else
+									Console_Printf("util_convertobj: %s:%i: invalid face vertex index (parameter #%i)\n", inputname, linenumber, i + 1);
+								break;
+							case 1:
+								if (k < 0)
+									k += num_texcoords;
+								else
+									k--;
+								if (k >= 0 && k < num_texcoords)
+									Vector2Copy(data_texcoord2f + k * 2, tc);
+								else
+									Console_Printf("util_convertobj: %s:%i: invalid face texcoord index (parameter #%i)\n", inputname, linenumber, i + 1);
+								break;
+							case 2:
+								if (k < 0)
+									k += num_normals;
+								else
+									k--;
+								if (k >= 0 && k < num_normals)
+									VectorCopy(data_normal3f + k * 3, vn);
+								else
+									Console_Printf("util_convertobj: %s:%i: invalid face vertexnormal index (parameter #%i)\n", inputname, linenumber, i + 1);
+								break;
+							}
+						}
+						// this weird code construct exits at NUL, or if the
+						// character is /, and then leaves s at the character
+						// after the NUL or /
+						while (*s)
+						{
+							if (*s == '/')
+							{
+								s++;
+								break;
+							}
+							s++;
+						}
+					}
+					indices[i] = Util_BuildModel_FindOrAddVertex(b, surfaceindex, v[0], v[1], v[2], vn[0], vn[1], vn[2], tc[0], tc[1]);
+					// make a triangle fan if we have enough vertices
+					if (i >= 2)
+					{
+#if 0
+						if (flip)
+							Util_BuildModel_AddTriangle(b, surfaceindex, indices[0], indices[i], indices[i-1]);
+						else
+#endif
+							Util_BuildModel_AddTriangle(b, surfaceindex, indices[0], indices[i-1], indices[i]);
+					}
+				}
+			}
+			else
+				Console_Printf("util_convertobj: %s:%i: command %s takes %i or more parameters\n", inputname, linenumber, argv[0], 3);
+		}
+		else if (!String_Compare(argv[0], "o"))
+		{
+			// o cylinder1
+			if (argc != 2)
+				Console_Printf("util_convertobj: %s:%i: command %s takes %i parameters\n", inputname, linenumber, argv[0], 1);
+		}
+		else if (!String_Compare(argv[0], "mtllib"))
+		{
+			// mtllib zrail2.mtl
+			if (argc != 2)
+				Console_Printf("util_convertobj: %s:%i: command %s takes %i parameters\n", inputname, linenumber, argv[0], 1);
+		}
+		else if (!String_Compare(argv[0], "g"))
+		{
+			// g cylinder1_mtrl/invisible
+			if (argc != 2)
+				Console_Printf("util_convertobj: %s:%i: command %s takes %i parameters\n", inputname, linenumber, argv[0], 1);
+		}
+		else
+			Console_Printf("util_convertobj: %s:%i: unknown obj command %s\n", inputname, linenumber, argv[0]);
+	}
+	if (data_vertex3f)
+		Mem_Free(&data_vertex3f);
+	if (data_normal3f)
+		Mem_Free(&data_normal3f);
+	if (data_texcoord2f)
+		Mem_Free(&data_texcoord2f);
+}
+
 static void Util_Shell_MakeTerrain(void)
 {
-	NUint32 i, tilesize, width, height, bx, by, tx, ty, tilewidth, tileheight, num, nummeshes, tileswidth, tilesheight;
+	NUint32 tilesize, width, height, bx, by, tx, ty, tilewidth, tileheight, num, tileswidth, tilesheight;
 	NUint8 *pixels;
 	const char *basename, *imagename, *materialname;
 	char *tilename;
 	Nvec terrainsize[3], texturesize[2], terrainscale[3], terrainorigin[3], texturescale[2];
-	Util_Mesh *meshes, *mesh;
+	Util_BuildModel b;
 	basename = Shell_Callback_GetArg(1);
 	imagename = Shell_Callback_GetArg(2);
 	tilesize = (int) Shell_Callback_GetArgValue(3);
@@ -313,10 +642,295 @@
 	terrainorigin[2] = terrainsize[2] * -0.5;
 	tileswidth = width / tilesize;
 	tilesheight = height / tilesize;
-	nummeshes = 0;
+	Util_BuildModel_Begin(&b);
 	for (by = 0;by < height;by += tilesize)
+	{
 		for (bx = 0;bx < width;bx += tilesize)
-			nummeshes++;
+		{
+			NUint32 surfaceindex = Util_BuildModel_AddSurface(&b, materialname);
+			tilewidth = Bound(0, width - bx, tilesize+2);
+			tileheight = Bound(0, height - by, tilesize+2);
+			for (ty = 0, num = 0;ty < tileheight;ty++)
+			{
+				for (tx = 0;tx < tilewidth;tx++, num++)
+				{
+					NUint32 w = width*4;
+					NUint8 *p = pixels + ((by + ty) * width + (bx + tx)) * 4;
+					NUint32 pxi, pyi;
+					double px, py, pz, pxf, pyf;
+					px = bx + Bound(0.5, tx, tilewidth - 1.5);
+					py = by + Bound(0.5, ty, tileheight - 1.5);
+					pxi = (int)px;
+					pyi = (int)py;
+					pxf = px - pxi;
+					pyf = py - pyi;
+					p = pixels + (pyi * width + pxi) * 4;
+					pz = ((p[0]+p[1]+p[2])*(1.0-pxf)+(p[4]+p[5]+p[6])*(pxf))*(1.0-pyf)+((p[w+0]+p[w+1]+p[w+2])*(1.0-pxf)+(p[w+4]+p[w+5]+p[w+6])*(pxf))*(pyf);
+					// FIXME: this SHOULD calculate a valid surface normal!!
+					Util_BuildModel_AddVertex(&b, surfaceindex, px * terrainscale[0] + terrainorigin[0], py * terrainscale[1] + terrainorigin[1], pz * terrainscale[2] + terrainorigin[2], 0, 0, 1, (px * terrainscale[0] + terrainorigin[0]) * texturescale[0], (py * terrainscale[1] + terrainorigin[1]) * texturescale[1]);
+				}
+			}
+			for (ty = 0, num = 0;ty < tileheight-1;ty++)
+			{
+				for (tx = 0;tx < tilewidth-1;tx++, num += 6)
+				{
+					NUint32 i = ty*tilewidth+tx;
+					Util_BuildModel_AddTriangle(&b, surfaceindex, i, i+1, i+1+tilewidth);
+					Util_BuildModel_AddTriangle(&b, surfaceindex, i, i+1+tilewidth, i+tilewidth);
+				}
+			}
+		}
+	}
+	Mem_Free(&pixels);
+	tilename = String_APrintf(Global_Zone, "%s.md5mesh", basename);
+	Util_BuildModel_Write(&b, tilename);
+	String_Free(&tilename);
+	Util_BuildModel_End(&b);
+}
+
+static Shell_SymbolDecl Util_Shell_MakeTerrain_Decl = {
+	"util_maketerrain",
+	"Turn a heightmap image into a set of md5mesh files.",
+	"Usage: util_maketerrain <maps/mymap/terrain> <image.tga> <tilesize> <terrainsizex> <terrainsizey> <terrainsizez> <material> <texturerepeatsizex> <texturerepeatsizey>\n"
+	"Example: util_maketerrain maps/mymap/terrain maps/mymap/terrain.tga 32 1000 1000 1000 maps/mymap/grass 32 32\n"
+	"would produce md5mesh tiles named maps/mymap/terrain_00_00.md5mesh and up (each containing up to 33x33 vertices and 32x32x2 triangles) comprising a 1000m wide x 1000m long x 1000m high terrain with a material named maps/mymap/grass repeating every 32m x 32m",
+	"ssvvvvsvv",
+	(Shell_Symbol_Callback) Util_Shell_MakeTerrain
+};
+
+static void Util_Shell_ConvertOBJ(void)
+{
+	Nsize objtextsize;
+	Util_BuildModel b;
+	const char *inputname = Shell_Callback_GetArg(1);
+	const char *outputname = Shell_Callback_GetArg(2);
+#if 0
+	float scale = atof(Shell_Callback_GetArg(3));
+	Nbool flip = atoi(Shell_Callback_GetArg(4)) != 0;
+	NUint32 conversionmode = atoi(Shell_Callback_GetArg(5));
+	NUint32 remapcoords[3];
+	float scalecoords[3];
+#endif
+	char *objtext = File_LoadFile(inputname, &objtextsize);
+	if (!objtext)
+	{
+		Shell_Callback_ThrowError("Could not open OBJ file\n");
+		// SHELLTODO
+		return;
+	}
+	Util_BuildModel_Begin(&b);
+#if 1
+	Util_BuildModel_ImportOBJ(&b, inputname, objtext, objtext + objtextsize);
+#else
+	scalecoords[0] = conversionmode & 1 ? -scale : scale;
+	scalecoords[1] = conversionmode & 2 ? -scale : scale;
+	scalecoords[2] = conversionmode & 4 ? -scale : scale;
+	switch((conversionmode / 8) % 6)
+	{
+	default:
+	case 0: remapcoords[0] = 0;remapcoords[1] = 1;remapcoords[2] = 2;break;
+	case 1: remapcoords[0] = 0;remapcoords[1] = 2;remapcoords[2] = 1;break;
+	case 2: remapcoords[0] = 1;remapcoords[1] = 0;remapcoords[2] = 2;break;
+	case 3: remapcoords[0] = 1;remapcoords[1] = 2;remapcoords[2] = 0;break;
+	case 4: remapcoords[0] = 2;remapcoords[1] = 0;remapcoords[2] = 1;break;
+	case 5: remapcoords[0] = 2;remapcoords[1] = 1;remapcoords[2] = 0;break;
+	}
+	Util_BuildModel_ImportOBJ(&b, inputname, objtext, objtext + objtextsize, flip, remapcoords, scalecoords);
+#endif
+	Util_BuildModel_Write(&b, outputname);
+	Util_BuildModel_End(&b);
+	Mem_Free(&objtext);
+}
+
+static Shell_SymbolDecl Util_Shell_ConvertOBJ_Decl = {
+	"util_convertobj",
+	"Turn a .obj model into a .md5mesh model file.",
+#if 1
+	"Usage: util_convertobj <inputname> <outputname>\n"
+	"Example: util_convertobj maps/mymap/mymodel.obj maps/mymap/mymodel.md5mesh",
+	"ss",
+#else
+	"Usage: util_convertobj <inputname> <outputname> <scale> <fliptriangles> <coordinateconversionmode>\n"
+	"Example: util_convertobj maps/mymap/mymodel.obj maps/mymap/mymodel.md5mesh 1 0 8\n"
+	"coordinate conversion mode is a special number that rotates and/or mirrors the geometry, it is in the range 0-47, example value for most 3D programs is 8",
+	"ssvvv",
+#endif
+	(Shell_Symbol_Callback) Util_Shell_ConvertOBJ
+};
+
+
+
+/*
+UNFINISHED
+#define UTIL_SHELL_CONVERTLWO_FACE_MAXVERTS 4
+typedef struct Util_Shell_ConvertLWO_Face
+{
+	NUint32 surfaceindex;
+	NUint32 numpoints;
+	float point[UTIL_SHELL_CONVERTLWO_FACE_MAXVERTS][3];
+}
+Util_Shell_ConvertLWO_Face;
+
+static void Util_Shell_ConvertLWO(void)
+{
+	NUint8 *lwodata;
+	Nsize lwosize;
+
+	NUint32 i, tilesize, width, height, bx, by, tx, ty, tilewidth, tileheight, num, nummeshes, tileswidth, tilesheight;
+	NUint8 *pixels;
+	const char *basename, *imagename, *materialname;
+	char *tilename;
+	Nvec terrainsize[3], texturesize[2], terrainscale[3], terrainorigin[3], texturescale[2];
+	Util_Mesh *meshes, *mesh;
+
+	inputname = Shell_Callback_GetArg(1);
+	outputname = Shell_Callback_GetArg(2);
+	lwodata = File_LoadFile(inputname, &lwosize);
+	if (!lwodata)
+	{
+		Shell_Callback_ThrowError("Could not open LWO file\n");
+		// SHELLTODO
+		return;
+	}
+	if (lwosize < 8 || memcmp(lwodata, "FORM", 4))
+	{
+		Mem_Free(&lwodata);
+		Shell_Callback_ThrowError("Not an Interchange File Format (and therefore not an LWO file)\n");
+		// SHELLTODO
+		return;
+	}
+	if ((lwodata[4] * 16777216 + lwodata[5] * 65536 + lwodata[6] * 256 + lwodata[7]) + 8 > lwosize)
+	{
+		Mem_Free(&lwodata);
+		Shell_Callback_ThrowError("Corrupt Interchange File Format file (truncated FORM chunk)\n");
+		// SHELLTODO
+		return;
+	}
+	if (memcmp(lwodata + 8, "LWO2", 4))
+	{
+		Mem_Free(&lwodata);
+		Shell_Callback_ThrowError("File is Interchange File Format but not an LWO2 (Lightwave 6 or later) file\n");
+		// SHELLTODO
+		return;
+	}
+	lwodatastart = lwodata + 12;
+	lwodataend = lwodata + 8 + (lwodata[4] * 16777216 + lwodata[5] * 65536 + lwodata[6] * 256 + lwodata[7]);
+	chunk_pnts = NULL;
+	chunk_vmap_txuv = NULL;
+	chunk_pols_face = NULL;
+	chunk_ptag_surf = NULL;
+	chunk_vmap_txuv_numtexcoordcomponents = 0;
+	chunk_vmap_txuv_numtexcoords = 0;
+	chunk_vmap_txuv_texcoords = NULL;
+	for (chunk = lwodatastart;chunk <= lwodataend - 8;chunk = contentend)
+	{
+		chunksize = (chunk[4] * 16777216 + chunk[5] * 65536 + chunk[6] * 256 + chunk[7]);
+		content = chunk;
+		contentend = chunk + 8 + chunksize;
+		if (!memcmp(chunk, "PNTS"))
+		{
+			chunk_pnts = chunk;
+			continue;
+		}
+		if (!memcmp(chunk, "VMAP"))
+		{
+			// read type of vertex map
+			if (!memcmp(content, "TXUV", 4))
+			{
+				//
+				chunk_vmap_txuv = chunk;
+				content += 4;
+				// read number of components
+				chunk_vmap_txuv_numtexcoordcomponents = content[0] * 256 + content[1]);
+				content += 2;
+				// skip name of this TXUV chunk, typically Texture
+				while (*content)
+					content++;
+				// skip to 4 byte boundary in file
+				while ((content - lwodatastart) & 3)
+					content++;
+				chunk_vmap_txuv_numtexcoords = (contentend - content) / (numtexcoordcomponents * 4);
+				chunk_vmap_txuv_texcoords = content;
+			}
+			continue;
+		}
+		if (!memcmp(chunk, "POLS", 4))
+		{
+			// polygons
+			if (!memcmp(content, "FACE", 4))
+			{
+				// polygons stored as short numpoints, short index[]
+				chunk_pols_face = chunk;
+				// count the number of triangles we'll be loading
+				content += 4;
+				polygons = content;
+				numtriangles = 0;
+				numpolygons = 0;
+				polygonmaterials = Mem_Alloc(Global_Zone, numpolygons * sizeof(short));
+				while (content < contentend)
+				{
+					int p;
+					p = content[2] * 256 + content[3];
+					content += 2;
+					numpolygons++;
+					if (p >= 3)
+						numtriangles += p - 2;
+					for (;p;p--)
+						content += 2;
+				}
+			}
+			continue;
+		}
+		if (!memcmp(chunk, "PTAG", 4))
+		{
+			// polygon attributes
+			if (!memcmp(chunk, "SURF", 4))
+			{
+				// list of polygons and their corresponding surfaces
+				chunk_ptag_surf = chunk;
+			}
+			continue;
+		}
+		if (!memcmp(chunk, "CLIP", 4))
+		{
+			//unknown
+			content += 4;
+			imageindex++;
+			for (;content < contentend;content = nextcontent)
+			{
+				nextcontent = content + content[4] * 256 + content[5];
+				if (!memcmp(content, "STIL", 4))
+				{
+					// image name
+					strncpy(imagename[imageindex], content, sizeof(imagename[imageindex]) - 1);
+				}
+			}
+		}
+		if (!memcmp(chunk, "SURF", 4))
+		{
+			materialindex++;
+			// surface name, often Default
+			while (*content)
+				content++;
+			// skip to 4 byte boundary in file
+			while ((content - lwodatastart) & 3)
+				content++;
+			for (;content < contentend;content = nextcontent)
+			{
+				nextcontent = content + content[4] * 256 + content[5];
+				if (!memcmp(content, "IMAG", 4))
+				{
+					// image index
+					materialimage[materialindex] = content[6] * 256 + content[7];
+				}
+			}
+		}
+	}
+
+	polygonmaterials = Mem_Alloc(Global_Zone,
+	for (
+
+
 	//nummeshes = tileswidth * tilesheight;
 	meshes = Mem_Alloc(Global_Zone, nummeshes * sizeof(Util_Mesh));
 	mesh = meshes;
@@ -394,29 +1008,18 @@
 	"ssvvvvsvv",
 	(Shell_Symbol_Callback) Util_Shell_MakeTerrain
 };
+*/
 
-float skyboxvertex3f[6*4*3] =
+float skyboxvertex[6][4][8] =
 {
-	 1,  1,  1,  1,  1, -1,  1, -1, -1,  1, -1,  1, // px
-	-1, -1,  1, -1, -1, -1, -1,  1, -1, -1,  1,  1, // nx
-	 1, -1,  1,  1, -1, -1, -1, -1, -1, -1, -1,  1, // py
-	-1,  1,  1, -1,  1, -1,  1,  1, -1,  1,  1,  1, // ny
-	-1,  1,  1,  1,  1,  1,  1, -1,  1, -1, -1,  1, // pz
-	 1,  1, -1, -1,  1, -1, -1, -1, -1,  1, -1, -1 // nz
+	{{ 1,  1,  1, -1,  0,  0, 0, 1}, { 1,  1, -1, -1,  0,  0, 1, 1}, { 1, -1, -1, -1,  0,  0, 1, 0}, { 1, -1,  1, -1,  0,  0, 0, 0}}, // px
+	{{-1, -1,  1,  1,  0,  0, 1, 0}, {-1, -1, -1,  1,  0,  0, 0, 0}, {-1,  1, -1,  1,  0,  0, 0, 1}, {-1,  1,  1,  1,  0,  0, 1, 1}}, // nx
+	{{ 1, -1,  1,  0, -1,  0, 1, 1}, { 1, -1, -1,  0, -1,  0, 1, 0}, {-1, -1, -1,  0, -1,  0, 0, 0}, {-1, -1,  1,  0, -1,  0, 0, 1}}, // py
+	{{-1,  1,  1,  0,  1,  0, 0, 0}, {-1,  1, -1,  0,  1,  0, 0, 1}, { 1,  1, -1,  0,  1,  0, 1, 1}, { 1,  1,  1,  0,  1,  0, 1, 0}}, // ny
+	{{-1,  1,  1,  0,  0, -1, 0, 1}, { 1,  1,  1,  0,  0, -1, 1, 1}, { 1, -1,  1,  0,  0, -1, 1, 0}, {-1, -1,  1,  0,  0, -1, 0, 0}}, // pz
+	{{ 1,  1, -1,  0,  0,  1, 0, 1}, {-1,  1, -1,  0,  0,  1, 1, 1}, {-1, -1, -1,  0,  0,  1, 1, 0}, { 1, -1, -1,  0,  0,  1, 0, 0}}  // nz
 };
 
-float skyboxtexcoord2f[6*4*2] =
-{
-	0, 1, 1, 1, 1, 0, 0, 0, // px
-	1, 0, 0, 0, 0, 1, 1, 1, // nx
-	1, 1, 1, 0, 0, 0, 0, 1, // py
-	0, 0, 0, 1, 1, 1, 1, 0, // ny
-	0, 1, 1, 1, 1, 0, 0, 0, // pz
-	0, 1, 1, 1, 1, 0, 0, 0  // nz
-};
-
-NUint32 skyboxelements[6] = {0,  2,  1,  0,  3,  2};
-
 const char *skyboxsuffix[6] = {"px", "nx", "py", "ny", "pz", "nz"};
 
 static void Util_Shell_MakeSkyBox(void)
@@ -424,29 +1027,28 @@
 	NUint32 i;
 	double boxsize;
 	const char *basefilename, *baseimagename;
-	char *filename, *materialname[6];
-	float verts[6*4*3];
-	Util_Mesh meshes[6];
+	char *filename;
+	Util_BuildModel b;
 	basefilename = Shell_Callback_GetArg(1);
 	baseimagename = Shell_Callback_GetArg(2);
 	boxsize = atof(Shell_Callback_GetArg(3));
+	Util_BuildModel_Begin(&b);
 	for (i = 0;i < 6;i++)
 	{
-		materialname[i] = String_APrintf(Global_Zone, "%s_%s", baseimagename, skyboxsuffix[i]);
-		meshes[i].materialname = materialname[i];
-		meshes[i].numvertices = 4;
-		meshes[i].vertex3f = verts + i*4*3;
-		meshes[i].texcoord2f = skyboxtexcoord2f + i*4*2;
-		meshes[i].numtriangles = 2;
-		meshes[i].element3i = skyboxelements;
+		NUint32 j;
+		NUint32 indices[4];
+		char *materialname = String_APrintf(Global_Zone, "%s_%s", baseimagename, skyboxsuffix[i]);
+		NUint32 surfaceindex = Util_BuildModel_AddSurface(&b, materialname);
+		String_Free(&materialname);
+		for (j = 0;j < 4;j++)
+			indices[j] = Util_BuildModel_FindOrAddVertex(&b, surfaceindex, skyboxvertex[i][j][0], skyboxvertex[i][j][1], skyboxvertex[i][j][2], skyboxvertex[i][j][3], skyboxvertex[i][j][4], skyboxvertex[i][j][5], skyboxvertex[i][j][6], skyboxvertex[i][j][7]);
+		Util_BuildModel_AddTriangle(&b, surfaceindex, indices[0], indices[2], indices[1]);
+		Util_BuildModel_AddTriangle(&b, surfaceindex, indices[0], indices[3], indices[2]);
 	}
-	for (i = 0;i < 72;i++)
-		verts[i] = skyboxvertex3f[i] * boxsize;
 	filename = String_APrintf(Global_Zone, "%s.md5mesh", basefilename);
-	Util_WriteModel(filename, 6, meshes);
+	Util_BuildModel_Write(&b, filename);
 	String_Free(&filename);
-	for (i = 0;i < 6;i++)
-		String_Free(&materialname[i]);
+	Util_BuildModel_End(&b);
 }
 
 static Shell_SymbolDecl Util_Shell_MakeSkyBox_Decl = {
@@ -463,6 +1065,7 @@
 {
 	Shell_RegisterFunction(&Util_Shell_MakeTerrain_Decl);
 	Shell_RegisterFunction(&Util_Shell_MakeSkyBox_Decl);
+	Shell_RegisterFunction(&Util_Shell_ConvertOBJ_Decl);
 }
 
 void Util_Quit(void)

Modified: trunk/util.h
===================================================================
--- trunk/util.h	2006-04-08 00:42:44 UTC (rev 688)
+++ trunk/util.h	2006-04-08 00:45:02 UTC (rev 689)
@@ -40,21 +40,39 @@
 // lex the next token
 void Util_ParseC_Lex(Util_ParseC_Thread *thread);
 
-typedef struct Util_Mesh
+typedef struct Util_BuildModel_Surface
 {
 	// no support for skeletal weighting since this is only used by utilities building simple models
-	const char *materialname;
-	NUint32 numvertices;
-	float *vertex3f;
-	float *texcoord2f;
-	NUint32 numtriangles;
-	NUint32 *element3i;
+	char materialname[64];
+	NUint32 num_triangles, max_triangles;
+	NUint32 *data_element3i;
+	NUint32 num_vertices, max_vertices;
+	float *data_vertex8f;
 }
-Util_Mesh;
+Util_BuildModel_Surface;
 
-// writes a .md5mesh file given an array of meshes
-void Util_WriteModel(const char *filename, NUint32 nummeshes, Util_Mesh *meshes);
+typedef struct Util_BuildModel
+{
+	NUint32 num_surfaces, max_surfaces;
+	Util_BuildModel_Surface *data_surfaces;
+	// no support for skeleton since this is only used by utilities building simple models
+}
+Util_BuildModel;
 
+// a small system for easily building md5mesh files
+void Util_BuildModel_Begin(Util_BuildModel *b);
+void Util_BuildModel_End(Util_BuildModel *b);
+NUint32 Util_BuildModel_AddSurface(Util_BuildModel *b, const char *materialname);
+NUint32 Util_BuildModel_AddVertex(Util_BuildModel *b, NUint32 surfaceindex, float x, float y, float z, float nx, float ny, float nz, float s, float t);
+NUint32 Util_BuildModel_FindOrAddVertex(Util_BuildModel *b, NUint32 surfaceindex, float x, float y, float z, float nx, float ny, float nz, float s, float t);
+NUint32 Util_BuildModel_AddTriangle(Util_BuildModel *b, NUint32 surfaceindex, int e0, int e1, int e2);
+void Util_BuildModel_Write(Util_BuildModel *b, const char *filename);
+#if 1
+void Util_BuildModel_ImportOBJ(Util_BuildModel *b, const char *inputname, const char *text, const char *textend);
+#else
+void Util_BuildModel_ImportOBJ(Util_BuildModel *b, const char *inputname, const char *text, const char *textend, Nbool flip, NUint32 remapcoords[3], float scalecoords[3]);
+#endif
+
 void Util_Init(void);
 void Util_Quit(void);
 




More information about the neither-commits mailing list