From 4a7eb0f11da147f22ae7a2a17b4e5f4b0aad60ac Mon Sep 17 00:00:00 2001
From: havoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Date: Wed, 14 Oct 2009 09:20:11 +0000
Subject: [PATCH] fixed bug with gl_paranoid 1 when gl_vbo is 0 where element3s
 contained no valid triangles because they were not initialized by
 Mod_BuildVBOs moved unfinished OBJ loader from alias code to brush code

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9336 d7cf8633-e32d-0410-b094-e92efae38249
---
 model_alias.c  | 464 ++-----------------------------------------------
 model_brush.c  | 461 ++++++++++++++++++++++++++++++++++++++++++++++++
 model_shared.c |  18 +-
 3 files changed, 488 insertions(+), 455 deletions(-)

diff --git a/model_alias.c b/model_alias.c
index 2cd55b36..7095e7ea 100644
--- a/model_alias.c
+++ b/model_alias.c
@@ -1071,11 +1071,10 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 	// generate ushort elements array if possible
 	if (loadmodel->surfmesh.num_vertices <= 65536)
-	{
 		loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
+	if (loadmodel->surfmesh.data_element3s)
 		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
 			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
-	}
 
 // load the frames
 	loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numframes);
@@ -1403,11 +1402,10 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 	// generate ushort elements array if possible
 	if (loadmodel->surfmesh.num_vertices <= 65536)
-	{
 		loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
+	if (loadmodel->surfmesh.data_element3s)
 		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
 			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
-	}
 
 	// load the frames
 	datapointer = (base + LittleLong(pinmodel->ofs_frames));
@@ -1568,11 +1566,7 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
 	loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
 	loadmodel->surfmesh.data_morphmd3vertex = (md3vertex_t *)data;data += meshvertices * loadmodel->numframes * sizeof(md3vertex_t);
 	if (meshvertices <= 65536)
-	{
 		loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
-		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
-	}
 
 	meshvertices = 0;
 	meshtriangles = 0;
@@ -1615,6 +1609,9 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 		Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
 	}
+	if (loadmodel->surfmesh.data_element3s)
+		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
 	Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
 	Mod_Alias_MorphMesh_CompileFrames();
 	Mod_Alias_CalculateBoundingBox();
@@ -1820,11 +1817,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 	loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * loadmodel->num_bones * sizeof(float[12]);
 	loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
 	if (loadmodel->surfmesh.num_vertices <= 65536)
-	{
 		loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
-		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
-	}
 
 	//zymlump_t lump_poses; // float pose[numposes][numbones][3][4]; // animation data
 	poses = (float *) (pheader->lump_poses.start + pbase);
@@ -1940,6 +1933,9 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 	Mod_MakeSortedSurfaces(loadmodel);
 
 	// compute all the mesh information that was not loaded from the file
+	if (loadmodel->surfmesh.data_element3s)
+		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
 	Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
 	Mod_BuildBaseBonePoses();
 	Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, true);
@@ -2076,11 +2072,7 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 	loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
 	loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
 	if (meshvertices <= 65536)
-	{
 		loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
-		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
-	}
 
 	for (i = 0;i < loadmodel->numskins;i++)
 	{
@@ -2247,6 +2239,9 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 	Mod_MakeSortedSurfaces(loadmodel);
 
 	// compute all the mesh information that was not loaded from the file
+	if (loadmodel->surfmesh.data_element3s)
+		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
 	Mod_BuildBaseBonePoses();
 	Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, true);
 	Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
@@ -2668,11 +2663,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 	loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
 	loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
 	if (loadmodel->surfmesh.num_vertices <= 65536)
-	{
 		loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
-		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
-	}
 
 	for (i = 0;i < loadmodel->numskins;i++)
 	{
@@ -2798,6 +2789,9 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 	// compute all the mesh information that was not loaded from the file
 	// TODO: honor smoothing groups somehow?
+	if (loadmodel->surfmesh.data_element3s)
+		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
 	Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
 	Mod_BuildBaseBonePoses();
 	Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, true);
@@ -2807,433 +2801,3 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 	loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
 }
-
-void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
-{
-#if 0
-	const char *textbase = (char *)buffer, *text = textbase;
-	char *s;
-	char *argv[512];
-	char line[1024];
-	char materialname[MAX_QPATH];
-	int j, index1, index2, index3, first, prev, index;
-	int argc;
-	int linelen;
-	int numtriangles = 0;
-	int maxtriangles = 32768;
-	int *element3i = Mem_Alloc(tempmempool, maxtriangles * sizeof(int[3]));
-	int *oldelement3i;
-	int numsurfaces = 0;
-	int maxsurfaces = 0;
-	msurface_t *surfaces = NULL;
-	int linenumber = 0;
-	int hashindex;
-	float *v, *vt, *vn;
-	float *oldv, *oldvt, *oldvn;
-	int maxv = 65536, numv = 1;
-	int maxvt = 65536, numvt = 1;
-	int maxvn = 65536, numvn = 1;
-	int maxverthash = 65536, numverthash = 0;
-	int numhashindex = 65536;
-	struct objverthash_s
-	{
-		struct objverthash_s *next;
-		int s;
-		int v;
-		int vt;
-		int vn;
-	}
-	*hash, **verthash = Mem_Alloc(tempmempool, numhashindex * sizeof(*verthash)), *verthashdata = Mem_Alloc(tempmempool, maxverthash * sizeof(*verthashdata)), *oldverthashdata;
-	skinfile_t *skinfiles;
-
-	dpsnprintf(materialname, sizeof(materialname), "%s", loadmodel->name);
-
-	skinfiles = Mod_LoadSkinFiles();
-
-	loadmodel->modeldatatypestring = "OBJ";
-
-	loadmodel->type = mod_alias;
-	loadmodel->AnimateVertices = NULL;
-	loadmodel->DrawSky = NULL;
-	loadmodel->DrawAddWaterPlanes = NULL;
-	loadmodel->Draw = R_Q1BSP_Draw;
-	loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
-	loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
-	loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
-	loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
-	loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
-	loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
-	loadmodel->DrawLight = R_Q1BSP_DrawLight;
-	loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
-	loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
-	loadmodel->PointSuperContents = NULL;
-
-	// parse the OBJ text now
-	for(;;)
-	{
-		if (!*text)
-			break;
-		linenumber++;
-		linelen = 0;
-		for (linelen = 0;text[linelen] && text[linelen] != '\r' && text[linelen] != '\n';linelen++)
-			line[linelen] = text[linelen];
-		line[linelen] = 0;
-		for (argc = 0;argc < (int)(sizeof(argv)/sizeof(argv[0]));argc++)
-			argv[argc] = "";
-		argc = 0;
-		s = line;
-		while (*s == ' ' || *s == '\t')
-			s++;
-		while (*s)
-		{
-			argv[argc++] = s;
-			while (*s > ' ')
-				s++;
-			if (!*s)
-				break;
-			*s++ = 0;
-			while (*s == ' ' || *s == '\t')
-				s++;
-		}
-		if (!argc)
-			continue;
-		if (argv[0][0] == '#')
-			continue;
-		if (!strcmp(argv[0], "v"))
-		{
-			if (maxv <= numv)
-			{
-				maxv *= 2;
-				oldv = v;
-				v = Mem_Alloc(tempmempool, maxv * sizeof(float[3]));
-				if (oldv)
-				{
-					memcpy(v, oldv, numv * sizeof(float[3]));
-					Mem_Free(oldv);
-				}
-			}
-			v[numv*3+0] = atof(argv[1]);
-			v[numv*3+1] = atof(argv[2]);
-			v[numv*3+2] = atof(argv[3]);
-			numv++;
-		}
-		else if (!strcmp(argv[0], "vt"))
-		{
-			if (maxvt <= numvt)
-			{
-				maxvt *= 2;
-				oldvt = vt;
-				vt = Mem_Alloc(tempmempool, maxvt * sizeof(float[2]));
-				if (oldvt)
-				{
-					memcpy(vt, oldvt, numvt * sizeof(float[2]));
-					Mem_Free(oldvt);
-				}
-			}
-			vt[numvt*2+0] = atof(argv[1]);
-			vt[numvt*2+1] = atof(argv[2]);
-			numvt++;
-		}
-		else if (!strcmp(argv[0], "vn"))
-		{
-			if (maxvn <= numvn)
-			{
-				maxvn *= 2;
-				oldvn = vn;
-				vn = Mem_Alloc(tempmempool, maxvn * sizeof(float[3]));
-				if (oldvn)
-				{
-					memcpy(vn, oldvn, numvn * sizeof(float[3]));
-					Mem_Free(oldvn);
-				}
-			}
-			vn[numvn*3+0] = atof(argv[1]);
-			vn[numvn*3+1] = atof(argv[2]);
-			vn[numvn*3+2] = atof(argv[3]);
-			numvn++;
-		}
-		else if (!strcmp(argv[0], "f"))
-		{
-			if (!surface)
-			{
-				if (maxsurfaces <= numsurfaces)
-				{
-					maxsurfaces++;
-					oldsurfaces = surfaces;
-					surfaces = Mem_Alloc(tempmempool, maxsurfaces * sizeof(*surfaces));
-					if (oldsurfaces)
-					{
-						memcpy(surfaces, oldsurfaces, numsurfaces * sizeof(*surfaces));
-						Mem_Free(oldsurfaces);
-					}
-				}
-				surface = surfaces + numsurfaces++;
-				surface->
-			}
-			for (j = 1;j < argc;j++)
-			{
-				index1 = atoi(argv[j]);
-				while(argv[j][0] && argv[j][0] != '/')
-					argv[j]++;
-				if (argv[j][0])
-					argv[j]++;
-				if (index1 < 0)
-					index1 = numv + 1 - index1;
-				index2 = atoi(argv[j]);
-				if (index2 < 0)
-					index2 = numvt + 1 - index2;
-				while(argv[j][0] && argv[j][0] != '/')
-					argv[j]++;
-				if (argv[j][0])
-					argv[j]++;
-				index3 = atoi(argv[j]);
-				if (index3 < 0)
-					index3 = numvn + 1 - index3;
-				hashindex = (index1 + index2 * 3571 + index3 * 42589) & (numhashindex - 1);
-				for (hash = verthash[hashindex];hash;hash = hash->next)
-					if (hash->surface == numsurfaces-1 && hash->v == index1 && hash->vt == index2 && hash->vn == index3)
-						break;
-				if (!hash)
-				{
-					if (maxverthash <= numverthash)
-					{
-						maxverthash *= 2;
-						oldverthashdata = verthashdata;
-						verthashdata = Mem_Alloc(tempmempool, maxverthash * sizeof(*verthashdata));
-						if (oldverthashdata)
-						{
-							memcpy(verthashdata, oldverthashdata, numverthash * sizeof(*verthashdata));
-							Mem_Free(oldverthashdata);
-						}
-					}
-					hash = verthashdata + numverthash++;
-					hash->next = verthash[hashindex];
-					hash->s = numsurfaces;
-					hash->v = index1;
-					hash->vt = index2;
-					hash->vn = index3;
-					verthash[hashindex] = hash;
-				}
-				index = (int)((size_t)(hash - verthashdata));
-				if (j == 1)
-					first = index;
-				else if (j >= 3)
-				{
-					if (maxtriangles <= numtriangles)
-					{
-						maxtriangles *= 2;
-						oldelement3i = element3i;
-						element3i = Mem_Alloc(tempmempool, numtriangles * sizeof(int[3]));
-						if (oldelement3i)
-						{
-							memcpy(element3i, oldelement3i, numtriangles * sizeof(int[3]));
-							Mem_Free(oldelement3i);
-						}
-					}
-					element3i[numtriangles*3+0] = first;
-					element3i[numtriangles*3+1] = prev;
-					element3i[numtriangles*3+2] = index;
-					numtriangles++;
-				}
-				prev = index;
-			}
-		}
-		else if (!strcmp(argv[0], "o") || !strcmp(argv[0], "g"))
-			surface = NULL;
-		else if (!!strcmp(argv[0], "usemtl"))
-		{
-			surface = NULL;
-			strlcpy(materialname, argv[1], sizeof(materialname);
-		}
-		text += linelen;
-		if (*text == '\r')
-			text++;
-		if (*text == '\n')
-			text++;
-	}
-
-	if (skinfiles)
-		Mod_FreeSkinFiles(skinfiles);
-
-	// now that we have the OBJ data loaded as-is, we can convert it
-	loadmodel->numskins = LittleLong(pinmodel->num_skins);
-	numxyz = LittleLong(pinmodel->num_xyz);
-	numst = LittleLong(pinmodel->num_st);
-	loadmodel->surfmesh.num_triangles = LittleLong(pinmodel->num_tris);
-	loadmodel->numframes = LittleLong(pinmodel->num_frames);
-	loadmodel->surfmesh.num_morphframes = loadmodel->numframes;
-	loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
-	skinwidth = LittleLong(pinmodel->skinwidth);
-	skinheight = LittleLong(pinmodel->skinheight);
-	iskinwidth = 1.0f / skinwidth;
-	iskinheight = 1.0f / skinheight;
-
-	loadmodel->num_surfaces = 1;
-	loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
-	data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->numframes * sizeof(animscene_t) + loadmodel->numframes * sizeof(float[6]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]));
-	loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-	loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
-	loadmodel->sortedmodelsurfaces[0] = 0;
-	loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
-	loadmodel->surfmesh.data_morphmd2framesize6f = (float *)data;data += loadmodel->numframes * sizeof(float[6]);
-	loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
-	loadmodel->surfmesh.data_neighbor3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
-
-	loadmodel->synctype = ST_RAND;
-
-	// load the skins
-	inskin = (char *)(base + LittleLong(pinmodel->ofs_skins));
-	skinfiles = Mod_LoadSkinFiles();
-	if (skinfiles)
-	{
-		loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
-		loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-		loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
-		Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures, skinfiles, "default", "");
-		Mod_FreeSkinFiles(skinfiles);
-	}
-	else if (loadmodel->numskins)
-	{
-		// skins found (most likely not a player model)
-		loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
-		loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-		loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
-		for (i = 0;i < loadmodel->numskins;i++, inskin += MD2_SKINNAME)
-			Mod_LoadTextureFromQ3Shader(loadmodel->data_textures + i * loadmodel->num_surfaces, inskin, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP | TEXF_COMPRESS);
-	}
-	else
-	{
-		// no skins (most likely a player model)
-		loadmodel->numskins = 1;
-		loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
-		loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-		loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
-		Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures, NULL);
-	}
-
-	loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numskins);
-	for (i = 0;i < loadmodel->numskins;i++)
-	{
-		loadmodel->skinscenes[i].firstframe = i;
-		loadmodel->skinscenes[i].framecount = 1;
-		loadmodel->skinscenes[i].loop = true;
-		loadmodel->skinscenes[i].framerate = 10;
-	}
-
-	// load the triangles and stvert data
-	inst = (unsigned short *)(base + LittleLong(pinmodel->ofs_st));
-	intri = (md2triangle_t *)(base + LittleLong(pinmodel->ofs_tris));
-	md2verthash = (struct md2verthash_s **)Mem_Alloc(tempmempool, 65536 * sizeof(hash));
-	md2verthashdata = (struct md2verthash_s *)Mem_Alloc(tempmempool, loadmodel->surfmesh.num_triangles * 3 * sizeof(*hash));
-	// swap the triangle list
-	loadmodel->surfmesh.num_vertices = 0;
-	for (i = 0;i < loadmodel->surfmesh.num_triangles;i++)
-	{
-		for (j = 0;j < 3;j++)
-		{
-			xyz = (unsigned short) LittleShort (intri[i].index_xyz[j]);
-			st = (unsigned short) LittleShort (intri[i].index_st[j]);
-			if (xyz >= numxyz)
-			{
-				Con_Printf("%s has an invalid xyz index (%i) on triangle %i, resetting to 0\n", loadmodel->name, xyz, i);
-				xyz = 0;
-			}
-			if (st >= numst)
-			{
-				Con_Printf("%s has an invalid st index (%i) on triangle %i, resetting to 0\n", loadmodel->name, st, i);
-				st = 0;
-			}
-			hashindex = (xyz * 256 + st) & 65535;
-			for (hash = md2verthash[hashindex];hash;hash = hash->next)
-				if (hash->xyz == xyz && hash->st == st)
-					break;
-			if (hash == NULL)
-			{
-				hash = md2verthashdata + loadmodel->surfmesh.num_vertices++;
-				hash->xyz = xyz;
-				hash->st = st;
-				hash->next = md2verthash[hashindex];
-				md2verthash[hashindex] = hash;
-			}
-			loadmodel->surfmesh.data_element3i[i*3+j] = (hash - md2verthashdata);
-		}
-	}
-
-	vertremap = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(int));
-	data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[2]) + loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes * sizeof(trivertx_t));
-	loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
-	loadmodel->surfmesh.data_morphmdlvertex = (trivertx_t *)data;data += loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes * sizeof(trivertx_t);
-	for (i = 0;i < loadmodel->surfmesh.num_vertices;i++)
-	{
-		int sts, stt;
-		hash = md2verthashdata + i;
-		vertremap[i] = hash->xyz;
-		sts = LittleShort(inst[hash->st*2+0]);
-		stt = LittleShort(inst[hash->st*2+1]);
-		if (sts < 0 || sts >= skinwidth || stt < 0 || stt >= skinheight)
-		{
-			Con_Printf("%s has an invalid skin coordinate (%i %i) on vert %i, changing to 0 0\n", loadmodel->name, sts, stt, i);
-			sts = 0;
-			stt = 0;
-		}
-		loadmodel->surfmesh.data_texcoordtexture2f[i*2+0] = sts * iskinwidth;
-		loadmodel->surfmesh.data_texcoordtexture2f[i*2+1] = stt * iskinheight;
-	}
-
-	Mem_Free(md2verthash);
-	Mem_Free(md2verthashdata);
-
-	// generate ushort elements array if possible
-	if (loadmodel->surfmesh.num_vertices <= 65536)
-	{
-		loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
-		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
-	}
-
-	// load the frames
-	datapointer = (base + LittleLong(pinmodel->ofs_frames));
-	for (i = 0;i < loadmodel->surfmesh.num_morphframes;i++)
-	{
-		int k;
-		trivertx_t *v;
-		trivertx_t *out;
-		pinframe = (md2frame_t *)datapointer;
-		datapointer += sizeof(md2frame_t);
-		// store the frame scale/translate into the appropriate array
-		for (j = 0;j < 3;j++)
-		{
-			loadmodel->surfmesh.data_morphmd2framesize6f[i*6+j] = LittleFloat(pinframe->scale[j]);
-			loadmodel->surfmesh.data_morphmd2framesize6f[i*6+3+j] = LittleFloat(pinframe->translate[j]);
-		}
-		// convert the vertices
-		v = (trivertx_t *)datapointer;
-		out = loadmodel->surfmesh.data_morphmdlvertex + i * loadmodel->surfmesh.num_vertices;
-		for (k = 0;k < loadmodel->surfmesh.num_vertices;k++)
-			out[k] = v[vertremap[k]];
-		datapointer += numxyz * sizeof(trivertx_t);
-
-		strlcpy(loadmodel->animscenes[i].name, pinframe->name, sizeof(loadmodel->animscenes[i].name));
-		loadmodel->animscenes[i].firstframe = i;
-		loadmodel->animscenes[i].framecount = 1;
-		loadmodel->animscenes[i].framerate = 10;
-		loadmodel->animscenes[i].loop = true;
-	}
-
-	Mem_Free(vertremap);
-
-	Mod_MakeSortedSurfaces(loadmodel);
-	Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
-	Mod_Alias_CalculateBoundingBox();
-	Mod_Alias_MorphMesh_CompileFrames();
-
-	surface = loadmodel->data_surfaces;
-	surface->texture = loadmodel->data_textures;
-	surface->num_firsttriangle = 0;
-	surface->num_triangles = loadmodel->surfmesh.num_triangles;
-	surface->num_firstvertex = 0;
-	surface->num_vertices = loadmodel->surfmesh.num_vertices;
-
-	loadmodel->surfmesh.isanimated = false;
-#endif
-}
diff --git a/model_brush.c b/model_brush.c
index d30aca74..da1585ca 100644
--- a/model_brush.c
+++ b/model_brush.c
@@ -6302,6 +6302,467 @@ void Mod_MAP_Load(dp_model_t *mod, void *buffer, void *bufferend)
 	Host_Error("Mod_MAP_Load: not yet implemented");
 }
 
+typedef struct objvertex_s
+{
+	float v[3];
+	float vt[2];
+	float vn[3];
+}
+objvertex_t;
+
+typedef struct objtriangle_s
+{
+	objvertex_t vertex[3];
+	int textureindex;
+}
+objtriangle_t;
+
+void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
+{
+#if 0
+	const char *textbase = (char *)buffer, *text = textbase;
+	char *s;
+	char *argv[512];
+	char line[1024];
+	char materialname[MAX_QPATH];
+	int j, index1, index2, index3, first, prev, index;
+	int argc;
+	int linelen;
+	int numtriangles = 0;
+	int maxtriangles = 131072;
+	objtriangle_t *triangles = Mem_Alloc(tempmempool, maxtriangles * sizeof(*triangles));
+	int linenumber = 0;
+	int maxtextures = 256, numtextures = 0, textureindex = 0;
+	int maxv = 1024, numv = 0;
+	int maxvt = 1024, numvt = 0;
+	int maxvn = 1024, numvn = 0;
+	char **texturenames;
+	float *v = Mem_Alloc(tempmempool, maxv * sizeof(float[3]));
+	float *vt = Mem_Alloc(tempmempool, maxvt * sizeof(float[2]));
+	float *vn = Mem_Alloc(tempmempool, maxvn * sizeof(float[3]));
+	objvertex_t vfirst, vprev, vcurrent;
+#if 0
+	int hashindex;
+	int maxverthash = 65536, numverthash = 0;
+	int numhashindex = 65536;
+	struct objverthash_s
+	{
+		struct objverthash_s *next;
+		int s;
+		int v;
+		int vt;
+		int vn;
+	}
+	*hash, **verthash = Mem_Alloc(tempmempool, numhashindex * sizeof(*verthash)), *verthashdata = Mem_Alloc(tempmempool, maxverthash * sizeof(*verthashdata)), *oldverthashdata;
+#endif
+
+	dpsnprintf(materialname, sizeof(materialname), "%s", loadmodel->name);
+
+	loadmodel->modeldatatypestring = "OBJ";
+
+	loadmodel->type = mod_obj;
+	loadmodel->soundfromcenter = true;
+	loadmodel->TraceBox = Mod_OBJ_TraceBox;
+	loadmodel->TraceLine = Mod_OBJ_TraceLine;
+	loadmodel->TracePoint = Mod_OBJ_TracePoint;
+	loadmodel->PointSuperContents = Mod_OBJ_PointSuperContents;
+	loadmodel->brush.TraceLineOfSight = Mod_OBJ_TraceLineOfSight;
+	loadmodel->brush.SuperContentsFromNativeContents = Mod_OBJ_SuperContentsFromNativeContents;
+	loadmodel->brush.NativeContentsFromSuperContents = Mod_OBJ_NativeContentsFromSuperContents;
+	loadmodel->brush.GetPVS = Mod_OBJ_GetPVS;
+	loadmodel->brush.FatPVS = Mod_OBJ_FatPVS;
+	loadmodel->brush.BoxTouchingPVS = Mod_OBJ_BoxTouchingPVS;
+	loadmodel->brush.BoxTouchingLeafPVS = Mod_OBJ_BoxTouchingLeafPVS;
+	loadmodel->brush.BoxTouchingVisibleLeafs = Mod_OBJ_BoxTouchingVisibleLeafs;
+	loadmodel->brush.FindBoxClusters = Mod_OBJ_FindBoxClusters;
+	loadmodel->brush.LightPoint = Mod_OBJ_LightPoint;
+	loadmodel->brush.FindNonSolidLocation = Mod_OBJ_FindNonSolidLocation;
+	loadmodel->brush.AmbientSoundLevelsForPoint = NULL;
+	loadmodel->brush.RoundUpToHullSize = NULL;
+	loadmodel->brush.PointInLeaf = Mod_OBJ_PointInLeaf;
+	loadmodel->Draw = R_Q1BSP_Draw;
+	loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
+	loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+	loadmodel->GetLightInfo = R_Q1BSP_GetLightInfo;
+	loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+	loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
+	loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
+	loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+	loadmodel->DrawLight = R_Q1BSP_DrawLight;
+
+	// parse the OBJ text now
+	for(;;)
+	{
+		if (!*text)
+			break;
+		linenumber++;
+		linelen = 0;
+		for (linelen = 0;text[linelen] && text[linelen] != '\r' && text[linelen] != '\n';linelen++)
+			line[linelen] = text[linelen];
+		line[linelen] = 0;
+		for (argc = 0;argc < (int)(sizeof(argv)/sizeof(argv[0]));argc++)
+			argv[argc] = "";
+		argc = 0;
+		s = line;
+		while (*s == ' ' || *s == '\t')
+			s++;
+		while (*s)
+		{
+			argv[argc++] = s;
+			while (*s > ' ')
+				s++;
+			if (!*s)
+				break;
+			*s++ = 0;
+			while (*s == ' ' || *s == '\t')
+				s++;
+		}
+		if (!argc)
+			continue;
+		if (argv[0][0] == '#')
+			continue;
+		if (!strcmp(argv[0], "v"))
+		{
+			if (maxv <= numv)
+			{
+				float *oldv = v;
+				maxv *= 2;
+				v = Mem_Alloc(tempmempool, maxv * sizeof(float[3]));
+				if (oldv)
+				{
+					memcpy(v, oldv, numv * sizeof(float[3]));
+					Mem_Free(oldv);
+				}
+			}
+			v[numv*3+0] = atof(argv[1]);
+			v[numv*3+1] = atof(argv[2]);
+			v[numv*3+2] = atof(argv[3]);
+			numv++;
+		}
+		else if (!strcmp(argv[0], "vt"))
+		{
+			if (maxvt <= numvt)
+			{
+				float *oldvt = vt;
+				maxvt *= 2;
+				vt = Mem_Alloc(tempmempool, maxvt * sizeof(float[2]));
+				if (oldvt)
+				{
+					memcpy(vt, oldvt, numvt * sizeof(float[2]));
+					Mem_Free(oldvt);
+				}
+			}
+			vt[numvt*2+0] = atof(argv[1]);
+			vt[numvt*2+1] = atof(argv[2]);
+			numvt++;
+		}
+		else if (!strcmp(argv[0], "vn"))
+		{
+			if (maxvn <= numvn)
+			{
+				float *oldvn = vn;
+				maxvn *= 2;
+				vn = Mem_Alloc(tempmempool, maxvn * sizeof(float[3]));
+				if (oldvn)
+				{
+					memcpy(vn, oldvn, numvn * sizeof(float[3]));
+					Mem_Free(oldvn);
+				}
+			}
+			vn[numvn*3+0] = atof(argv[1]);
+			vn[numvn*3+1] = atof(argv[2]);
+			vn[numvn*3+2] = atof(argv[3]);
+			numvn++;
+		}
+		else if (!strcmp(argv[0], "f"))
+		{
+			for (j = 1;j < argc;j++)
+			{
+				index1 = atoi(argv[j]);
+				while(argv[j][0] && argv[j][0] != '/')
+					argv[j]++;
+				if (argv[j][0])
+					argv[j]++;
+				index2 = atoi(argv[j]);
+				while(argv[j][0] && argv[j][0] != '/')
+					argv[j]++;
+				if (argv[j][0])
+					argv[j]++;
+				index3 = atoi(argv[j]);
+				// negative refers to a recent vertex
+				// zero means not specified
+				// positive means an absolute vertex index
+				if (index1 < 0)
+					index1 = numv - index1;
+				if (index2 < 0)
+					index2 = numvt - index2;
+				if (index3 < 0)
+					index3 = numvn - index3;
+				VectorCopy(v + 3*index1, vcurrent.v);
+				Vector2Copy(vt + 2*index2, vcurrent.vt);
+				VectorCopy(vn + 3*index3, vcurrent.vn);
+				if (j == 1)
+					vfirst = vcurrent;
+				else if (j >= 3)
+				{
+					if (maxtriangles <= numtriangles)
+					{
+						objtriangle_t *oldtriangles = triangles;
+						maxtriangles *= 2;
+						triangles = Mem_Alloc(tempmempool, numtriangles * sizeof(*triangles));
+						if (oldtriangles)
+						{
+							memcpy(triangles, oldtriangles, numtriangles * sizeof(*triangles));
+							Mem_Free(oldtriangles);
+						}
+					}
+					triangles[numtriangles].textureindex = textureindex;
+					triangles[numtriangles].vertex[0] = vfirst;
+					triangles[numtriangles].vertex[1] = vprev;
+					triangles[numtriangles].vertex[2] = vcurrent;
+					numtriangles++;
+				}
+				vprev = vcurrent;
+				prev = index;
+			}
+		}
+		else if (!strcmp(argv[0], "o") || !strcmp(argv[0], "g"))
+			;
+		else if (!!strcmp(argv[0], "usemtl"))
+		{
+			for (i = 0;i < numtextures;i++)
+				if (!strcmp(texturenames[numtextures], argv[1]))
+					break;
+			if (i < numtextures)
+				texture = textures + i;
+			else
+			{
+				if (maxtextures <= numtextures)
+				{
+					texture_t *oldtextures = textures;
+					maxtextures *= 2;
+					textures = Mem_Alloc(tempmempool, maxtextures * sizeof(*textures));
+					if (oldtextures)
+					{
+						memcpy(textures, oldtextures, numtexutres * sizeof(*textures));
+						Mem_Free(oldtextures);
+					}
+				}
+				textureindex = numtextures++;
+				texturenames[textureindex] = Mem_Alloc(tempmempool, strlen(argv[1]) + 1);
+				memcpy(texturenames[textureindex], argv[1], strlen(argv[1]) + 1);
+			}
+		}
+		text += linelen;
+		if (*text == '\r')
+			text++;
+		if (*text == '\n')
+			text++;
+	}
+
+	// now that we have the OBJ data loaded as-is, we can convert it
+
+	// load the textures
+	loadmodel->num_textures = numtextures;
+	loadmodel->data_textures = Mem_Alloc(loadmodel->mempool, loadmodel->num_textures * sizeof(texture_t));
+	for (i = 0;i < numtextures;i++)
+		Mod_LoadTextureFromQ3Shader(loadmodel->data_textures + i, texturenames[i], true, true, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | (r_picmipworld.integer ? TEXF_PICMIP : 0) | TEXF_COMPRESS);
+
+	// free the texturenames array since we are now done with it
+	for (i = 0;i < numtextures;i++)
+	{
+		Mem_Free(texturenames[i]);
+		texturenames[i] = NULL;
+	}
+	Mem_Free(texturenames);
+	texturenames = NULL;
+
+	// generate a rough BSP tree from triangle data, we don't have to be too careful here, it only has to define the basic areas of the map
+
+	// generate surfaces by recursing triangles into BSP tree and ensuring they do not overlap in the lightmap projection axis
+
+	loadmodel->numskins = LittleLong(pinmodel->num_skins);
+	numxyz = LittleLong(pinmodel->num_xyz);
+	numst = LittleLong(pinmodel->num_st);
+	loadmodel->surfmesh.num_triangles = LittleLong(pinmodel->num_tris);
+	loadmodel->numframes = LittleLong(pinmodel->num_frames);
+	loadmodel->surfmesh.num_morphframes = loadmodel->numframes;
+	loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
+	skinwidth = LittleLong(pinmodel->skinwidth);
+	skinheight = LittleLong(pinmodel->skinheight);
+	iskinwidth = 1.0f / skinwidth;
+	iskinheight = 1.0f / skinheight;
+
+	loadmodel->num_surfaces = 1;
+	loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
+	data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->numframes * sizeof(animscene_t) + loadmodel->numframes * sizeof(float[6]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]));
+	loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
+	loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+	loadmodel->sortedmodelsurfaces[0] = 0;
+	loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
+	loadmodel->surfmesh.data_morphmd2framesize6f = (float *)data;data += loadmodel->numframes * sizeof(float[6]);
+	loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
+	loadmodel->surfmesh.data_neighbor3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
+
+	loadmodel->synctype = ST_RAND;
+
+	// load the skins
+	inskin = (char *)(base + LittleLong(pinmodel->ofs_skins));
+	skinfiles = Mod_LoadSkinFiles();
+	if (skinfiles)
+	{
+		loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
+		loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+		loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
+		Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures, skinfiles, "default", "");
+		Mod_FreeSkinFiles(skinfiles);
+	}
+	else if (loadmodel->numskins)
+	{
+		// skins found (most likely not a player model)
+		loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
+		loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+		loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
+		for (i = 0;i < loadmodel->numskins;i++, inskin += MD2_SKINNAME)
+			Mod_LoadTextureFromQ3Shader(loadmodel->data_textures + i * loadmodel->num_surfaces, inskin, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP | TEXF_COMPRESS);
+	}
+	else
+	{
+		// no skins (most likely a player model)
+		loadmodel->numskins = 1;
+		loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
+		loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+		loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
+		Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures, NULL);
+	}
+
+	loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numskins);
+	for (i = 0;i < loadmodel->numskins;i++)
+	{
+		loadmodel->skinscenes[i].firstframe = i;
+		loadmodel->skinscenes[i].framecount = 1;
+		loadmodel->skinscenes[i].loop = true;
+		loadmodel->skinscenes[i].framerate = 10;
+	}
+
+	// load the triangles and stvert data
+	inst = (unsigned short *)(base + LittleLong(pinmodel->ofs_st));
+	intri = (md2triangle_t *)(base + LittleLong(pinmodel->ofs_tris));
+	md2verthash = (struct md2verthash_s **)Mem_Alloc(tempmempool, 65536 * sizeof(hash));
+	md2verthashdata = (struct md2verthash_s *)Mem_Alloc(tempmempool, loadmodel->surfmesh.num_triangles * 3 * sizeof(*hash));
+	// swap the triangle list
+	loadmodel->surfmesh.num_vertices = 0;
+	for (i = 0;i < loadmodel->surfmesh.num_triangles;i++)
+	{
+		for (j = 0;j < 3;j++)
+		{
+			xyz = (unsigned short) LittleShort (intri[i].index_xyz[j]);
+			st = (unsigned short) LittleShort (intri[i].index_st[j]);
+			if (xyz >= numxyz)
+			{
+				Con_Printf("%s has an invalid xyz index (%i) on triangle %i, resetting to 0\n", loadmodel->name, xyz, i);
+				xyz = 0;
+			}
+			if (st >= numst)
+			{
+				Con_Printf("%s has an invalid st index (%i) on triangle %i, resetting to 0\n", loadmodel->name, st, i);
+				st = 0;
+			}
+			hashindex = (xyz * 256 + st) & 65535;
+			for (hash = md2verthash[hashindex];hash;hash = hash->next)
+				if (hash->xyz == xyz && hash->st == st)
+					break;
+			if (hash == NULL)
+			{
+				hash = md2verthashdata + loadmodel->surfmesh.num_vertices++;
+				hash->xyz = xyz;
+				hash->st = st;
+				hash->next = md2verthash[hashindex];
+				md2verthash[hashindex] = hash;
+			}
+			loadmodel->surfmesh.data_element3i[i*3+j] = (hash - md2verthashdata);
+		}
+	}
+
+	vertremap = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(int));
+	data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[2]) + loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes * sizeof(trivertx_t));
+	loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
+	loadmodel->surfmesh.data_morphmdlvertex = (trivertx_t *)data;data += loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes * sizeof(trivertx_t);
+	for (i = 0;i < loadmodel->surfmesh.num_vertices;i++)
+	{
+		int sts, stt;
+		hash = md2verthashdata + i;
+		vertremap[i] = hash->xyz;
+		sts = LittleShort(inst[hash->st*2+0]);
+		stt = LittleShort(inst[hash->st*2+1]);
+		if (sts < 0 || sts >= skinwidth || stt < 0 || stt >= skinheight)
+		{
+			Con_Printf("%s has an invalid skin coordinate (%i %i) on vert %i, changing to 0 0\n", loadmodel->name, sts, stt, i);
+			sts = 0;
+			stt = 0;
+		}
+		loadmodel->surfmesh.data_texcoordtexture2f[i*2+0] = sts * iskinwidth;
+		loadmodel->surfmesh.data_texcoordtexture2f[i*2+1] = stt * iskinheight;
+	}
+
+	Mem_Free(md2verthash);
+	Mem_Free(md2verthashdata);
+
+	// generate ushort elements array if possible
+	if (loadmodel->surfmesh.num_vertices <= 65536)
+		loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
+
+	// load the frames
+	datapointer = (base + LittleLong(pinmodel->ofs_frames));
+	for (i = 0;i < loadmodel->surfmesh.num_morphframes;i++)
+	{
+		int k;
+		trivertx_t *v;
+		trivertx_t *out;
+		pinframe = (md2frame_t *)datapointer;
+		datapointer += sizeof(md2frame_t);
+		// store the frame scale/translate into the appropriate array
+		for (j = 0;j < 3;j++)
+		{
+			loadmodel->surfmesh.data_morphmd2framesize6f[i*6+j] = LittleFloat(pinframe->scale[j]);
+			loadmodel->surfmesh.data_morphmd2framesize6f[i*6+3+j] = LittleFloat(pinframe->translate[j]);
+		}
+		// convert the vertices
+		v = (trivertx_t *)datapointer;
+		out = loadmodel->surfmesh.data_morphmdlvertex + i * loadmodel->surfmesh.num_vertices;
+		for (k = 0;k < loadmodel->surfmesh.num_vertices;k++)
+			out[k] = v[vertremap[k]];
+		datapointer += numxyz * sizeof(trivertx_t);
+
+		strlcpy(loadmodel->animscenes[i].name, pinframe->name, sizeof(loadmodel->animscenes[i].name));
+		loadmodel->animscenes[i].firstframe = i;
+		loadmodel->animscenes[i].framecount = 1;
+		loadmodel->animscenes[i].framerate = 10;
+		loadmodel->animscenes[i].loop = true;
+	}
+
+	Mem_Free(vertremap);
+
+	Mod_MakeSortedSurfaces(loadmodel);
+	Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+	Mod_Alias_CalculateBoundingBox();
+	Mod_Alias_MorphMesh_CompileFrames();
+
+	surface = loadmodel->data_surfaces;
+	surface->texture = loadmodel->data_textures;
+	surface->num_firsttriangle = 0;
+	surface->num_triangles = loadmodel->surfmesh.num_triangles;
+	surface->num_firstvertex = 0;
+	surface->num_vertices = loadmodel->surfmesh.num_vertices;
+
+	loadmodel->surfmesh.isanimated = false;
+
+	if (loadmodel->surfmesh.data_element3s)
+		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+			loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
+#endif
+}
+
 qboolean Mod_CanSeeBox_Trace(int numsamples, float t, dp_model_t *model, vec3_t eye, vec3_t minsX, vec3_t maxsX)
 {
 	// we already have done PVS culling at this point...
diff --git a/model_shared.c b/model_shared.c
index 4893aea6..653c715c 100644
--- a/model_shared.c
+++ b/model_shared.c
@@ -2346,6 +2346,19 @@ void Mod_MakeSortedSurfaces(dp_model_t *mod)
 
 static void Mod_BuildVBOs(void)
 {
+	if (developer.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
+	{
+		int i;
+		for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+		{
+			if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
+			{
+				Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
+				loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
+			}
+		}
+	}
+
 	if (!gl_support_arb_vertex_buffer_object)
 		return;
 
@@ -2353,12 +2366,7 @@ static void Mod_BuildVBOs(void)
 	if (loadmodel->surfmesh.num_triangles)
 	{
 		if (loadmodel->surfmesh.data_element3s)
-		{
-			int i;
-			for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-				loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
 			loadmodel->surfmesh.ebo3s = R_Mesh_CreateStaticBufferObject(GL_ELEMENT_ARRAY_BUFFER_ARB, loadmodel->surfmesh.data_element3s, loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]), loadmodel->name);
-		}
 		else
 			loadmodel->surfmesh.ebo3i = R_Mesh_CreateStaticBufferObject(GL_ELEMENT_ARRAY_BUFFER_ARB, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles * sizeof(unsigned int[3]), loadmodel->name);
 	}
-- 
2.39.5