示例#1
0
static int MakeMeshTriangles(int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE],
							 srfTriangle_t triangles[SHADER_MAX_TRIANGLES])
{
	int             i, j;
	int             numTriangles;
	int             w, h;
	srfVert_t      *dv;
	static srfVert_t       ctrl2[MAX_GRID_SIZE * MAX_GRID_SIZE];

	h = height - 1;
	w = width - 1;
	numTriangles = 0;
	for(i = 0; i < h; i++)
	{
		for(j = 0; j < w; j++)
		{
			int             v1, v2, v3, v4;

			// vertex order to be reckognized as tristrips
			v1 = i * width + j + 1;
			v2 = v1 - 1;
			v3 = v2 + width;
			v4 = v3 + 1;

			triangles[numTriangles].indexes[0] = v2;
			triangles[numTriangles].indexes[1] = v3;
			triangles[numTriangles].indexes[2] = v1;
			numTriangles++;

			triangles[numTriangles].indexes[0] = v1;
			triangles[numTriangles].indexes[1] = v3;
			triangles[numTriangles].indexes[2] = v4;
			numTriangles++;
		}
	}

	R_CalcSurfaceTriangleNeighbors(numTriangles, triangles);

	// FIXME: use more elegant way
	for(i = 0; i < width; i++)
	{
		for(j = 0; j < height; j++)
		{
			dv = &ctrl2[j * width + i];
			*dv = ctrl[j][i];
		}
	}

	R_CalcSurfaceTrianglePlanes(numTriangles, triangles, ctrl2);

	return numTriangles;
}
示例#2
0
/*
=================
R_LoadMD3
=================
*/
qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, int bufferSize, const char *modName)
{
    int             i, j, k;//, l;

    md3Header_t    *md3Model;
    md3Frame_t     *md3Frame;
    md3Surface_t   *md3Surf;
    md3Shader_t    *md3Shader;
    md3Triangle_t  *md3Tri;
    md3St_t        *md3st;
    md3XyzNormal_t *md3xyz;
    md3Tag_t       *md3Tag;

    mdvModel_t     *mdvModel;
    mdvFrame_t     *frame;
    mdvSurface_t   *surf;//, *surface;
    srfTriangle_t  *tri;
    mdvXyz_t       *v;
    mdvSt_t        *st;
    mdvTag_t       *tag;
    mdvTagName_t   *tagName;

    int             version;
    int             size;

    md3Model = (md3Header_t *) buffer;

    version = LittleLong(md3Model->version);
    if(version != MD3_VERSION)
    {
        ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", modName, version, MD3_VERSION);
        return qfalse;
    }

    mod->type = MOD_MESH;
    size = LittleLong(md3Model->ofsEnd);
    mod->dataSize += size;
    mdvModel = mod->mdv[lod] = (mdvModel_t*)ri.Hunk_Alloc(sizeof(mdvModel_t), h_low);

//  Com_Memcpy(mod->md3[lod], buffer, LittleLong(md3Model->ofsEnd));

    LL(md3Model->ident);
    LL(md3Model->version);
    LL(md3Model->numFrames);
    LL(md3Model->numTags);
    LL(md3Model->numSurfaces);
    LL(md3Model->ofsFrames);
    LL(md3Model->ofsTags);
    LL(md3Model->ofsSurfaces);
    LL(md3Model->ofsEnd);

    if(md3Model->numFrames < 1)
    {
        ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has no frames\n", modName);
        return qfalse;
    }

    // swap all the frames
    mdvModel->numFrames = md3Model->numFrames;
    mdvModel->frames = frame = (mdvFrame_t*)ri.Hunk_Alloc(sizeof(*frame) * md3Model->numFrames, h_low);

    md3Frame = (md3Frame_t *) ((byte *) md3Model + md3Model->ofsFrames);
    for(i = 0; i < md3Model->numFrames; i++, frame++, md3Frame++)
    {
        frame->radius = LittleFloat(md3Frame->radius);
        for(j = 0; j < 3; j++)
        {
            frame->bounds[0][j] = LittleFloat(md3Frame->bounds[0][j]);
            frame->bounds[1][j] = LittleFloat(md3Frame->bounds[1][j]);
            frame->localOrigin[j] = LittleFloat(md3Frame->localOrigin[j]);
        }
    }

    // swap all the tags
    mdvModel->numTags = md3Model->numTags;
    mdvModel->tags = tag = (mdvTag_t*)ri.Hunk_Alloc(sizeof(*tag) * (md3Model->numTags * md3Model->numFrames), h_low);

    md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags);
    for(i = 0; i < md3Model->numTags * md3Model->numFrames; i++, tag++, md3Tag++)
    {
        for(j = 0; j < 3; j++)
        {
            tag->origin[j] = LittleFloat(md3Tag->origin[j]);
            tag->axis[0][j] = LittleFloat(md3Tag->axis[0][j]);
            tag->axis[1][j] = LittleFloat(md3Tag->axis[1][j]);
            tag->axis[2][j] = LittleFloat(md3Tag->axis[2][j]);
        }
    }


    mdvModel->tagNames = tagName = (mdvTagName_t*)ri.Hunk_Alloc(sizeof(*tagName) * (md3Model->numTags), h_low);

    md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags);
    for(i = 0; i < md3Model->numTags; i++, tagName++, md3Tag++)
    {
        Q_strncpyz(tagName->name, md3Tag->name, sizeof(tagName->name));
    }

    // swap all the surfaces
    mdvModel->numSurfaces = md3Model->numSurfaces;
    mdvModel->surfaces = surf = (mdvSurface_t*)ri.Hunk_Alloc(sizeof(*surf) * md3Model->numSurfaces, h_low);

    md3Surf = (md3Surface_t *) ((byte *) md3Model + md3Model->ofsSurfaces);
    for(i = 0; i < md3Model->numSurfaces; i++)
    {
        LL(md3Surf->ident);
        LL(md3Surf->flags);
        LL(md3Surf->numFrames);
        LL(md3Surf->numShaders);
        LL(md3Surf->numTriangles);
        LL(md3Surf->ofsTriangles);
        LL(md3Surf->numVerts);
        LL(md3Surf->ofsShaders);
        LL(md3Surf->ofsSt);
        LL(md3Surf->ofsXyzNormals);
        LL(md3Surf->ofsEnd);

        if(md3Surf->numVerts > SHADER_MAX_VERTEXES)
        {
            ri.Error(ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)",
                     modName, SHADER_MAX_VERTEXES, md3Surf->numVerts);
        }
        if(md3Surf->numTriangles * 3 > SHADER_MAX_INDEXES)
        {
            ri.Error(ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)",
                     modName, SHADER_MAX_INDEXES / 3, md3Surf->numTriangles);
        }

        // change to surface identifier
        surf->surfaceType = SF_MDV;

        // give pointer to model for Tess_SurfaceMDX
        surf->model = mdvModel;

        // copy surface name
        Q_strncpyz(surf->name, md3Surf->name, sizeof(surf->name));

        // lowercase the surface name so skin compares are faster
        Q_strlwr(surf->name);

        // strip off a trailing _1 or _2
        // this is a crutch for q3data being a mess
        j = strlen(surf->name);
        if(j > 2 && surf->name[j - 2] == '_')
        {
            surf->name[j - 2] = 0;
        }

        // register the shaders
        /*
           surf->numShaders = md3Surf->numShaders;
           surf->shaders = shader = ri.Hunk_Alloc(sizeof(*shader) * md3Surf->numShaders, h_low);

           md3Shader = (md3Shader_t *) ((byte *) md3Surf + md3Surf->ofsShaders);
           for(j = 0; j < md3Surf->numShaders; j++, shader++, md3Shader++)
           {
           shader_t       *sh;

           sh = R_FindShader(md3Shader->name, SHADER_3D_DYNAMIC, qtrue);
           if(sh->defaultShader)
           {
           shader->shaderIndex = 0;
           }
           else
           {
           shader->shaderIndex = sh->index;
           }
           }
         */

        // only consider the first shader
        md3Shader = (md3Shader_t *) ((byte *) md3Surf + md3Surf->ofsShaders);
        surf->shader = R_FindShader(md3Shader->name, SHADER_3D_DYNAMIC, qtrue);

        // swap all the triangles
        surf->numTriangles = md3Surf->numTriangles;
        surf->triangles = tri = (srfTriangle_t*)ri.Hunk_Alloc(sizeof(*tri) * md3Surf->numTriangles, h_low);

        md3Tri = (md3Triangle_t *) ((byte *) md3Surf + md3Surf->ofsTriangles);
        for(j = 0; j < md3Surf->numTriangles; j++, tri++, md3Tri++)
        {
            tri->indexes[0] = LittleLong(md3Tri->indexes[0]);
            tri->indexes[1] = LittleLong(md3Tri->indexes[1]);
            tri->indexes[2] = LittleLong(md3Tri->indexes[2]);
        }

        R_CalcSurfaceTriangleNeighbors(surf->numTriangles, surf->triangles);

        // swap all the XyzNormals
        surf->numVerts = md3Surf->numVerts;
        surf->verts = v = (mdvXyz_t*)ri.Hunk_Alloc( sizeof( *v ) * ( md3Surf->numVerts * md3Surf->numFrames ), h_low );

        md3xyz = (md3XyzNormal_t *) ((byte *) md3Surf + md3Surf->ofsXyzNormals);
        for(j = 0; j < md3Surf->numVerts * md3Surf->numFrames; j++, md3xyz++, v++)
        {
            v->xyz[0] = LittleShort(md3xyz->xyz[0]) * MD3_XYZ_SCALE;
            v->xyz[1] = LittleShort(md3xyz->xyz[1]) * MD3_XYZ_SCALE;
            v->xyz[2] = LittleShort(md3xyz->xyz[2]) * MD3_XYZ_SCALE;
        }

        // swap all the ST
        surf->st = st = (mdvSt_t*)ri.Hunk_Alloc(sizeof(*st) * md3Surf->numVerts, h_low);

        md3st = (md3St_t *) ((byte *) md3Surf + md3Surf->ofsSt);
        for(j = 0; j < md3Surf->numVerts; j++, md3st++, st++)
        {
            st->st[0] = LittleFloat(md3st->st[0]);
            st->st[1] = LittleFloat(md3st->st[1]);
        }

        // find the next surface
        md3Surf = (md3Surface_t *) ((byte *) md3Surf + md3Surf->ofsEnd);
        surf++;
    }

#if defined(USE_D3D10)
    // TODO
#else
#if 1
    // create VBO surfaces from md3 surfaces
    {
        mdvNormTanBi_t  *vertexes;
        mdvNormTanBi_t  *vert;

        growList_t      vboSurfaces;
        srfVBOMDVMesh_t *vboSurf;

        byte           *data;
        int             dataSize;
        int             dataOfs;

        vec4_t          tmp;

        GLuint          ofsTexCoords;
        GLuint          ofsTangents;
        GLuint          ofsBinormals;
        GLuint          ofsNormals;

        GLuint			sizeXYZ;
        GLuint			sizeTangents = 0;
        GLuint			sizeBinormals = 0;
        GLuint			sizeNormals = 0;

        int				vertexesNum;
        int				f;

        Com_InitGrowList(&vboSurfaces, 10);

        for(i = 0, surf = mdvModel->surfaces; i < mdvModel->numSurfaces; i++, surf++)
        {
            //allocate temp memory for vertex data
            vertexes = (mdvNormTanBi_t*)ri.Hunk_AllocateTempMemory( sizeof( *vertexes ) * surf->numVerts * mdvModel->numFrames );

            // calc tangent spaces
            {
                const float    *v0, *v1, *v2;
                const float    *t0, *t1, *t2;
                vec3_t          tangent;
                vec3_t          binormal;
                vec3_t          normal;

                for ( j = 0, vert = vertexes; j < ( surf->numVerts * mdvModel->numFrames ); j++, vert++ )
                {
                    VectorClear(vert->tangent);
                    VectorClear(vert->binormal);
                    VectorClear(vert->normal);
                }

                for(f = 0; f < mdvModel->numFrames; f++)
                {
                    for(j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++)
                    {
                        v0 = surf->verts[surf->numVerts * f + tri->indexes[0]].xyz;
                        v1 = surf->verts[surf->numVerts * f + tri->indexes[1]].xyz;
                        v2 = surf->verts[surf->numVerts * f + tri->indexes[2]].xyz;

                        t0 = surf->st[tri->indexes[0]].st;
                        t1 = surf->st[tri->indexes[1]].st;
                        t2 = surf->st[tri->indexes[2]].st;

#if 1
                        R_CalcTangentSpace(tangent, binormal, normal, v0, v1, v2, t0, t1, t2);
#else
                        R_CalcNormalForTriangle(normal, v0, v1, v2);
                        R_CalcTangentsForTriangle(tangent, binormal, v0, v1, v2, t0, t1, t2);
#endif

                        for(k = 0; k < 3; k++)
                        {
                            float          *v;

                            v = vertexes[surf->numVerts * f + tri->indexes[k]].tangent;
                            VectorAdd(v, tangent, v);

                            v = vertexes[surf->numVerts * f + tri->indexes[k]].binormal;
                            VectorAdd(v, binormal, v);

                            v = vertexes[surf->numVerts * f + tri->indexes[k]].normal;
                            VectorAdd(v, normal, v);
                        }
                    }
                }

                for ( j = 0, vert = vertexes; j < ( surf->numVerts * mdvModel->numFrames ); j++, vert++ )
                {
                    VectorNormalize(vert->tangent);
                    VectorNormalize(vert->binormal);
                    VectorNormalize(vert->normal);
                }
            }

            //ri.Printf(PRINT_ALL, "...calculating MD3 mesh VBOs ( '%s', %i verts %i tris )\n", surf->name, surf->numVerts, surf->numTriangles);

            // create surface
            vboSurf = (srfVBOMDVMesh_t*)ri.Hunk_Alloc(sizeof(*vboSurf), h_low);
            Com_AddToGrowList(&vboSurfaces, vboSurf);

            vboSurf->surfaceType = SF_VBO_MDVMESH;
            vboSurf->mdvModel = mdvModel;
            vboSurf->mdvSurface = surf;
            vboSurf->numIndexes = surf->numTriangles * 3;
            vboSurf->numVerts = surf->numVerts;

            /*
            vboSurf->vbo = R_CreateVBO2(va("staticWorldMesh_vertices %i", vboSurfaces.currentElements), numVerts, optimizedVerts,
            						   ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_TANGENT | ATTR_BINORMAL | ATTR_NORMAL
            						   | ATTR_COLOR);
            						   */

            vboSurf->ibo = R_CreateIBO2(va("staticMD3Mesh_IBO %s", surf->name), surf->numTriangles, surf->triangles, VBO_USAGE_STATIC);


            // create VBO
            vertexesNum = surf->numVerts;

            // allocate vbo data
            dataSize =	(surf->numVerts * mdvModel->numFrames * sizeof(vec4_t) * 4) + // xyz, tangent, binormal, normal
                        (surf->numVerts * sizeof(vec4_t)); // texcoords
            data = (byte*)ri.Hunk_AllocateTempMemory(dataSize);
            dataOfs = 0;

            // feed vertex XYZ
            for(f = 0; f < mdvModel->numFrames; f++)
            {
                for(j = 0; j < vertexesNum; j++)
                {
                    for(k = 0; k < 3; k++)
                    {
                        tmp[k] = vertexes[f * vertexesNum + j].xyz[k];
                    }
                    tmp[3] = 1;
                    Com_Memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t));
                    dataOfs += sizeof(vec4_t);
                }

                if(f == 0)
                {
                    sizeXYZ = dataOfs;
                }
            }

            // feed vertex texcoords
            ofsTexCoords = dataOfs;
            for(j = 0; j < vertexesNum; j++)
            {
                for(k = 0; k < 2; k++)
                {
                    tmp[k] = surf->st[j].st[k];
                }
                tmp[2] = 0;
                tmp[3] = 1;
                Com_Memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t));
                dataOfs += sizeof(vec4_t);
            }

            // feed vertex tangents
            ofsTangents = dataOfs;
            for(f = 0; f < mdvModel->numFrames; f++)
            {
                for(j = 0; j < vertexesNum; j++)
                {
                    for(k = 0; k < 3; k++)
                    {
                        tmp[k] = vertexes[f * vertexesNum + j].tangent[k];
                    }
                    tmp[3] = 1;
                    Com_Memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t));
                    dataOfs += sizeof(vec4_t);
                }

                if(f == 0)
                {
                    sizeTangents = dataOfs - ofsTangents;
                }
            }

            // feed vertex binormals
            ofsBinormals = dataOfs;
            for(f = 0; f < mdvModel->numFrames; f++)
            {
                for(j = 0; j < vertexesNum; j++)
                {
                    for(k = 0; k < 3; k++)
                    {
                        tmp[k] = vertexes[f * vertexesNum + j].binormal[k];
                    }
                    tmp[3] = 1;
                    Com_Memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t));
                    dataOfs += sizeof(vec4_t);
                }

                if(f == 0)
                {
                    sizeBinormals = dataOfs - ofsBinormals;
                }
            }

            // feed vertex normals
            ofsNormals = dataOfs;
            for(f = 0; f < mdvModel->numFrames; f++)
            {
                for(j = 0; j < vertexesNum; j++)
                {
                    for(k = 0; k < 3; k++)
                    {
                        tmp[k] = vertexes[f * vertexesNum + j].normal[k];
                    }
                    tmp[3] = 1;
                    Com_Memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t));
                    dataOfs += sizeof(vec4_t);
                }

                if(f == 0)
                {
                    sizeNormals = dataOfs - ofsNormals;
                }
            }

            vboSurf->vbo = R_CreateVBO(va("staticMD3Mesh_VBO '%s'", surf->name), data, dataSize, VBO_USAGE_STATIC);
            vboSurf->vbo->ofsXYZ = 0;
            vboSurf->vbo->ofsTexCoords = ofsTexCoords;
            vboSurf->vbo->ofsLightCoords = ofsTexCoords;
            vboSurf->vbo->ofsTangents = ofsTangents;
            vboSurf->vbo->ofsBinormals = ofsBinormals;
            vboSurf->vbo->ofsNormals = ofsNormals;

            vboSurf->vbo->sizeXYZ = sizeXYZ;
            vboSurf->vbo->sizeTangents = sizeTangents;
            vboSurf->vbo->sizeBinormals = sizeBinormals;
            vboSurf->vbo->sizeNormals = sizeNormals;

            ri.Hunk_FreeTempMemory(data);
        }

        // move VBO surfaces list to hunk
        mdvModel->numVBOSurfaces = vboSurfaces.currentElements;
        mdvModel->vboSurfaces = (srfVBOMDVMesh_t**)ri.Hunk_Alloc(mdvModel->numVBOSurfaces * sizeof(*mdvModel->vboSurfaces), h_low);

        for(i = 0; i < mdvModel->numVBOSurfaces; i++)
        {
            mdvModel->vboSurfaces[i] = (srfVBOMDVMesh_t *) Com_GrowListElement(&vboSurfaces, i);
        }

        Com_DestroyGrowList(&vboSurfaces);
    }
#endif
#endif // USE_D3D10
    return qtrue;
}
示例#3
0
/*
=================
R_LoadMD5
=================
*/
qboolean R_LoadMD5(model_t * mod, void *buffer, int bufferSize, const char *modName)
{
	int             i, j, k;
	md5Model_t     *md5;
	md5Bone_t      *bone;
	md5Surface_t   *surf;
	srfTriangle_t  *tri;
	md5Vertex_t    *v;
	md5Weight_t    *weight;
	int             version;
	shader_t       *sh;
	char           *buf_p;
	char           *token;
	vec3_t          boneOrigin;
	quat_t          boneQuat;
	matrix_t        boneMat;

	int				numRemaining;
	growList_t		sortedTriangles;
	growList_t      vboTriangles;
	growList_t      vboSurfaces;

	int				numBoneReferences;
	int				boneReferences[MAX_BONES];

	buf_p = (char *)buffer;

	// skip MD5Version indent string
	COM_ParseExt(&buf_p, qfalse);

	// check version
	token = COM_ParseExt(&buf_p, qfalse);
	version = atoi(token);
	if(version != MD5_VERSION)
	{
		ri.Printf(PRINT_WARNING, "R_LoadMD5: %s has wrong version (%i should be %i)\n", modName, version, MD5_VERSION);
		return qfalse;
	}

	mod->type = MOD_MD5;
	mod->dataSize += sizeof(md5Model_t);
	md5 = mod->md5 = ri.Hunk_Alloc(sizeof(md5Model_t), h_low);

	// skip commandline <arguments string>
	token = COM_ParseExt(&buf_p, qtrue);
	token = COM_ParseExt(&buf_p, qtrue);
//  ri.Printf(PRINT_ALL, "%s\n", token);

	// parse numJoints <number>
	token = COM_ParseExt(&buf_p, qtrue);
	if(Q_stricmp(token, "numJoints"))
	{
		ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'numJoints' found '%s' in model '%s'\n", token, modName);
		return qfalse;
	}
	token = COM_ParseExt(&buf_p, qfalse);
	md5->numBones = atoi(token);

	// parse numMeshes <number>
	token = COM_ParseExt(&buf_p, qtrue);
	if(Q_stricmp(token, "numMeshes"))
	{
		ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'numMeshes' found '%s' in model '%s'\n", token, modName);
		return qfalse;
	}
	token = COM_ParseExt(&buf_p, qfalse);
	md5->numSurfaces = atoi(token);
	//ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces);


	if(md5->numBones < 1)
	{
		ri.Printf(PRINT_WARNING, "R_LoadMD5: '%s' has no bones\n", modName);
		return qfalse;
	}
	if(md5->numBones > MAX_BONES)
	{
		ri.Printf(PRINT_WARNING, "R_LoadMD5: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones);
		return qfalse;
	}
	//ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i bones\n", modName, md5->numBones);

	// parse all the bones
	md5->bones = ri.Hunk_Alloc(sizeof(*bone) * md5->numBones, h_low);

	// parse joints {
	token = COM_ParseExt(&buf_p, qtrue);
	if(Q_stricmp(token, "joints"))
	{
		ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'joints' found '%s' in model '%s'\n", token, modName);
		return qfalse;
	}
	token = COM_ParseExt(&buf_p, qfalse);
	if(Q_stricmp(token, "{"))
	{
		ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName);
		return qfalse;
	}

	for(i = 0, bone = md5->bones; i < md5->numBones; i++, bone++)
	{
		token = COM_ParseExt(&buf_p, qtrue);
		Q_strncpyz(bone->name, token, sizeof(bone->name));

		//ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has bone '%s'\n", modName, bone->name);

		token = COM_ParseExt(&buf_p, qfalse);
		bone->parentIndex = atoi(token);

		//ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has bone '%s' with parent index %i\n", modName, bone->name, bone->parentIndex);

		if(bone->parentIndex >= md5->numBones)
		{
			ri.Error(ERR_DROP, "R_LoadMD5: '%s' has bone '%s' with bad parent index %i while numBones is %i\n", modName,
					 bone->name, bone->parentIndex, md5->numBones);
		}

		// skip (
		token = COM_ParseExt(&buf_p, qfalse);
		if(Q_stricmp(token, "("))
		{
			ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName);
			return qfalse;
		}

		for(j = 0; j < 3; j++)
		{
			token = COM_ParseExt(&buf_p, qfalse);
			boneOrigin[j] = atof(token);
		}

		// skip )
		token = COM_ParseExt(&buf_p, qfalse);
		if(Q_stricmp(token, ")"))
		{
			ri.Printf(PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName);
			return qfalse;
		}

		// skip (
		token = COM_ParseExt(&buf_p, qfalse);
		if(Q_stricmp(token, "("))
		{
			ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName);
			return qfalse;
		}

		for(j = 0; j < 3; j++)
		{
			token = COM_ParseExt(&buf_p, qfalse);
			boneQuat[j] = atof(token);
		}
		QuatCalcW(boneQuat);
		MatrixFromQuat(boneMat, boneQuat);

		VectorCopy(boneOrigin, bone->origin);
		QuatCopy(boneQuat, bone->rotation);

		MatrixSetupTransformFromQuat(bone->inverseTransform, boneQuat, boneOrigin);
		MatrixInverse(bone->inverseTransform);

		// skip )
		token = COM_ParseExt(&buf_p, qfalse);
		if(Q_stricmp(token, ")"))
		{
			ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName);
			return qfalse;
		}
	}

	// parse }
	token = COM_ParseExt(&buf_p, qtrue);
	if(Q_stricmp(token, "}"))
	{
		ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName);
		return qfalse;
	}

	// parse all the surfaces
	if(md5->numSurfaces < 1)
	{
		ri.Printf(PRINT_WARNING, "R_LoadMD5: '%s' has no surfaces\n", modName);
		return qfalse;
	}
	//ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces);

	md5->surfaces = ri.Hunk_Alloc(sizeof(*surf) * md5->numSurfaces, h_low);
	for(i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++)
	{
		// parse mesh {
		token = COM_ParseExt(&buf_p, qtrue);
		if(Q_stricmp(token, "mesh"))
		{
			ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'mesh' found '%s' in model '%s'\n", token, modName);
			return qfalse;
		}
		token = COM_ParseExt(&buf_p, qfalse);
		if(Q_stricmp(token, "{"))
		{
			ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName);
			return qfalse;
		}

		// change to surface identifier
		surf->surfaceType = SF_MD5;

		// give pointer to model for Tess_SurfaceMD5
		surf->model = md5;

		// parse shader <name>
		token = COM_ParseExt(&buf_p, qtrue);
		if(Q_stricmp(token, "shader"))
		{
			ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'shader' found '%s' in model '%s'\n", token, modName);
			return qfalse;
		}
		token = COM_ParseExt(&buf_p, qfalse);
		Q_strncpyz(surf->shader, token, sizeof(surf->shader));

		//ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' uses shader '%s'\n", modName, surf->shader);

		// FIXME .md5mesh meshes don't have surface names
		// lowercase the surface name so skin compares are faster
		//Q_strlwr(surf->name);
		//ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has surface '%s'\n", modName, surf->name);

		// register the shaders
		sh = R_FindShader(surf->shader, SHADER_3D_DYNAMIC, qtrue);
		if(sh->defaultShader)
		{
			surf->shaderIndex = 0;
		}
		else
		{
			surf->shaderIndex = sh->index;
		}

		// parse numVerts <number>
		token = COM_ParseExt(&buf_p, qtrue);
		if(Q_stricmp(token, "numVerts"))
		{
			ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'numVerts' found '%s' in model '%s'\n", token, modName);
			return qfalse;
		}
		token = COM_ParseExt(&buf_p, qfalse);
		surf->numVerts = atoi(token);

		if(surf->numVerts > SHADER_MAX_VERTEXES)
		{
			ri.Error(ERR_DROP, "R_LoadMD5: '%s' has more than %i verts on a surface (%i)",
					 modName, SHADER_MAX_VERTEXES, surf->numVerts);
		}

		surf->verts = ri.Hunk_Alloc(sizeof(*v) * surf->numVerts, h_low);
		for(j = 0, v = surf->verts; j < surf->numVerts; j++, v++)
		{
			// skip vert <number>
			token = COM_ParseExt(&buf_p, qtrue);
			if(Q_stricmp(token, "vert"))
			{
				ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'vert' found '%s' in model '%s'\n", token, modName);
				return qfalse;
			}
			COM_ParseExt(&buf_p, qfalse);

			// skip (
			token = COM_ParseExt(&buf_p, qfalse);
			if(Q_stricmp(token, "("))
			{
				ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName);
				return qfalse;
			}

			for(k = 0; k < 2; k++)
			{
				token = COM_ParseExt(&buf_p, qfalse);
				v->texCoords[k] = atof(token);
			}

			// skip )
			token = COM_ParseExt(&buf_p, qfalse);
			if(Q_stricmp(token, ")"))
			{
				ri.Printf(PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName);
				return qfalse;
			}

			token = COM_ParseExt(&buf_p, qfalse);
			v->firstWeight = atoi(token);

			token = COM_ParseExt(&buf_p, qfalse);
			v->numWeights = atoi(token);

			if(v->numWeights > MAX_WEIGHTS)
			{
				ri.Error(ERR_DROP, "R_LoadMD5: vertex %i requires more than %i weights on surface (%i) in model '%s'",
						 j, MAX_WEIGHTS, i, modName);
			}
		}

		// parse numTris <number>
		token = COM_ParseExt(&buf_p, qtrue);
		if(Q_stricmp(token, "numTris"))
		{
			ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'numTris' found '%s' in model '%s'\n", token, modName);
			return qfalse;
		}
		token = COM_ParseExt(&buf_p, qfalse);
		surf->numTriangles = atoi(token);

		if(surf->numTriangles > SHADER_MAX_TRIANGLES)
		{
			ri.Error(ERR_DROP, "R_LoadMD5: '%s' has more than %i triangles on a surface (%i)",
					 modName, SHADER_MAX_TRIANGLES, surf->numTriangles);
		}

		surf->triangles = ri.Hunk_Alloc(sizeof(*tri) * surf->numTriangles, h_low);
		for(j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++)
		{
			// skip tri <number>
			token = COM_ParseExt(&buf_p, qtrue);
			if(Q_stricmp(token, "tri"))
			{
				ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'tri' found '%s' in model '%s'\n", token, modName);
				return qfalse;
			}
			COM_ParseExt(&buf_p, qfalse);

			for(k = 0; k < 3; k++)
			{
				token = COM_ParseExt(&buf_p, qfalse);
				tri->indexes[k] = atoi(token);
			}
		}

		R_CalcSurfaceTriangleNeighbors(surf->numTriangles, surf->triangles);

		// parse numWeights <number>
		token = COM_ParseExt(&buf_p, qtrue);
		if(Q_stricmp(token, "numWeights"))
		{
			ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'numWeights' found '%s' in model '%s'\n", token, modName);
			return qfalse;
		}
		token = COM_ParseExt(&buf_p, qfalse);
		surf->numWeights = atoi(token);

		surf->weights = ri.Hunk_Alloc(sizeof(*weight) * surf->numWeights, h_low);
		for(j = 0, weight = surf->weights; j < surf->numWeights; j++, weight++)
		{
			// skip weight <number>
			token = COM_ParseExt(&buf_p, qtrue);
			if(Q_stricmp(token, "weight"))
			{
				ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'weight' found '%s' in model '%s'\n", token, modName);
				return qfalse;
			}
			COM_ParseExt(&buf_p, qfalse);

			token = COM_ParseExt(&buf_p, qfalse);
			weight->boneIndex = atoi(token);

			token = COM_ParseExt(&buf_p, qfalse);
			weight->boneWeight = atof(token);

			// skip (
			token = COM_ParseExt(&buf_p, qfalse);
			if(Q_stricmp(token, "("))
			{
				ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName);
				return qfalse;
			}

			for(k = 0; k < 3; k++)
			{
				token = COM_ParseExt(&buf_p, qfalse);
				weight->offset[k] = atof(token);
			}

			// skip )
			token = COM_ParseExt(&buf_p, qfalse);
			if(Q_stricmp(token, ")"))
			{
				ri.Printf(PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName);
				return qfalse;
			}
		}

		// parse }
		token = COM_ParseExt(&buf_p, qtrue);
		if(Q_stricmp(token, "}"))
		{
			ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName);
			return qfalse;
		}

		// loop trough all vertices and set up the vertex weights
		for(j = 0, v = surf->verts; j < surf->numVerts; j++, v++)
		{
			v->weights = ri.Hunk_Alloc(sizeof(*v->weights) * v->numWeights, h_low);

			for(k = 0; k < v->numWeights; k++)
			{
				v->weights[k] = surf->weights + (v->firstWeight + k);
			}
		}
	}

	// loading is done now calculate the bounding box and tangent spaces
	ClearBounds(md5->bounds[0], md5->bounds[1]);

	for(i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++)
	{
		for(j = 0, v = surf->verts; j < surf->numVerts; j++, v++)
		{
			vec3_t          tmpVert;
			md5Weight_t    *w;

			VectorClear(tmpVert);

			for(k = 0, w = v->weights[0]; k < v->numWeights; k++, w++)
			{
				vec3_t          offsetVec;

				bone = &md5->bones[w->boneIndex];

				QuatTransformVector(bone->rotation, w->offset, offsetVec);
				VectorAdd(bone->origin, offsetVec, offsetVec);

				VectorMA(tmpVert, w->boneWeight, offsetVec, tmpVert);
			}

			VectorCopy(tmpVert, v->position);
			AddPointToBounds(tmpVert, md5->bounds[0], md5->bounds[1]);
		}

		// calc tangent spaces
#if 1
		{
			const float    *v0, *v1, *v2;
			const float    *t0, *t1, *t2;
			vec3_t          tangent;
			vec3_t          binormal;
			vec3_t          normal;

			for(j = 0, v = surf->verts; j < surf->numVerts; j++, v++)
			{
				VectorClear(v->tangent);
				VectorClear(v->binormal);
				VectorClear(v->normal);
			}

			for(j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++)
			{
				v0 = surf->verts[tri->indexes[0]].position;
				v1 = surf->verts[tri->indexes[1]].position;
				v2 = surf->verts[tri->indexes[2]].position;

				t0 = surf->verts[tri->indexes[0]].texCoords;
				t1 = surf->verts[tri->indexes[1]].texCoords;
				t2 = surf->verts[tri->indexes[2]].texCoords;

#if 1
				R_CalcTangentSpace(tangent, binormal, normal, v0, v1, v2, t0, t1, t2);
#else
				R_CalcNormalForTriangle(normal, v0, v1, v2);
				R_CalcTangentsForTriangle(tangent, binormal, v0, v1, v2, t0, t1, t2);
#endif

				for(k = 0; k < 3; k++)
				{
					float          *v;

					v = surf->verts[tri->indexes[k]].tangent;
					VectorAdd(v, tangent, v);

					v = surf->verts[tri->indexes[k]].binormal;
					VectorAdd(v, binormal, v);

					v = surf->verts[tri->indexes[k]].normal;
					VectorAdd(v, normal, v);
				}
			}

			for(j = 0, v = surf->verts; j < surf->numVerts; j++, v++)
			{
				VectorNormalize(v->tangent);
				VectorNormalize(v->binormal);
				VectorNormalize(v->normal);
			}
		}
#else
		{
			int             k;
			float           bb, s, t;
			vec3_t          bary;
			vec3_t			faceNormal;
			md5Vertex_t    *dv[3];

			for(j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++)
			{
				dv[0] = &surf->verts[tri->indexes[0]];
				dv[1] = &surf->verts[tri->indexes[1]];
				dv[2] = &surf->verts[tri->indexes[2]];

				R_CalcNormalForTriangle(faceNormal, dv[0]->position, dv[1]->position, dv[2]->position);

				// calculate barycentric basis for the triangle
				bb = (dv[1]->texCoords[0] - dv[0]->texCoords[0]) * (dv[2]->texCoords[1] - dv[0]->texCoords[1]) - (dv[2]->texCoords[0] - dv[0]->texCoords[0]) * (dv[1]->texCoords[1] -
																													  dv[0]->texCoords[1]);
				if(fabs(bb) < 0.00000001f)
					continue;

				// do each vertex
				for(k = 0; k < 3; k++)
				{
					// calculate s tangent vector
					s = dv[k]->texCoords[0] + 10.0f;
					t = dv[k]->texCoords[1];
					bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb;
					bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb;
					bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb;

					dv[k]->tangent[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0];
					dv[k]->tangent[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1];
					dv[k]->tangent[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2];

					VectorSubtract(dv[k]->tangent, dv[k]->position, dv[k]->tangent);
					VectorNormalize(dv[k]->tangent);

					// calculate t tangent vector (binormal)
					s = dv[k]->texCoords[0];
					t = dv[k]->texCoords[1] + 10.0f;
					bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb;
					bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb;
					bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb;

					dv[k]->binormal[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0];
					dv[k]->binormal[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1];
					dv[k]->binormal[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2];

					VectorSubtract(dv[k]->binormal, dv[k]->position, dv[k]->binormal);
					VectorNormalize(dv[k]->binormal);

					// calculate the normal as cross product N=TxB
#if 0
					CrossProduct(dv[k]->tangent, dv[k]->binormal, dv[k]->normal);
					VectorNormalize(dv[k]->normal);

					// Gram-Schmidt orthogonalization process for B
					// compute the cross product B=NxT to obtain
					// an orthogonal basis
					CrossProduct(dv[k]->normal, dv[k]->tangent, dv[k]->binormal);

					if(DotProduct(dv[k]->normal, faceNormal) < 0)
					{
						VectorInverse(dv[k]->normal);
						//VectorInverse(dv[k]->tangent);
						//VectorInverse(dv[k]->binormal);
					}
#else
					VectorAdd(dv[k]->normal, faceNormal, dv[k]->normal);
#endif
				}
			}

#if 1
			for(j = 0, v = surf->verts; j < surf->numVerts; j++, v++)
			{
				//VectorNormalize(v->tangent);
				//VectorNormalize(v->binormal);
				VectorNormalize(v->normal);
			}
#endif
		}
#endif

#if 0
		// do another extra smoothing for normals to avoid flat shading
		for(j = 0; j < surf->numVerts; j++)
		{
			for(k = 0; k < surf->numVerts; k++)
			{
				if(j == k)
					continue;

				if(VectorCompare(surf->verts[j].position, surf->verts[k].position))
				{
					VectorAdd(surf->verts[j].normal, surf->verts[k].normal, surf->verts[j].normal);
				}
			}

			VectorNormalize(surf->verts[j].normal);
		}
#endif
	}

	// split the surfaces into VBO surfaces by the maximum number of GPU vertex skinning bones
	Com_InitGrowList(&vboSurfaces, 10);

	for(i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++)
	{
		// sort triangles
		Com_InitGrowList(&sortedTriangles, 1000);

		for(j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++)
		{
			skelTriangle_t *sortTri = Com_Allocate(sizeof(*sortTri));

			for(k = 0; k < 3; k++)
			{
				sortTri->indexes[k] = tri->indexes[k];
				sortTri->vertexes[k] = &surf->verts[tri->indexes[k]];
			}
			sortTri->referenced = qfalse;

			Com_AddToGrowList(&sortedTriangles, sortTri);
		}

		//qsort(sortedTriangles.elements, sortedTriangles.currentElements, sizeof(void *), CompareTrianglesByBoneReferences);

#if 0
		for(j = 0; j < sortedTriangles.currentElements; j++)
		{
			int		b[MAX_WEIGHTS * 3];

			skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j);

			for(k = 0; k < 3; k++)
			{
				v = sortTri->vertexes[k];

				for(l = 0; l < MAX_WEIGHTS; l++)
				{
					b[k * 3 + l] = (l < v->numWeights) ? v->weights[l]->boneIndex : 9999;
				}

				qsort(b, MAX_WEIGHTS * 3, sizeof(int), CompareBoneIndices);
				//ri.Printf(PRINT_ALL, "bone indices: %i %i %i %i\n", b[k * 3 + 0], b[k * 3 + 1], b[k * 3 + 2], b[k * 3 + 3]);
			}
		}
#endif

		numRemaining = sortedTriangles.currentElements;
		while(numRemaining)
		{
			numBoneReferences = 0;
			Com_Memset(boneReferences, 0, sizeof(boneReferences));

			Com_InitGrowList(&vboTriangles, 1000);

			for(j = 0; j < sortedTriangles.currentElements; j++)
			{
				skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j);

				if(sortTri->referenced)
					continue;

				if(AddTriangleToVBOTriangleList(&vboTriangles, sortTri, &numBoneReferences, boneReferences))
				{
					sortTri->referenced = qtrue;
				}
			}

			if(!vboTriangles.currentElements)
			{
				ri.Printf(PRINT_WARNING, "R_LoadMD5: could not add triangles to a remaining VBO surfaces for model '%s'\n", modName);
				break;
			}

			AddSurfaceToVBOSurfacesList(&vboSurfaces, &vboTriangles, md5, surf, i, numBoneReferences, boneReferences);
			numRemaining -= vboTriangles.currentElements;

			Com_DestroyGrowList(&vboTriangles);
		}

		for(j = 0; j < sortedTriangles.currentElements; j++)
		{
			skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j);

			Com_Dealloc(sortTri);
		}
		Com_DestroyGrowList(&sortedTriangles);
	}

	// move VBO surfaces list to hunk
	md5->numVBOSurfaces = vboSurfaces.currentElements;
	md5->vboSurfaces = ri.Hunk_Alloc(md5->numVBOSurfaces * sizeof(*md5->vboSurfaces), h_low);

	for(i = 0; i < md5->numVBOSurfaces; i++)
	{
		md5->vboSurfaces[i] = (srfVBOMD5Mesh_t *) Com_GrowListElement(&vboSurfaces, i);
	}

	Com_DestroyGrowList(&vboSurfaces);

	return qtrue;
}