Beispiel #1
0
qboolean R_LoadMDM(model_t *mod, void *buffer, const char *modName)
{
	int i, j, k;

	mdmHeader_t *mdm;
//    mdmFrame_t            *frame;
	mdmSurface_t  *mdmSurf;
	mdmTriangle_t *mdmTri;
	mdmVertex_t   *mdmVertex;
	mdmTag_t      *mdmTag;
	int           version;
//	int             size;
	shader_t *sh;
	int32_t  *collapseMap, *collapseMapOut, *boneref, *bonerefOut;

	mdmModel_t         *mdmModel;
	mdmTagIntern_t     *tag;
	mdmSurfaceIntern_t *surf;
	srfTriangle_t      *tri;
	md5Vertex_t        *v;

	mdm = ( mdmHeader_t * ) buffer;

	version = LittleLong(mdm->version);

	if (version != MDM_VERSION)
	{
		ri.Printf(PRINT_WARNING, "R_LoadMDM: %s has wrong version (%i should be %i)\n", modName, version, MDM_VERSION);
		return qfalse;
	}

	mod->type = MOD_MDM;
//	size = LittleLong(mdm->ofsEnd);
	mod->dataSize += sizeof(mdmModel_t);

	//mdm = mod->mdm = ri.Hunk_Alloc(size, h_low);
	//memcpy(mdm, buffer, LittleLong(pinmodel->ofsEnd));

	mdmModel = mod->mdm = ri.Hunk_Alloc(sizeof(mdmModel_t), h_low);

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

	mdmModel->lodBias  = LittleFloat(mdm->lodBias);
	mdmModel->lodScale = LittleFloat(mdm->lodScale);

	/*  mdm->skel = RE_RegisterModel(mdm->bonesfile);
	        if ( !mdm->skel ) {
	                ri.Error (ERR_DROP, "R_LoadMDM: %s skeleton not found", mdm->bonesfile );
	        }

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

	// swap all the frames

	/*frameSize = (int) ( sizeof( mdmFrame_t ) );
	   for ( i = 0 ; i < mdm->numFrames ; i++, frame++) {
	   frame = (mdmFrame_t *) ( (byte *)mdm + mdm->ofsFrames + i * frameSize );
	   frame->radius = LittleFloat( frame->radius );
	   for ( j = 0 ; j < 3 ; j++ ) {
	   frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] );
	   frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] );
	   frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] );
	   frame->parentOffset[j] = LittleFloat( frame->parentOffset[j] );
	   }
	   } */

	// swap all the tags
	mdmModel->numTags = mdm->numTags;
	mdmModel->tags    = tag = ri.Hunk_Alloc(sizeof(*tag) * mdm->numTags, h_low);

	mdmTag = ( mdmTag_t * )(( byte * ) mdm + mdm->ofsTags);

	for (i = 0; i < mdm->numTags; i++, tag++)
	{
		int ii;

		Q_strncpyz(tag->name, mdmTag->name, sizeof(tag->name));

		for (ii = 0; ii < 3; ii++)
		{
			tag->axis[ii][0] = LittleFloat(mdmTag->axis[ii][0]);
			tag->axis[ii][1] = LittleFloat(mdmTag->axis[ii][1]);
			tag->axis[ii][2] = LittleFloat(mdmTag->axis[ii][2]);
		}

		tag->boneIndex = LittleLong(mdmTag->boneIndex);
		//tag->torsoWeight = LittleFloat( tag->torsoWeight );
		tag->offset[0] = LittleFloat(mdmTag->offset[0]);
		tag->offset[1] = LittleFloat(mdmTag->offset[1]);
		tag->offset[2] = LittleFloat(mdmTag->offset[2]);

		LL(mdmTag->numBoneReferences);
		LL(mdmTag->ofsBoneReferences);
		LL(mdmTag->ofsEnd);

		tag->numBoneReferences = mdmTag->numBoneReferences;
		tag->boneReferences    = ri.Hunk_Alloc(sizeof(*bonerefOut) * mdmTag->numBoneReferences, h_low);

		// swap the bone references
		boneref = ( int32_t * )(( byte * ) mdmTag + mdmTag->ofsBoneReferences);

		for (j = 0, bonerefOut = tag->boneReferences; j < mdmTag->numBoneReferences; j++, boneref++, bonerefOut++)
		{
			*bonerefOut = LittleLong(*boneref);
		}

		// find the next tag
		mdmTag = ( mdmTag_t * )(( byte * ) mdmTag + mdmTag->ofsEnd);
	}

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

	mdmSurf = ( mdmSurface_t * )(( byte * ) mdm + mdm->ofsSurfaces);

	for (i = 0, surf = mdmModel->surfaces; i < mdm->numSurfaces; i++, surf++)
	{
		LL(mdmSurf->shaderIndex);
		LL(mdmSurf->ofsHeader);
		LL(mdmSurf->ofsCollapseMap);
		LL(mdmSurf->numTriangles);
		LL(mdmSurf->ofsTriangles);
		LL(mdmSurf->numVerts);
		LL(mdmSurf->ofsVerts);
		LL(mdmSurf->numBoneReferences);
		LL(mdmSurf->ofsBoneReferences);
		LL(mdmSurf->ofsEnd);

		surf->minLod = LittleLong(mdmSurf->minLod);

		// change to surface identifier
		surf->surfaceType = SF_MDM;
		surf->model       = mdmModel;

		Q_strncpyz(surf->name, mdmSurf->name, sizeof(surf->name));

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

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

		// register the shaders
		if (mdmSurf->shader[0])
		{
			Q_strncpyz(surf->shader, mdmSurf->shader, sizeof(surf->shader));

			sh = R_FindShader(surf->shader, SHADER_3D_DYNAMIC, qtrue);

			if (sh->defaultShader)
			{
				surf->shaderIndex = 0;
			}
			else
			{
				surf->shaderIndex = sh->index;
			}
		}
		else
		{
			surf->shaderIndex = 0;
		}

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

		mdmTri = ( mdmTriangle_t * )(( byte * ) mdmSurf + mdmSurf->ofsTriangles);

		for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, mdmTri++, tri++)
		{
			tri->indexes[0] = LittleLong(mdmTri->indexes[0]);
			tri->indexes[1] = LittleLong(mdmTri->indexes[1]);
			tri->indexes[2] = LittleLong(mdmTri->indexes[2]);
		}

		// swap all the vertexes
		surf->numVerts = mdmSurf->numVerts;
		surf->verts    = ri.Hunk_Alloc(sizeof(*v) * surf->numVerts, h_low);

		mdmVertex = ( mdmVertex_t * )(( byte * ) mdmSurf + mdmSurf->ofsVerts);

		for (j = 0, v = surf->verts; j < mdmSurf->numVerts; j++, v++)
		{
			v->normal[0] = LittleFloat(mdmVertex->normal[0]);
			v->normal[1] = LittleFloat(mdmVertex->normal[1]);
			v->normal[2] = LittleFloat(mdmVertex->normal[2]);

			v->texCoords[0] = LittleFloat(mdmVertex->texCoords[0]);
			v->texCoords[1] = LittleFloat(mdmVertex->texCoords[1]);

			v->numWeights = LittleLong(mdmVertex->numWeights);

			if (v->numWeights > MAX_WEIGHTS)
			{
#if 0
				ri.Error(ERR_DROP, "R_LoadMDM: vertex %i requires %i instead of maximum %i weights on surface (%i) in model '%s'",
				         j, v->numWeights, MAX_WEIGHTS, i, modName);
#else
				ri.Printf(PRINT_WARNING, "WARNING: R_LoadMDM: vertex %i requires %i instead of maximum %i weights on surface (%i) in model '%s'\n",
				          j, v->numWeights, MAX_WEIGHTS, i, modName);
#endif
			}

			v->weights = ri.Hunk_Alloc(sizeof(*v->weights) * v->numWeights, h_low);

			for (k = 0; k < v->numWeights; k++)
			{
				md5Weight_t *weight = ri.Hunk_Alloc(sizeof(*weight), h_low);

				weight->boneIndex  = LittleLong(mdmVertex->weights[k].boneIndex);
				weight->boneWeight = LittleFloat(mdmVertex->weights[k].boneWeight);
				weight->offset[0]  = LittleFloat(mdmVertex->weights[k].offset[0]);
				weight->offset[1]  = LittleFloat(mdmVertex->weights[k].offset[1]);
				weight->offset[2]  = LittleFloat(mdmVertex->weights[k].offset[2]);

				v->weights[k] = weight;
			}

			mdmVertex = ( mdmVertex_t * ) &mdmVertex->weights[v->numWeights];
		}

		// swap the collapse map
		surf->collapseMap = ri.Hunk_Alloc(sizeof(*collapseMapOut) * mdmSurf->numVerts, h_low);

		collapseMap = ( int32_t * )(( byte * ) mdmSurf + mdmSurf->ofsCollapseMap);

		//ri.Printf(PRINT_ALL, "collapse map for mdm surface '%s': ", surf->name);
		for (j = 0, collapseMapOut = surf->collapseMap; j < mdmSurf->numVerts; j++, collapseMap++, collapseMapOut++)
		{
			int32_t value = LittleLong(*collapseMap);
			//surf->collapseMap[j] = value;
			*collapseMapOut = value;

			//ri.Printf(PRINT_ALL, "(%i -> %i) ", j, value);
		}

		//ri.Printf(PRINT_ALL, "\n");

#if 0
		ri.Printf(PRINT_ALL, "collapse map for mdm surface '%s': ", surf->name);

		for (j = 0, collapseMap = surf->collapseMap; j < mdmSurf->numVerts; j++, collapseMap++)
		{
			ri.Printf(PRINT_ALL, "(%i -> %i) ", j, *collapseMap);
		}

		ri.Printf(PRINT_ALL, "\n");
#endif

		// swap the bone references
		surf->numBoneReferences = mdmSurf->numBoneReferences;
		surf->boneReferences    = ri.Hunk_Alloc(sizeof(*bonerefOut) * mdmSurf->numBoneReferences, h_low);

		boneref = ( int32_t * )(( byte * ) mdmSurf + mdmSurf->ofsBoneReferences);

		for (j = 0, bonerefOut = surf->boneReferences; j < surf->numBoneReferences; j++, boneref++, bonerefOut++)
		{
			*bonerefOut = LittleLong(*boneref);
		}

		// find the next surface
		mdmSurf = ( mdmSurface_t * )(( byte * ) mdmSurf + mdmSurf->ofsEnd);
	}

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

	for (i = 0, surf = mdmModel->surfaces; i < mdmModel->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;

				//VectorClear(offsetVec);

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

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

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

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

		// calc tangent spaces
#if 0
		{
			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
	{
		int        numRemaining;
		growList_t sortedTriangles;
		growList_t vboTriangles;
		growList_t vboSurfaces;

		int numBoneReferences;
		int boneReferences[MAX_BONES];

		Com_InitGrowList(&vboSurfaces, 10);

		for (i = 0, surf = mdmModel->surfaces; i < mdmModel->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_LoadMDM: could not add triangles to a remaining VBO surface for model '%s'\n", modName);
					break;
				}

				AddSurfaceToVBOSurfacesListMDM(&vboSurfaces, &vboTriangles, mdmModel, 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
		mdmModel->numVBOSurfaces = vboSurfaces.currentElements;
		mdmModel->vboSurfaces    = ri.Hunk_Alloc(mdmModel->numVBOSurfaces * sizeof(*mdmModel->vboSurfaces), h_low);

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

		Com_DestroyGrowList(&vboSurfaces);
	}

	return qtrue;
}
Beispiel #2
0
void FogDrawSurfaces(entity_t * e)
{
	int             i, j, k, fogNum;
	fog_t          *fog;
	mapDrawSurface_t *ds;
	vec3_t          mins, maxs;
	int             fogged, numFogged;
	int             numBaseDrawSurfs;


	/* note it */
	Sys_FPrintf(SYS_VRB, "----- FogDrawSurfs -----\n");

	/* reset counters */
	numFogged = 0;
	numFogFragments = 0;

	/* walk fog list */
	for(fogNum = 0; fogNum < numMapFogs; fogNum++)
	{
		/* get fog */
		fog = &mapFogs[fogNum];

		/* clip each surface into this, but don't clip any of the resulting fragments to the same brush */
		numBaseDrawSurfs = numMapDrawSurfs;
		for(i = 0; i < numBaseDrawSurfs; i++)
		{
			/* get the drawsurface */
			ds = &mapDrawSurfs[i];

			/* no fog? */
			if(ds->shaderInfo->noFog)
				continue;

			/* global fog doesn't have a brush */
			if(fog->brush == NULL)
			{
				/* don't re-fog already fogged surfaces */
				if(ds->fogNum >= 0)
					continue;
				fogged = 1;
			}
			else
			{
				/* find drawsurface bounds */
				ClearBounds(mins, maxs);
				for(j = 0; j < ds->numVerts; j++)
					AddPointToBounds(ds->verts[j].xyz, mins, maxs);

				/* check against the fog brush */
				for(k = 0; k < 3; k++)
				{
					if(mins[k] > fog->brush->maxs[k])
						break;
					if(maxs[k] < fog->brush->mins[k])
						break;
				}

				/* no intersection? */
				if(k < 3)
					continue;

				/* ydnar: gs mods: handle the various types of surfaces */
				switch (ds->type)
				{
						/* handle brush faces */
					case SURFACE_FACE:
						fogged = ChopFaceSurfaceByBrush(e, ds, fog->brush);
						break;

						/* handle patches */
					case SURFACE_PATCH:
						fogged = ChopPatchSurfaceByBrush(e, ds, fog->brush);
						break;

						/* handle triangle surfaces (fixme: split triangle surfaces) */
					case SURFACE_TRIANGLES:
					case SURFACE_FORCED_META:
					case SURFACE_META:
						fogged = 1;
						break;

						/* no fogging */
					default:
						fogged = 0;
						break;
				}
			}

			/* is this surface fogged? */
			if(fogged)
			{
				numFogged += fogged;
				ds->fogNum = fogNum;
			}
		}
	}

	/* emit some statistics */
	Sys_FPrintf(SYS_VRB, "%9d fog polygon fragments\n", numFogFragments);
	Sys_FPrintf(SYS_VRB, "%9d fog patch fragments\n", numFogPatchFragments);
	Sys_FPrintf(SYS_VRB, "%9d fogged drawsurfs\n", numFogged);
}
Beispiel #3
0
/*
============
ExpandBrush
=============
*/
void ExpandBrush (int hullnum)
{
	int			i, x, s;
	vec3_t		corner;
	winding_t	*w;
	plane_t		plane;

	int				j, k, numwindings;
	vec_t			r;
	winding_t		**windings;
	plane_t			clipplane, faceplane;
	mface_t			*mf;
	vec3_t			point;
	vec3_t		mins, maxs;

	if (!numbrushfaces)
		return;

	num_hull_points = 0;
	num_hull_edges = 0;

	ClearBounds( mins, maxs );

	// generate windings and bounds data
	numwindings = 0;
	windings = calloc(numbrushfaces, sizeof(*windings));
	for (i = 0;i < numbrushfaces;i++)
	{
		mf = &faces[i];
		windings[i] = NULL;

		faceplane = mf->plane;
		w = BaseWindingForPlane (&faceplane);

		for (j = 0;j < numbrushfaces && w;j++)
		{
			clipplane = faces[j].plane;
			if( j == i )
				continue;

			// flip the plane, because we want to keep the back side
			VectorNegate(clipplane.normal, clipplane.normal);
			clipplane.dist *= -1;

			w = ClipWindingEpsilon (w, &clipplane, ON_EPSILON, true);
		}

		if (!w)
			continue;	// overcontrained plane

		for (j = 0;j < w->numpoints;j++)
		{
			for (k = 0;k < 3;k++)
			{
				point[k] = w->points[j][k];
				r = Q_rint( point[k] );
				if ( fabs( point[k] - r ) < ZERO_EPSILON)
					w->points[j][k] = r;
				else
					w->points[j][k] = point[k];

				// check for incomplete brushes
				if( w->points[j][k] >= BOGUS_RANGE || w->points[j][k] <= -BOGUS_RANGE )
					return;
			}

			AddPointToBounds( w->points[j], mins, maxs );
		}

		windings[i] = w;
	}

	// add all of the corner offsets
	for (i = 0;i < numwindings;i++)
	{
		w = windings[i];
		for (j = 0;j < w->numpoints;j++)
			AddHullPoint(w->points[j], hullnum);
	}

	// expand the face planes
	for (i = 0;i < numbrushfaces;i++)
	{
		mf = &faces[i];
		for (x=0 ; x<3 ; x++)
		{
			if (mf->plane.normal[x] > 0)
				corner[x] = -hullinfo.hullsizes[hullnum][0][x];
			else if (mf->plane.normal[x] < 0)
				corner[x] = -hullinfo.hullsizes[hullnum][1][x];
		}
		mf->plane.dist += DotProduct (corner, mf->plane.normal);
	}

	// add any axis planes not contained in the brush to bevel off corners
	for (x=0 ; x<3 ; x++)
		for (s=-1 ; s<=1 ; s+=2)
		{
			// add the plane
			VectorClear (plane.normal);
			plane.normal[x] = s;
			if (s == -1)
				plane.dist = -mins[x] + hullinfo.hullsizes[hullnum][1][x];
			else
				plane.dist = maxs[x] + -hullinfo.hullsizes[hullnum][0][x];
			AddBrushPlane (&plane);
		}

	// add all of the edge bevels
	for (i = 0;i < numwindings;i++)
	{
		w = windings[i];
		for (j = 0;j < w->numpoints;j++)
			AddHullEdge(w->points[j], w->points[(j+1)%w->numpoints], hullnum);
	}

	// free the windings as we no longer need them
	for (i = 0;i < numwindings;i++)
		if (windings[i])
			FreeWinding(windings[i]);
	free(windings);
}
Beispiel #4
0
/*
=================
R_MarkFragments

=================
*/
int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection,
                     int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer )
{
    int              numsurfaces, numPlanes;
    int              i, j, k, m, n;
    surfaceType_t    *surfaces[ 64 ];
    vec3_t           mins, maxs;
    int              returnedFragments;
    int              returnedPoints;
    vec3_t           normals[ MAX_VERTS_ON_POLY + 2 ];
    float            dists[ MAX_VERTS_ON_POLY + 2 ];
    vec3_t           clipPoints[ 2 ][ MAX_VERTS_ON_POLY ];
    int              numClipPoints;
    float            *v;
    srfSurfaceFace_t *face;
    srfGridMesh_t    *cv;
    srfTriangles_t   *trisurf;
    srfVert_t        *dv;
    srfTriangle_t    *tri;
    vec3_t           normal;
    vec3_t           projectionDir;
    vec3_t           v1, v2;

    //increment view count for double check prevention
    tr.viewCountNoReset++;

    //
    VectorNormalize2( projection, projectionDir );
    // find all the brushes that are to be considered
    ClearBounds( mins, maxs );

    for ( i = 0; i < numPoints; i++ )
    {
        vec3_t temp;

        AddPointToBounds( points[ i ], mins, maxs );
        VectorAdd( points[ i ], projection, temp );
        AddPointToBounds( temp, mins, maxs );
        // make sure we get all the leafs (also the one(s) in front of the hit surface)
        VectorMA( points[ i ], -20, projectionDir, temp );
        AddPointToBounds( temp, mins, maxs );
    }

    if ( numPoints > MAX_VERTS_ON_POLY )
    {
        numPoints = MAX_VERTS_ON_POLY;
    }

    // create the bounding planes for the to be projected polygon
    for ( i = 0; i < numPoints; i++ )
    {
        VectorSubtract( points[( i + 1 ) % numPoints ], points[ i ], v1 );
        VectorAdd( points[ i ], projection, v2 );
        VectorSubtract( points[ i ], v2, v2 );
        CrossProduct( v1, v2, normals[ i ] );
        VectorNormalizeFast( normals[ i ] );
        dists[ i ] = DotProduct( normals[ i ], points[ i ] );
    }

    // add near and far clipping planes for projection
    VectorCopy( projectionDir, normals[ numPoints ] );
    dists[ numPoints ] = DotProduct( normals[ numPoints ], points[ 0 ] ) - 32;
    VectorCopy( projectionDir, normals[ numPoints + 1 ] );
    VectorInverse( normals[ numPoints + 1 ] );
    dists[ numPoints + 1 ] = DotProduct( normals[ numPoints + 1 ], points[ 0 ] ) - 20;
    numPlanes = numPoints + 2;

    numsurfaces = 0;
    R_BoxSurfaces_r( tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir );

    returnedPoints = 0;
    returnedFragments = 0;

    for ( i = 0; i < numsurfaces; i++ )
    {
        if ( *surfaces[ i ] == SF_GRID )
        {
            cv = ( srfGridMesh_t * ) surfaces[ i ];

            for ( m = 0; m < cv->height - 1; m++ )
            {
                for ( n = 0; n < cv->width - 1; n++ )
                {
                    // We triangulate the grid and chop all triangles within
                    // the bounding planes of the to be projected polygon.
                    // LOD is not taken into account, not such a big deal though.
                    //
                    // It's probably much nicer to chop the grid itself and deal
                    // with this chopped grid as an SF_GRID surface, so LOD will
                    // be applied. However the LOD of that chopped grid must
                    // be synced with the LOD of the original curve.
                    // One way to do this; the chopped grid shares vertices with
                    // the original curve. When LOD is applied to the original
                    // curve the unused vertices are flagged. Now the chopped curve
                    // should skip the flagged vertices. This still leaves the
                    // problems with the vertices at the chopped grid edges.
                    //
                    // To avoid issues when LOD applied to "hollow curves" (like
                    // the ones around many jump pads) we now just add a 2 unit
                    // offset to the triangle vertices.
                    // The offset is added in the vertex normal vector direction
                    // so all triangles will still fit together.
                    // The 2 unit offset should avoid pretty much all LOD problems.

                    numClipPoints = 3;

                    dv = cv->verts + m * cv->width + n;

                    VectorCopy( dv[ 0 ].xyz, clipPoints[ 0 ][ 0 ] );
                    VectorMA( clipPoints[ 0 ][ 0 ], MARKER_OFFSET, dv[ 0 ].normal, clipPoints[ 0 ][ 0 ] );
                    VectorCopy( dv[ cv->width ].xyz, clipPoints[ 0 ][ 1 ] );
                    VectorMA( clipPoints[ 0 ][ 1 ], MARKER_OFFSET, dv[ cv->width ].normal, clipPoints[ 0 ][ 1 ] );
                    VectorCopy( dv[ 1 ].xyz, clipPoints[ 0 ][ 2 ] );
                    VectorMA( clipPoints[ 0 ][ 2 ], MARKER_OFFSET, dv[ 1 ].normal, clipPoints[ 0 ][ 2 ] );
                    // check the normal of this triangle
                    VectorSubtract( clipPoints[ 0 ][ 0 ], clipPoints[ 0 ][ 1 ], v1 );
                    VectorSubtract( clipPoints[ 0 ][ 2 ], clipPoints[ 0 ][ 1 ], v2 );
                    CrossProduct( v1, v2, normal );
                    VectorNormalizeFast( normal );

                    if ( DotProduct( normal, projectionDir ) < -0.1 )
                    {
                        // add the fragments of this triangle
                        R_AddMarkFragments( numClipPoints, clipPoints,
                                            numPlanes, normals, dists,
                                            maxPoints, pointBuffer,
                                            maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs );

                        if ( returnedFragments == maxFragments )
                        {
                            return returnedFragments; // not enough space for more fragments
                        }
                    }

                    VectorCopy( dv[ 1 ].xyz, clipPoints[ 0 ][ 0 ] );
                    VectorMA( clipPoints[ 0 ][ 0 ], MARKER_OFFSET, dv[ 1 ].normal, clipPoints[ 0 ][ 0 ] );
                    VectorCopy( dv[ cv->width ].xyz, clipPoints[ 0 ][ 1 ] );
                    VectorMA( clipPoints[ 0 ][ 1 ], MARKER_OFFSET, dv[ cv->width ].normal, clipPoints[ 0 ][ 1 ] );
                    VectorCopy( dv[ cv->width + 1 ].xyz, clipPoints[ 0 ][ 2 ] );
                    VectorMA( clipPoints[ 0 ][ 2 ], MARKER_OFFSET, dv[ cv->width + 1 ].normal, clipPoints[ 0 ][ 2 ] );
                    // check the normal of this triangle
                    VectorSubtract( clipPoints[ 0 ][ 0 ], clipPoints[ 0 ][ 1 ], v1 );
                    VectorSubtract( clipPoints[ 0 ][ 2 ], clipPoints[ 0 ][ 1 ], v2 );
                    CrossProduct( v1, v2, normal );
                    VectorNormalizeFast( normal );

                    if ( DotProduct( normal, projectionDir ) < -0.05 )
                    {
                        // add the fragments of this triangle
                        R_AddMarkFragments( numClipPoints, clipPoints,
                                            numPlanes, normals, dists,
                                            maxPoints, pointBuffer,
                                            maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs );

                        if ( returnedFragments == maxFragments )
                        {
                            return returnedFragments; // not enough space for more fragments
                        }
                    }
                }
            }
        }
        else if ( *surfaces[ i ] == SF_FACE )
        {
            face = ( srfSurfaceFace_t * ) surfaces[ i ];

            // check the normal of this face
            if ( DotProduct( face->plane.normal, projectionDir ) > -0.5 )
            {
                continue;
            }

            for ( k = 0, tri = face->triangles; k < face->numTriangles; k++, tri++ )
            {
                for ( j = 0; j < 3; j++ )
                {
                    v = face->verts[ tri->indexes[ j ] ].xyz;
                    VectorMA( v, MARKER_OFFSET, face->plane.normal, clipPoints[ 0 ][ j ] );
                }

                // add the fragments of this face
                R_AddMarkFragments( 3, clipPoints,
                                    numPlanes, normals, dists,
                                    maxPoints, pointBuffer,
                                    maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs );

                if ( returnedFragments == maxFragments )
                {
                    return returnedFragments; // not enough space for more fragments
                }
            }
        }
        else if ( *surfaces[ i ] == SF_TRIANGLES && !r_noMarksOnTrisurfs->integer )
        {
            trisurf = ( srfTriangles_t * ) surfaces[ i ];

            for ( k = 0, tri = trisurf->triangles; k < trisurf->numTriangles; k++, tri++ )
            {
                for ( j = 0; j < 3; j++ )
                {
                    VectorCopy( trisurf->verts[ tri->indexes[ j ] ].xyz, clipPoints[ 0 ][ j ] );
                }

                // add the fragments of this face
                R_AddMarkFragments( 3, clipPoints,
                                    numPlanes, normals, dists,
                                    maxPoints, pointBuffer,
                                    maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs );

                if ( returnedFragments == maxFragments )
                {
                    return returnedFragments; // not enough space for more fragments
                }
            }
        }
    }

    return returnedFragments;
}
Beispiel #5
0
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int AAS_StoreArea( tmp_area_t *tmparea ) {
	int side, edgenum, i;
	plane_t *plane;
	tmp_face_t *tmpface;
	aas_area_t *aasarea;
	aas_edge_t *edge;
	aas_face_t *aasface;
	aas_faceindex_t aasfacenum;
	vec3_t facecenter;
	winding_t *w;

	//when the area is merged go to the merged area
	//FIXME: this isn't necessary anymore because the tree
	//			is refreshed after area merging
	while ( tmparea->mergedarea ) tmparea = tmparea->mergedarea;
	//
	if ( tmparea->invalid ) {
		Error( "AAS_StoreArea: tried to store invalid area" );
	}
	//if there is an aas area already stored for this tmp area
	if ( tmparea->aasareanum ) {
		return -tmparea->aasareanum;
	}
	//
	if ( ( *aasworld ).numareas >= max_aas.max_areas ) {
		Error( "AAS_MAX_AREAS = %d", max_aas.max_areas );
	} //end if
	  //area zero is a dummy
	if ( ( *aasworld ).numareas == 0 ) {
		( *aasworld ).numareas = 1;
	}
	//create an area from this leaf
	aasarea = &( *aasworld ).areas[( *aasworld ).numareas];
	aasarea->areanum = ( *aasworld ).numareas;
	aasarea->numfaces = 0;
	aasarea->firstface = ( *aasworld ).faceindexsize;
	ClearBounds( aasarea->mins, aasarea->maxs );
	VectorClear( aasarea->center );
	//
//	Log_Write("tmparea %d became aasarea %d\r\n", tmparea->areanum, aasarea->areanum);
	//store the aas area number at the tmp area
	tmparea->aasareanum = aasarea->areanum;
	//
	for ( tmpface = tmparea->tmpfaces; tmpface; tmpface = tmpface->next[side] )
	{
		side = tmpface->frontarea != tmparea;
		//if there's an aas face created for the tmp face already
		if ( tmpface->aasfacenum ) {
			//we're at the back of the face so use a negative index
			aasfacenum = -tmpface->aasfacenum;
#ifdef DEBUG
			if ( tmpface->aasfacenum < 0 || tmpface->aasfacenum > max_aas.max_faces ) {
				Error( "AAS_CreateTree_r: face number out of range" );
			} //end if
#endif //DEBUG
			aasface = &( *aasworld ).faces[tmpface->aasfacenum];
			aasface->backarea = aasarea->areanum;
		} //end if
		else
		{
			plane = &mapplanes[tmpface->planenum ^ side];
			if ( side ) {
				w = tmpface->winding;
				tmpface->winding = ReverseWinding( tmpface->winding );
			} //end if
			if ( !AAS_GetFace( tmpface->winding, plane, 0, &aasfacenum ) ) {
				continue;
			}
			if ( side ) {
				FreeWinding( tmpface->winding );
				tmpface->winding = w;
			} //end if
			aasface = &( *aasworld ).faces[aasfacenum];
			aasface->frontarea = aasarea->areanum;
			aasface->backarea = 0;
			aasface->faceflags = tmpface->faceflags;
			//set the face number at the tmp face
			tmpface->aasfacenum = aasfacenum;
		} //end else
		  //add face points to the area bounds and
		  //calculate the face 'center'
		VectorClear( facecenter );
		for ( edgenum = 0; edgenum < aasface->numedges; edgenum++ )
		{
			edge = &( *aasworld ).edges[abs( ( *aasworld ).edgeindex[aasface->firstedge + edgenum] )];
			for ( i = 0; i < 2; i++ )
			{
				AddPointToBounds( ( *aasworld ).vertexes[edge->v[i]], aasarea->mins, aasarea->maxs );
				VectorAdd( ( *aasworld ).vertexes[edge->v[i]], facecenter, facecenter );
			} //end for
		} //end for
		VectorScale( facecenter, 1.0 / ( aasface->numedges * 2.0 ), facecenter );
		//add the face 'center' to the area 'center'
		VectorAdd( aasarea->center, facecenter, aasarea->center );
		//
		if ( ( *aasworld ).faceindexsize >= max_aas.max_faceindexsize ) {
			Error( "AAS_MAX_FACEINDEXSIZE = %d", max_aas.max_faceindexsize );
		} //end if
		( *aasworld ).faceindex[( *aasworld ).faceindexsize++] = aasfacenum;
		aasarea->numfaces++;
	} //end for
	  //if the area has no faces at all (return 0, = solid leaf)
	if ( !aasarea->numfaces ) {
		return 0;
	}
	//
	VectorScale( aasarea->center, 1.0 / aasarea->numfaces, aasarea->center );
	//Log_Write("area %d center %f %f %f\r\n", (*aasworld).numareas,
	//				aasarea->center[0], aasarea->center[1], aasarea->center[2]);
	//store the area settings
	AAS_StoreAreaSettings( tmparea->settings );
	//
	//Log_Write("tmp area %d became aas area %d\r\n", tmpareanum, aasarea->areanum);
	qprintf( "\r%6d", aasarea->areanum );
	//
	if ( ( *aasworld ).areasettings[aasarea->areanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
		static int num;
		Log_Write( "***** area %d is a cluster portal %d\n", aasarea->areanum, num++ );
	} //end if
	  //
	( *aasworld ).numareas++;
	return -( ( *aasworld ).numareas - 1 );
} //end of the function AAS_StoreArea
Beispiel #6
0
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_ParseExt2(&buf_p, qfalse);

	// check version
	token   = COM_ParseExt2(&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_ParseExt2(&buf_p, qtrue);
	token = COM_ParseExt2(&buf_p, qtrue);
	//  ri.Printf(PRINT_ALL, "%s\n", token);

	// parse numJoints <number>
	token = COM_ParseExt2(&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_ParseExt2(&buf_p, qfalse);
	md5->numBones = atoi(token);

	// parse numMeshes <number>
	token = COM_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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", modName,
			         bone->name, bone->parentIndex, md5->numBones);
		}

		// skip (
		token = COM_ParseExt2(&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_ParseExt2(&buf_p, qfalse);
			boneOrigin[j] = atof(token);
		}

		// skip )
		token = COM_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&buf_p, qfalse);

			// skip (
			token = COM_ParseExt2(&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_ParseExt2(&buf_p, qfalse);
				v->texCoords[k] = atof(token);
			}

			// skip )
			token = COM_ParseExt2(&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_ParseExt2(&buf_p, qfalse);
			v->firstWeight = atoi(token);

			token         = COM_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&buf_p, qfalse);

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

		// parse numWeights <number>
		token = COM_ParseExt2(&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_ParseExt2(&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_ParseExt2(&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_ParseExt2(&buf_p, qfalse);

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

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

			// skip (
			token = COM_ParseExt2(&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_ParseExt2(&buf_p, qfalse);
				weight->offset[k] = atof(token);
			}

			// skip )
			token = COM_ParseExt2(&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_ParseExt2(&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);
				Com_DestroyGrowList(&vboTriangles);
				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;
}
Beispiel #7
0
/*
============
BuildST

Builds the triangle_st array for the base frame and
model.skinwidth / model.skinheight

  FIXME: allow this to be loaded from a file for
  arbitrary mappings
============
*/
void BuildST (triangle_t *ptri, int numtri)
{
	int			i, j;
	int			width, height, iwidth, iheight, swidth;
	float		basex, basey;
	float		s_scale, t_scale;
	float		scale;
	vec3_t		mins, maxs;
	float		*pbasevert;
	vec3_t		vtemp1, vtemp2, normal;

	//
	// find bounds of all the verts on the base frame
	//
	ClearBounds (mins, maxs);

	for (i=0 ; i<numtri ; i++)
		for (j=0 ; j<3 ; j++)
			AddPointToBounds (ptri[i].verts[j], mins, maxs);

	for (i=0 ; i<3 ; i++)
	{
		mins[i] = floor(mins[i]);
		maxs[i] = ceil(maxs[i]);
	}

	width = maxs[0] - mins[0];
	height = maxs[2] - mins[2];

	if (!g_fixedwidth)
	{	// old style
		scale = 8;
		if (width*scale >= 150)
			scale = 150.0 / width;
		if (height*scale >= 190)
			scale = 190.0 / height;

		s_scale = t_scale = scale;

		iwidth = ceil(width*s_scale);
		iheight = ceil(height*t_scale);

		iwidth += 4;
		iheight += 4;
	}
	else
	{	// new style
		iwidth = g_fixedwidth / 2;
		iheight = g_fixedheight;

		s_scale = (float)(iwidth-4) / width;
		t_scale = (float)(iheight-4) / height;
	}

//
// determine which side of each triangle to map the texture to
//
	for (i=0 ; i<numtri ; i++)
	{
		VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
		VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
		CrossProduct (vtemp1, vtemp2, normal);

		if (normal[1] > 0)
		{
			basex = iwidth + 2;
		}
		else
		{
			basex = 2;
		}
		basey = 2;

		for (j=0 ; j<3 ; j++)
		{
			pbasevert = ptri[i].verts[j];

			triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);
			triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);
		}
	}

// make the width a multiple of 4; some hardware requires this, and it ensures
// dword alignment for each scan
	swidth = iwidth*2;
	model.skinwidth = (swidth + 3) & ~3;
	model.skinheight = iheight;
}
/*
=====================
RE_AddPolyToScene

=====================
*/
void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) {
	srfPoly_t	*poly;
	int			i, j;
	int			fogIndex;
	fog_t		*fog;
	vec3_t		bounds[2];

	if ( !tr.registered ) {
		return;
	}

	if ( !hShader ) {
		ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n");
		return;
	}

	for ( j = 0; j < numPolys; j++ ) {
		if ( r_numpolyverts + numVerts > max_polyverts || r_numpolys >= max_polys ) {
      /*
      NOTE TTimo this was initially a PRINT_WARNING
      but it happens a lot with high fighting scenes and particles
      since we don't plan on changing the const and making for room for those effects
      simply cut this message to developer only
      */
			ri.Printf( PRINT_DEVELOPER, "WARNING: RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n");
			return;
		}

		poly = &backEndData[tr.smpFrame]->polys[r_numpolys];
		poly->surfaceType = SF_POLY;
		poly->hShader = hShader;
		poly->numVerts = numVerts;
		poly->verts = &backEndData[tr.smpFrame]->polyVerts[r_numpolyverts];

		Com_Memcpy( poly->verts, &verts[numVerts*j], numVerts * sizeof( *verts ) );

		if ( glConfig.hardwareType == GLHW_RAGEPRO ) {
			poly->verts->modulate[0] = 255;
			poly->verts->modulate[1] = 255;
			poly->verts->modulate[2] = 255;
			poly->verts->modulate[3] = 255;
		}
		// done.
		r_numpolys++;
		r_numpolyverts += numVerts;

		// if no world is loaded
		if ( tr.world == NULL ) {
			fogIndex = 0;
		}
		// see if it is in a fog volume
		else if ( tr.world->numfogs == 1 ) {
			fogIndex = 0;
		} else {
			// find which fog volume the poly is in
			VectorCopy( poly->verts[0].xyz, bounds[0] );
			VectorCopy( poly->verts[0].xyz, bounds[1] );
			for ( i = 1 ; i < poly->numVerts ; i++ ) {
				AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] );
			}
			for ( fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++ ) {
				fog = &tr.world->fogs[fogIndex];
				if ( bounds[1][0] >= fog->bounds[0][0]
					&& bounds[1][1] >= fog->bounds[0][1]
					&& bounds[1][2] >= fog->bounds[0][2]
					&& bounds[0][0] <= fog->bounds[1][0]
					&& bounds[0][1] <= fog->bounds[1][1]
					&& bounds[0][2] <= fog->bounds[1][2] ) {
					break;
				}
			}
			if ( fogIndex == tr.world->numfogs ) {
				fogIndex = 0;
			}
		}
		poly->fogIndex = fogIndex;
	}
}
Beispiel #9
0
/*
=======================================================================================================================================
RE_AddPolyToScene

=======================================================================================================================================
*/
void RE_AddPolyToScene(qhandle_t hShader, int numVerts, const polyVert_t *verts) {
	srfPoly_t   *poly;
	int i;
	int fogIndex;
	fog_t       *fog;
	vec3_t bounds[2];

	if (!tr.registered) {
		return;
	}

	if (!hShader) {
		// This isn't a useful warning, and an hShader of zero isn't a null shader, it's
		// the default shader.
		//ri.Printf(PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n");
		//return;
	}

	if (((r_numpolyverts + numVerts) >= max_polyverts) || (r_numpolys >= max_polys)) {
		return;
	}

	poly = &backEndData->polys[r_numpolys];
	poly->surfaceType = SF_POLY;
	poly->hShader = hShader;
	poly->numVerts = numVerts;
	poly->verts = &backEndData->polyVerts[r_numpolyverts];

	memcpy(poly->verts, verts, numVerts * sizeof(*verts));
	// Ridah
	if (glConfig.hardwareType == GLHW_RAGEPRO) {
		poly->verts->modulate[0] = 255;
		poly->verts->modulate[1] = 255;
		poly->verts->modulate[2] = 255;
		poly->verts->modulate[3] = 255;
	}
	// done.
	r_numpolys++;
	r_numpolyverts += numVerts;

	// see if it is in a fog volume
	if (tr.world->numfogs == 1) {
		fogIndex = 0;
	} else {
		// find which fog volume the poly is in
		VectorCopy(poly->verts[0].xyz, bounds[0]);
		VectorCopy(poly->verts[0].xyz, bounds[1]);
		for (i = 1 ; i < poly->numVerts ; i++) {
			AddPointToBounds(poly->verts[i].xyz, bounds[0], bounds[1]);
		}
		for (fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++) {
			fog = &tr.world->fogs[fogIndex];
			if (bounds[1][0] >= fog->bounds[0][0]
				 && bounds[1][1] >= fog->bounds[0][1]
				 && bounds[1][2] >= fog->bounds[0][2]
				 && bounds[0][0] <= fog->bounds[1][0]
				 && bounds[0][1] <= fog->bounds[1][1]
				 && bounds[0][2] <= fog->bounds[1][2]) {
				break;
			}
		}
		if (fogIndex == tr.world->numfogs) {
			fogIndex = 0;
		}
	}
	poly->fogIndex = fogIndex;
}
Beispiel #10
0
/*
=================
R_MarkFragments

=================
*/
int R_MarkFragments( int orientation, const vec3_t *points, const vec3_t projection,
					 int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) {
	int numsurfaces, numPlanes;
	int i, j, k, m, n;
	surfaceType_t   *surfaces[4096];
	vec3_t mins, maxs;
	int returnedFragments;
	int returnedPoints;
	vec3_t normals[MAX_VERTS_ON_POLY + 2];
	float dists[MAX_VERTS_ON_POLY + 2];
	vec3_t clipPoints[2][MAX_VERTS_ON_POLY];
	int numClipPoints;
	float           *v;
	srfGridMesh_t   *cv;
	drawVert_t      *dv;
	vec3_t normal;
	vec3_t projectionDir;
	vec3_t v1, v2;
	int             *indexes;
	float radius;
	vec3_t center;          // center of original mark
	int numPoints = 4;              // Ridah, we were only ever passing in 4, so I made this local and used the parameter for the orientation
	qboolean oldMapping = qfalse;

	if (numPoints <= 0) {
		return 0;
	}

	//increment view count for double check prevention
	tr.viewCount++;

	// RF, negative maxFragments means we want original mapping
	if ( maxFragments < 0 ) {
		maxFragments = -maxFragments;
		oldMapping = qtrue;
	}

	VectorClear( center );
	for ( i = 0 ; i < numPoints ; i++ ) {
		VectorAdd( points[i], center, center );
	}
	VectorScale( center, 1.0 / numPoints, center );
	//
	radius = VectorNormalize2( projection, projectionDir ) / 2.0;
	bestdist = 0;
	VectorNegate( projectionDir, bestnormal );
	// find all the brushes that are to be considered
	ClearBounds( mins, maxs );
	for ( i = 0 ; i < numPoints ; i++ ) {
		vec3_t temp;

		AddPointToBounds( points[i], mins, maxs );
		VectorMA( points[i], 1 * ( 1 + oldMapping * radius * 4 ), projection, temp );
		AddPointToBounds( temp, mins, maxs );
		// make sure we get all the leafs (also the one(s) in front of the hit surface)
		VectorMA( points[i], -20 * ( 1.0 + (float)oldMapping * ( radius / 20.0 ) * 4 ), projectionDir, temp );
		AddPointToBounds( temp, mins, maxs );
	}

	if ( numPoints > MAX_VERTS_ON_POLY ) {
		numPoints = MAX_VERTS_ON_POLY;
	}
	// create the bounding planes for the to be projected polygon
	for ( i = 0 ; i < numPoints ; i++ ) {
		VectorSubtract( points[( i + 1 ) % numPoints], points[i], v1 );
		VectorAdd( points[i], projection, v2 );
		VectorSubtract( points[i], v2, v2 );
		CrossProduct( v1, v2, normals[i] );
		VectorNormalize( normals[i] );
		dists[i] = DotProduct( normals[i], points[i] );
	}
	// add near and far clipping planes for projection
	VectorCopy( projectionDir, normals[numPoints] );
	dists[numPoints] = DotProduct( normals[numPoints], points[0] ) - radius * ( 1 + oldMapping * 10 );
	VectorCopy( projectionDir, normals[numPoints + 1] );
	VectorInverse( normals[numPoints + 1] );
	dists[numPoints + 1] = DotProduct( normals[numPoints + 1], points[0] ) - radius * ( 1 + oldMapping * 10 );
	numPlanes = numPoints + 2;

	numsurfaces = 0;
	R_BoxSurfaces_r( tr.world->nodes, mins, maxs, surfaces, 4096, &numsurfaces, projectionDir );

	returnedPoints = 0;
	returnedFragments = 0;

	// find the closest surface to center the decal there, and wrap around other surfaces
	if ( !oldMapping ) {
		VectorNegate( bestnormal, bestnormal );
	}

	for ( i = 0 ; i < numsurfaces ; i++ ) {

		if ( *surfaces[i] == SF_GRID ) {

			cv = (srfGridMesh_t *) surfaces[i];
			for ( m = 0 ; m < cv->height - 1 ; m++ ) {
				for ( n = 0 ; n < cv->width - 1 ; n++ ) {
					// We triangulate the grid and chop all triangles within
					// the bounding planes of the to be projected polygon.
					// LOD is not taken into account, not such a big deal though.
					//
					// It's probably much nicer to chop the grid itself and deal
					// with this grid as a normal SF_GRID surface so LOD will
					// be applied. However the LOD of that chopped grid must
					// be synced with the LOD of the original curve.
					// One way to do this; the chopped grid shares vertices with
					// the original curve. When LOD is applied to the original
					// curve the unused vertices are flagged. Now the chopped curve
					// should skip the flagged vertices. This still leaves the
					// problems with the vertices at the chopped grid edges.
					//
					// To avoid issues when LOD applied to "hollow curves" (like
					// the ones around many jump pads) we now just add a 2 unit
					// offset to the triangle vertices.
					// The offset is added in the vertex normal vector direction
					// so all triangles will still fit together.
					// The 2 unit offset should avoid pretty much all LOD problems.

					numClipPoints = 3;

					dv = cv->verts + m * cv->width + n;

					VectorCopy( dv[0].xyz, clipPoints[0][0] );
					VectorMA( clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0] );
					VectorCopy( dv[cv->width].xyz, clipPoints[0][1] );
					VectorMA( clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1] );
					VectorCopy( dv[1].xyz, clipPoints[0][2] );
					VectorMA( clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2] );
					// check the normal of this triangle
					VectorSubtract( clipPoints[0][0], clipPoints[0][1], v1 );
					VectorSubtract( clipPoints[0][2], clipPoints[0][1], v2 );
					CrossProduct( v1, v2, normal );
					VectorNormalize( normal );
					if ( DotProduct( normal, projectionDir ) < -0.1 ) {
						// add the fragments of this triangle
						R_AddMarkFragments( numClipPoints, clipPoints,
											numPlanes, normals, dists,
											maxPoints, pointBuffer,
											maxFragments, fragmentBuffer,
											&returnedPoints, &returnedFragments, mins, maxs );

						if ( returnedFragments == maxFragments ) {
							return returnedFragments;   // not enough space for more fragments
						}
					}

					VectorCopy( dv[1].xyz, clipPoints[0][0] );
					VectorMA( clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0] );
					VectorCopy( dv[cv->width].xyz, clipPoints[0][1] );
					VectorMA( clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1] );
					VectorCopy( dv[cv->width + 1].xyz, clipPoints[0][2] );
					VectorMA( clipPoints[0][2], MARKER_OFFSET, dv[cv->width + 1].normal, clipPoints[0][2] );
					// check the normal of this triangle
					VectorSubtract( clipPoints[0][0], clipPoints[0][1], v1 );
					VectorSubtract( clipPoints[0][2], clipPoints[0][1], v2 );
					CrossProduct( v1, v2, normal );
					VectorNormalize( normal );
					if ( DotProduct( normal, projectionDir ) < -0.05 ) {
						// add the fragments of this triangle
						R_AddMarkFragments( numClipPoints, clipPoints,
											numPlanes, normals, dists,
											maxPoints, pointBuffer,
											maxFragments, fragmentBuffer,
											&returnedPoints, &returnedFragments, mins, maxs );

						if ( returnedFragments == maxFragments ) {
							return returnedFragments;   // not enough space for more fragments
						}
					}
				}
			}
		} else if ( *surfaces[i] == SF_FACE )     {
			extern float VectorDistance( vec3_t v1, vec3_t v2 );
			vec3_t axis[3];
			float texCoordScale, dot;
			vec3_t originalPoints[4];
			vec3_t newCenter, delta;
			int oldNumPoints;
			float epsilon = 0.5;
			// duplicated so we don't mess with the original clips for the curved surfaces
			vec3_t lnormals[MAX_VERTS_ON_POLY + 2];
			float ldists[MAX_VERTS_ON_POLY + 2];
			vec3_t lmins, lmaxs;

			srfSurfaceFace_t *surf = ( srfSurfaceFace_t * ) surfaces[i];

			if ( !oldMapping ) {

				// Ridah, create a new clip box such that this decal surface is mapped onto
				// the current surface without distortion. To find the center of the new clip box,
				// we project the center of the original impact center out along the projection vector,
				// onto the current surface

				// find the center of the new decal
				dot = DotProduct( center, surf->plane.normal );
				dot -= surf->plane.dist;
				// check the normal of this face
				if ( dot < -epsilon && DotProduct( surf->plane.normal, projectionDir ) >= 0.01 ) {
					continue;
				} else if ( fabs( dot ) > radius ) {
					continue;
				}
				// if the impact point is behind the surface, subtract the projection, otherwise add it
				VectorMA( center, -dot, bestnormal, newCenter );

				// recalc dot from the offset position
				dot = DotProduct( newCenter, surf->plane.normal );
				dot -= surf->plane.dist;
				VectorMA( newCenter, -dot, surf->plane.normal, newCenter );

				VectorMA( newCenter, MARKER_OFFSET, surf->plane.normal, newCenter );

				// create the texture axis
				VectorNormalize2( surf->plane.normal, axis[0] );
				PerpendicularVector( axis[1], axis[0] );
				RotatePointAroundVector( axis[2], axis[0], axis[1], (float)orientation );
				CrossProduct( axis[0], axis[2], axis[1] );

				texCoordScale = 0.5 * 1.0 / radius;

				// create the full polygon
				for ( j = 0 ; j < 3 ; j++ ) {
					originalPoints[0][j] = newCenter[j] - radius * axis[1][j] - radius * axis[2][j];
					originalPoints[1][j] = newCenter[j] + radius * axis[1][j] - radius * axis[2][j];
					originalPoints[2][j] = newCenter[j] + radius * axis[1][j] + radius * axis[2][j];
					originalPoints[3][j] = newCenter[j] - radius * axis[1][j] + radius * axis[2][j];
				}

				ClearBounds( lmins, lmaxs );

				// create the bounding planes for the to be projected polygon
				for ( j = 0 ; j < 4 ; j++ ) {
					AddPointToBounds( originalPoints[j], lmins, lmaxs );

					VectorSubtract( originalPoints[( j + 1 ) % numPoints], originalPoints[j], v1 );
					VectorSubtract( originalPoints[j], surf->plane.normal, v2 );
					VectorSubtract( originalPoints[j], v2, v2 );
					CrossProduct( v1, v2, lnormals[j] );
					VectorNormalize( lnormals[j] );
					ldists[j] = DotProduct( lnormals[j], originalPoints[j] );
				}
				numPlanes = numPoints;

				// done.

				indexes = ( int * )( (byte *)surf + surf->ofsIndices );
				for ( k = 0 ; k < surf->numIndices ; k += 3 ) {
					for ( j = 0 ; j < 3 ; j++ ) {
						v = surf->points[0] + VERTEXSIZE * indexes[k + j];
						VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] );
					}

					oldNumPoints = returnedPoints;

					// add the fragments of this face
					R_AddMarkFragments( 3, clipPoints,
										numPlanes, lnormals, ldists,
										maxPoints, pointBuffer,
										maxFragments, fragmentBuffer,
										&returnedPoints, &returnedFragments, lmins, lmaxs );

					if ( oldNumPoints != returnedPoints ) {
						// flag this surface as already having computed ST's
						fragmentBuffer[returnedFragments - 1].numPoints *= -1;

						// Ridah, calculate ST's
						for ( j = 0 ; j < ( returnedPoints - oldNumPoints ) ; j++ ) {
							VectorSubtract( (float *)pointBuffer + 5 * ( oldNumPoints + j ), newCenter, delta );
							*( (float *)pointBuffer + 5 * ( oldNumPoints + j ) + 3 ) = 0.5 + DotProduct( delta, axis[1] ) * texCoordScale;
							*( (float *)pointBuffer + 5 * ( oldNumPoints + j ) + 4 ) = 0.5 + DotProduct( delta, axis[2] ) * texCoordScale;
						}
					}

					if ( returnedFragments == maxFragments ) {
						return returnedFragments;   // not enough space for more fragments
					}
				}

			} else {    // old mapping

				// check the normal of this face
				//if (DotProduct(surf->plane.normal, projectionDir) > 0.0) {
				//	continue;
				//}

				indexes = ( int * )( (byte *)surf + surf->ofsIndices );
				for ( k = 0 ; k < surf->numIndices ; k += 3 ) {
					for ( j = 0 ; j < 3 ; j++ ) {
						v = surf->points[0] + VERTEXSIZE * indexes[k + j];;
						VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] );
					}
					// add the fragments of this face
					R_AddMarkFragments( 3, clipPoints,
										numPlanes, normals, dists,
										maxPoints, pointBuffer,
										maxFragments, fragmentBuffer,
										&returnedPoints, &returnedFragments, mins, maxs );
					if ( returnedFragments == maxFragments ) {
						return returnedFragments;   // not enough space for more fragments
					}
				}

			}

		}
		else if(*surfaces[i] == SF_TRIANGLES && r_marksOnTriangleMeshes->integer) {

			srfTriangles_t *surf = (srfTriangles_t *) surfaces[i];

			for (k = 0; k < surf->numIndexes; k += 3)
			{
				for(j = 0; j < 3; j++)
				{
					v = surf->verts[surf->indexes[k + j]].xyz;
					VectorMA(v, MARKER_OFFSET, surf->verts[surf->indexes[k + j]].normal, clipPoints[0][j]);
				}

				// add the fragments of this face
				R_AddMarkFragments(3, clipPoints,
								   numPlanes, normals, dists,
								   maxPoints, pointBuffer,
								   maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs);
				if(returnedFragments == maxFragments)
				{
					return returnedFragments;	// not enough space for more fragments
				}
			}
		}
	}
	return returnedFragments;
}
Beispiel #11
0
void BeginModel( void ){
	bspModel_t  *mod;
	brush_t     *b;
	entity_t    *e;
	vec3_t mins, maxs;
	vec3_t lgMins, lgMaxs;          /* ydnar: lightgrid mins/maxs */
	parseMesh_t *p;
	int i;


	/* test limits */
	if ( numBSPModels == MAX_MAP_MODELS ) {
		Error( "MAX_MAP_MODELS" );
	}

	/* get model and entity */
	mod = &bspModels[ numBSPModels ];
	e = &entities[ mapEntityNum ];

	/* ydnar: lightgrid mins/maxs */
	ClearBounds( lgMins, lgMaxs );

	/* bound the brushes */
	ClearBounds( mins, maxs );
	for ( b = e->brushes; b; b = b->next )
	{
		/* ignore non-real brushes (origin, etc) */
		if ( b->numsides == 0 ) {
			continue;
		}
		AddPointToBounds( b->mins, mins, maxs );
		AddPointToBounds( b->maxs, mins, maxs );

		/* ydnar: lightgrid bounds */
		if ( b->compileFlags & C_LIGHTGRID ) {
			AddPointToBounds( b->mins, lgMins, lgMaxs );
			AddPointToBounds( b->maxs, lgMins, lgMaxs );
		}
	}

	/* bound patches */
	for ( p = e->patches; p; p = p->next )
	{
		for ( i = 0; i < ( p->mesh.width * p->mesh.height ); i++ )
			AddPointToBounds( p->mesh.verts[i].xyz, mins, maxs );
	}

	/* ydnar: lightgrid mins/maxs */
	if ( lgMins[ 0 ] < 99999 ) {
		/* use lightgrid bounds */
		VectorCopy( lgMins, mod->mins );
		VectorCopy( lgMaxs, mod->maxs );
	}
	else
	{
		/* use brush/patch bounds */
		VectorCopy( mins, mod->mins );
		VectorCopy( maxs, mod->maxs );
	}

	/* note size */
	Sys_FPrintf( SYS_VRB, "BSP bounds: { %f %f %f } { %f %f %f }\n", mins[ 0 ], mins[ 1 ], mins[ 2 ], maxs[ 0 ], maxs[ 1 ], maxs[ 2 ] );
	Sys_FPrintf( SYS_VRB, "Lightgrid bounds: { %f %f %f } { %f %f %f }\n", lgMins[ 0 ], lgMins[ 1 ], lgMins[ 2 ], lgMaxs[ 0 ], lgMaxs[ 1 ], lgMaxs[ 2 ] );

	/* set firsts */
	mod->firstBSPSurface = numBSPDrawSurfaces;
	mod->firstBSPBrush = numBSPBrushes;
}
Beispiel #12
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;
	md5Triangle_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;

	buf_p = ( char * ) buffer;

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

	// check version
	token = COM_ParseExt2( &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->model.md5 = ri.Hunk_Alloc( sizeof( md5Model_t ), h_low );

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

	// parse numJoints <number>
	token = COM_ParseExt2( &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_ParseExt2( &buf_p, qfalse );
	md5->numBones = atoi( token );

	// parse numMeshes <number>
	token = COM_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &buf_p, qfalse );
			boneOrigin[ j ] = atof( token );
		}

		// skip )
		token = COM_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &buf_p, qtrue );

		if ( Q_stricmp( token, "shader" ) )
		{
			Q_strncpyz( surf->shader, "<default>", sizeof( surf->shader ) );
			surf->shaderIndex = 0;
		}
		else
		{
			token = COM_ParseExt2( &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, LIGHTMAP_NONE, qtrue );

			if ( sh->defaultShader )
			{
				surf->shaderIndex = 0;
			}
			else
			{
				surf->shaderIndex = sh->index;
			}

			token = COM_ParseExt2( &buf_p, qtrue );
		}

		// parse numVerts <number>
		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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &buf_p, qfalse );

			// skip (
			token = COM_ParseExt2( &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_ParseExt2( &buf_p, qfalse );
				v->texCoords[ k ] = atof( token );
			}

			// skip )
			token = COM_ParseExt2( &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_ParseExt2( &buf_p, qfalse );
			v->firstWeight = atoi( token );

			token = COM_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &buf_p, qfalse );

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

		// parse numWeights <number>
		token = COM_ParseExt2( &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_ParseExt2( &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_ParseExt2( &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_ParseExt2( &buf_p, qfalse );

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

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

			// skip (
			token = COM_ParseExt2( &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_ParseExt2( &buf_p, qfalse );
				weight->offset[ k ] = atof( token );
			}

			// skip )
			token = COM_ParseExt2( &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_ParseExt2( &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 normals
		{
			const float *v0, *v1, *v2;
			const float *t0, *t1, *t2;
			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;

				R_CalcNormalForTriangle( normal, v0, v1, v2 );

				for ( k = 0; k < 3; k++ )
				{
					float *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->normal );
			}
		}

#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
	}

	return qtrue;
}
Beispiel #13
0
/*
=================
R_CreateSurfaceGridMesh
=================
*/
static srfGridMesh_t *R_CreateSurfaceGridMesh( int width, int height,
    srfVert_t ctrl[ MAX_GRID_SIZE ][ MAX_GRID_SIZE ],
    float errorTable[ 2 ][ MAX_GRID_SIZE ],
    int numTriangles, srfTriangle_t triangles[ SHADER_MAX_TRIANGLES ] )
{
	int           i, j, size;
	srfVert_t     *vert;
	vec3_t        tmpVec;
	srfGridMesh_t *grid;

	// copy the results out to a grid
	size = sizeof( *grid );

	if ( r_stitchCurves->integer )
	{
		grid = (srfGridMesh_t*)/*ri.Hunk_Alloc */ Com_Allocate( size );
		Com_Memset( grid, 0, size );

		grid->widthLodError = (float*)/*ri.Hunk_Alloc */ Com_Allocate( width * 4 );
		Com_Memcpy( grid->widthLodError, errorTable[ 0 ], width * 4 );

		grid->heightLodError = (float*)/*ri.Hunk_Alloc */ Com_Allocate( height * 4 );
		Com_Memcpy( grid->heightLodError, errorTable[ 1 ], height * 4 );

		grid->numTriangles = numTriangles;
		grid->triangles = (srfTriangle_t*) Com_Allocate( grid->numTriangles * sizeof( srfTriangle_t ) );
		Com_Memcpy( grid->triangles, triangles, numTriangles * sizeof( srfTriangle_t ) );

		grid->numVerts = ( width * height );
		grid->verts = (srfVert_t*) Com_Allocate( grid->numVerts * sizeof( srfVert_t ) );
	}
	else
	{
		grid = (srfGridMesh_t*) ri.Hunk_Alloc( size, ha_pref::h_low );
		Com_Memset( grid, 0, size );

		grid->widthLodError = (float*) ri.Hunk_Alloc( width * 4, ha_pref::h_low );
		Com_Memcpy( grid->widthLodError, errorTable[ 0 ], width * 4 );

		grid->heightLodError = (float*) ri.Hunk_Alloc( height * 4, ha_pref::h_low );
		Com_Memcpy( grid->heightLodError, errorTable[ 1 ], height * 4 );

		grid->numTriangles = numTriangles;
		grid->triangles = (srfTriangle_t*) ri.Hunk_Alloc( grid->numTriangles * sizeof( srfTriangle_t ), ha_pref::h_low );
		Com_Memcpy( grid->triangles, triangles, numTriangles * sizeof( srfTriangle_t ) );

		grid->numVerts = ( width * height );
		grid->verts = (srfVert_t*) ri.Hunk_Alloc( grid->numVerts * sizeof( srfVert_t ), ha_pref::h_low );
	}

	grid->width = width;
	grid->height = height;
	grid->surfaceType = surfaceType_t::SF_GRID;
	ClearBounds( grid->bounds[ 0 ], grid->bounds[ 1 ] );

	for ( i = 0; i < width; i++ )
	{
		for ( j = 0; j < height; j++ )
		{
			vert = &grid->verts[ j * width + i ];
			*vert = ctrl[ j ][ i ];
			AddPointToBounds( vert->xyz, grid->bounds[ 0 ], grid->bounds[ 1 ] );
		}
	}

	// compute local origin and bounds
	VectorAdd( grid->bounds[ 0 ], grid->bounds[ 1 ], grid->origin );
	VectorScale( grid->origin, 0.5f, grid->origin );
	VectorSubtract( grid->bounds[ 0 ], grid->origin, tmpVec );
	grid->radius = VectorLength( tmpVec );

	VectorCopy( grid->origin, grid->lodOrigin );
	grid->lodRadius = grid->radius;
	//
	return grid;
}
Beispiel #14
0
/*
* R_UpdatePortalSurface
*/
void R_UpdatePortalSurface( portalSurface_t *portalSurface, const mesh_t *mesh,
	const vec3_t mins, const vec3_t maxs, const shader_t *shader ) {
	unsigned int i;
	float dist;
	cplane_t plane, untransformed_plane;
	vec3_t v[3];
	const entity_t *ent;

	if( !mesh || !portalSurface ) {
		return;
	}

	ent = portalSurface->entity;

	for( i = 0; i < 3; i++ ) {
		VectorCopy( mesh->xyzArray[mesh->elems[i]], v[i] );
	}

	PlaneFromPoints( v, &untransformed_plane );
	untransformed_plane.dist += DotProduct( ent->origin, untransformed_plane.normal );
	untransformed_plane.dist += 1; // nudge along the normal a bit
	CategorizePlane( &untransformed_plane );

	if( shader->flags & SHADER_AUTOSPRITE ) {
		vec3_t centre;

		// autosprites are quads, facing the viewer
		if( mesh->numVerts < 4 ) {
			return;
		}

		// compute centre as average of 4 vertices
		VectorCopy( mesh->xyzArray[mesh->elems[3]], centre );
		for( i = 0; i < 3; i++ )
			VectorAdd( centre, v[i], centre );
		VectorMA( ent->origin, 0.25, centre, centre );

		VectorNegate( &rn.viewAxis[AXIS_FORWARD], plane.normal );
		plane.dist = DotProduct( plane.normal, centre );
		CategorizePlane( &plane );
	} else {
		vec3_t temp;
		mat3_t entity_rotation;

		// regular surfaces
		if( !Matrix3_Compare( ent->axis, axis_identity ) ) {
			Matrix3_Transpose( ent->axis, entity_rotation );

			for( i = 0; i < 3; i++ ) {
				VectorCopy( v[i], temp );
				Matrix3_TransformVector( entity_rotation, temp, v[i] );
				VectorMA( ent->origin, ent->scale, v[i], v[i] );
			}

			PlaneFromPoints( v, &plane );
			CategorizePlane( &plane );
		} else {
			plane = untransformed_plane;
		}
	}

	if( ( dist = PlaneDiff( rn.viewOrigin, &plane ) ) <= BACKFACE_EPSILON ) {
		// behind the portal plane
		if( !( shader->flags & SHADER_PORTAL_CAPTURE2 ) ) {
			return;
		}

		// we need to render the backplane view
	}

	// check if portal view is opaque due to alphagen portal
	if( shader->portalDistance && dist > shader->portalDistance ) {
		return;
	}

	portalSurface->plane = plane;
	portalSurface->untransformed_plane = untransformed_plane;

	AddPointToBounds( mins, portalSurface->mins, portalSurface->maxs );
	AddPointToBounds( maxs, portalSurface->mins, portalSurface->maxs );
	VectorAdd( portalSurface->mins, portalSurface->maxs, portalSurface->centre );
	VectorScale( portalSurface->centre, 0.5, portalSurface->centre );
}
Beispiel #15
0
/*
==============
RE_BuildSkeleton
==============
*/
int RE_BuildSkeleton(refSkeleton_t *skel, qhandle_t hAnim, int startFrame, int endFrame, float frac, qboolean clearOrigin)
{
	skelAnimation_t *skelAnim;

	skelAnim = R_GetAnimationByHandle(hAnim);

	if (skelAnim->type == AT_MD5 && skelAnim->md5)
	{
		int            i;
		md5Animation_t *anim = skelAnim->md5;
		md5Channel_t   *channel;
		md5Frame_t     *newFrame, *oldFrame;
		vec3_t         newOrigin, oldOrigin, lerpedOrigin;
		quat_t         newQuat, oldQuat, lerpedQuat;
		int            componentsApplied;

		// Validate the frames so there is no chance of a crash.
		// This will write directly into the entity structure, so
		// when the surfaces are rendered, they don't need to be
		// range checked again.
		//if((startFrame >= anim->numFrames) || (startFrame < 0) || (endFrame >= anim->numFrames) || (endFrame < 0))
		//{
		//  Ren_Developer( "RE_BuildSkeleton: no such frame %d to %d for '%s'\n", startFrame, endFrame, anim->name);
		//  //startFrame = 0;
		//  //endFrame = 0;
		//}

		Q_clamp(startFrame, 0, anim->numFrames - 1);
		Q_clamp(endFrame, 0, anim->numFrames - 1);

		// compute frame pointers
		oldFrame = &anim->frames[startFrame];
		newFrame = &anim->frames[endFrame];

		// calculate a bounding box in the current coordinate system
		for (i = 0; i < 3; i++)
		{
			skel->bounds[0][i] =
			    oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i];
			skel->bounds[1][i] =
			    oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i];
		}

		for (i = 0, channel = anim->channels; i < anim->numChannels; i++, channel++)
		{
			// set baseframe values
			VectorCopy(channel->baseOrigin, newOrigin);
			VectorCopy(channel->baseOrigin, oldOrigin);

			quat_copy(channel->baseQuat, newQuat);
			quat_copy(channel->baseQuat, oldQuat);

			componentsApplied = 0;

			// update tranlation bits
			if (channel->componentsBits & COMPONENT_BIT_TX)
			{
				oldOrigin[0] = oldFrame->components[channel->componentsOffset + componentsApplied];
				newOrigin[0] = newFrame->components[channel->componentsOffset + componentsApplied];
				componentsApplied++;
			}

			if (channel->componentsBits & COMPONENT_BIT_TY)
			{
				oldOrigin[1] = oldFrame->components[channel->componentsOffset + componentsApplied];
				newOrigin[1] = newFrame->components[channel->componentsOffset + componentsApplied];
				componentsApplied++;
			}

			if (channel->componentsBits & COMPONENT_BIT_TZ)
			{
				oldOrigin[2] = oldFrame->components[channel->componentsOffset + componentsApplied];
				newOrigin[2] = newFrame->components[channel->componentsOffset + componentsApplied];
				componentsApplied++;
			}

			// update quaternion rotation bits
			if (channel->componentsBits & COMPONENT_BIT_QX)
			{
				((vec_t *) oldQuat)[0] = oldFrame->components[channel->componentsOffset + componentsApplied];
				((vec_t *) newQuat)[0] = newFrame->components[channel->componentsOffset + componentsApplied];
				componentsApplied++;
			}

			if (channel->componentsBits & COMPONENT_BIT_QY)
			{
				((vec_t *) oldQuat)[1] = oldFrame->components[channel->componentsOffset + componentsApplied];
				((vec_t *) newQuat)[1] = newFrame->components[channel->componentsOffset + componentsApplied];
				componentsApplied++;
			}

			if (channel->componentsBits & COMPONENT_BIT_QZ)
			{
				((vec_t *) oldQuat)[2] = oldFrame->components[channel->componentsOffset + componentsApplied];
				((vec_t *) newQuat)[2] = newFrame->components[channel->componentsOffset + componentsApplied];
			}

			QuatCalcW(oldQuat);
			quat_norm(oldQuat);

			QuatCalcW(newQuat);
			quat_norm(newQuat);

#if 1
			VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin);
			quat_slerp(oldQuat, newQuat, frac, lerpedQuat);
#else
			VectorCopy(newOrigin, lerpedOrigin);
			quat_copy(newQuat, lerpedQuat);
#endif

			// copy lerped information to the bone + extra data
			skel->bones[i].parentIndex = channel->parentIndex;

			if (channel->parentIndex < 0 && clearOrigin)
			{
				VectorClear(skel->bones[i].origin);
				QuatClear(skel->bones[i].rotation);

				// move bounding box back
				VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]);
				VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]);
			}
			else
			{
				VectorCopy(lerpedOrigin, skel->bones[i].origin);
			}

			quat_copy(lerpedQuat, skel->bones[i].rotation);

#if defined(REFBONE_NAMES)
			Q_strncpyz(skel->bones[i].name, channel->name, sizeof(skel->bones[i].name));
#endif
		}

		skel->numBones = anim->numChannels;
		skel->type     = SK_RELATIVE;
		return qtrue;
	}
	else if (skelAnim->type == AT_PSA && skelAnim->psa)
	{
		int               i;
		psaAnimation_t    *anim = skelAnim->psa;
		axAnimationKey_t  *newKey, *oldKey;
		axReferenceBone_t *refBone;
		vec3_t            newOrigin, oldOrigin, lerpedOrigin;
		quat_t            newQuat, oldQuat, lerpedQuat;
		refSkeleton_t     skeleton;

		Q_clamp(startFrame, 0, anim->info.numRawFrames - 1);
		Q_clamp(endFrame, 0, anim->info.numRawFrames - 1);

		ClearBounds(skel->bounds[0], skel->bounds[1]);

		skel->numBones = anim->info.numBones;
		for (i = 0, refBone = anim->bones; i < anim->info.numBones; i++, refBone++)
		{
			oldKey = &anim->keys[startFrame * anim->info.numBones + i];
			newKey = &anim->keys[endFrame * anim->info.numBones + i];

			VectorCopy(newKey->position, newOrigin);
			VectorCopy(oldKey->position, oldOrigin);

			quat_copy(newKey->quat, newQuat);
			quat_copy(oldKey->quat, oldQuat);

			//QuatCalcW(oldQuat);
			//QuatNormalize(oldQuat);

			//QuatCalcW(newQuat);
			//QuatNormalize(newQuat);

			VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin);
			quat_slerp(oldQuat, newQuat, frac, lerpedQuat);

			// copy lerped information to the bone + extra data
			skel->bones[i].parentIndex = refBone->parentIndex;

			if (refBone->parentIndex < 0 && clearOrigin)
			{
				VectorClear(skel->bones[i].origin);
				QuatClear(skel->bones[i].rotation);

				// move bounding box back
				VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]);
				VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]);
			}
			else
			{
				VectorCopy(lerpedOrigin, skel->bones[i].origin);
			}

			quat_copy(lerpedQuat, skel->bones[i].rotation);

#if defined(REFBONE_NAMES)
			Q_strncpyz(skel->bones[i].name, refBone->name, sizeof(skel->bones[i].name));
#endif

			// calculate absolute values for the bounding box approximation
			VectorCopy(skel->bones[i].origin, skeleton.bones[i].origin);
			quat_copy(skel->bones[i].rotation, skeleton.bones[i].rotation);

			if (refBone->parentIndex >= 0)
			{
				vec3_t    rotated;
				quat_t    quat;
				refBone_t *bone   = &skeleton.bones[i];
				refBone_t *parent = &skeleton.bones[refBone->parentIndex];

				QuatTransformVector(parent->rotation, bone->origin, rotated);

				VectorAdd(parent->origin, rotated, bone->origin);

				QuatMultiply1(parent->rotation, bone->rotation, quat);
				quat_copy(quat, bone->rotation);

				AddPointToBounds(bone->origin, skel->bounds[0], skel->bounds[1]);
			}
		}

		skel->numBones = anim->info.numBones;
		skel->type     = SK_RELATIVE;
		return qtrue;
	}

	//Ren_Warning( "RE_BuildSkeleton: bad animation '%s' with handle %i\n", anim->name, hAnim);

	// FIXME: clear existing bones and bounds?
	return qfalse;
}
Beispiel #16
0
static void RadSample( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si, radWinding_t *rw, vec3_t average, vec3_t gradient, int *style ){
	int i, j, k, l, v, x, y, samples;
	vec3_t color, mins, maxs;
	vec4_t textureColor;
	float alpha, alphaI, bf;
	vec3_t blend;
	float st[ 2 ], lightmap[ 2 ], *radLuxel;
	radVert_t   *rv[ 3 ];


	/* initial setup */
	ClearBounds( mins, maxs );
	VectorClear( average );
	VectorClear( gradient );
	alpha = 0;

	/* dummy check */
	if ( rw == NULL || rw->numVerts < 3 ) {
		return;
	}

	/* start sampling */
	samples = 0;

	/* sample vertex colors if no lightmap or this is the initial pass */
	if ( lm == NULL || lm->radLuxels[ lightmapNum ] == NULL || bouncing == qfalse ) {
		for ( samples = 0; samples < rw->numVerts; samples++ )
		{
			/* multiply by texture color */
			if ( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, rw->verts[ samples ].st, textureColor ) ) {
				VectorCopy( si->averageColor, textureColor );
				textureColor[ 4 ] = 255.0f;
			}
			for ( i = 0; i < 3; i++ )
				color[ i ] = ( textureColor[ i ] / 255 ) * ( rw->verts[ samples ].color[ lightmapNum ][ i ] / 255.0f );

			AddPointToBounds( color, mins, maxs );
			VectorAdd( average, color, average );

			/* get alpha */
			alpha += ( textureColor[ 3 ] / 255.0f ) * ( rw->verts[ samples ].color[ lightmapNum ][ 3 ] / 255.0f );
		}

		/* set style */
		*style = ds->vertexStyles[ lightmapNum ];
	}

	/* sample lightmap */
	else
	{
		/* fracture the winding into a fan (including degenerate tris) */
		for ( v = 1; v < ( rw->numVerts - 1 ) && samples < MAX_SAMPLES; v++ )
		{
			/* get a triangle */
			rv[ 0 ] = &rw->verts[ 0 ];
			rv[ 1 ] = &rw->verts[ v ];
			rv[ 2 ] = &rw->verts[ v + 1 ];

			/* this code is embarassing (really should just rasterize the triangle) */
			for ( i = 1; i < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; i++ )
			{
				for ( j = 1; j < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; j++ )
				{
					for ( k = 1; k < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; k++ )
					{
						/* create a blend vector (barycentric coordinates) */
						blend[ 0 ] = i;
						blend[ 1 ] = j;
						blend[ 2 ] = k;
						bf = ( 1.0 / ( blend[ 0 ] + blend[ 1 ] + blend[ 2 ] ) );
						VectorScale( blend, bf, blend );

						/* create a blended sample */
						st[ 0 ] = st[ 1 ] = 0.0f;
						lightmap[ 0 ] = lightmap[ 1 ] = 0.0f;
						alphaI = 0.0f;
						for ( l = 0; l < 3; l++ )
						{
							st[ 0 ] += ( rv[ l ]->st[ 0 ] * blend[ l ] );
							st[ 1 ] += ( rv[ l ]->st[ 1 ] * blend[ l ] );
							lightmap[ 0 ] += ( rv[ l ]->lightmap[ lightmapNum ][ 0 ] * blend[ l ] );
							lightmap[ 1 ] += ( rv[ l ]->lightmap[ lightmapNum ][ 1 ] * blend[ l ] );
							alphaI += ( rv[ l ]->color[ lightmapNum ][ 3 ] * blend[ l ] );
						}

						/* get lightmap xy coords */
						x = lightmap[ 0 ] / (float) superSample;
						y = lightmap[ 1 ] / (float) superSample;
						if ( x < 0 ) {
							x = 0;
						}
						else if ( x >= lm->w ) {
							x = lm->w - 1;
						}
						if ( y < 0 ) {
							y = 0;
						}
						else if ( y >= lm->h ) {
							y = lm->h - 1;
						}

						/* get radiosity luxel */
						radLuxel = RAD_LUXEL( lightmapNum, x, y );

						/* ignore unlit/unused luxels */
						if ( radLuxel[ 0 ] < 0.0f ) {
							continue;
						}

						/* inc samples */
						samples++;

						/* multiply by texture color */
						if ( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, st, textureColor ) ) {
							VectorCopy( si->averageColor, textureColor );
							textureColor[ 4 ] = 255;
						}
						for ( i = 0; i < 3; i++ )
							color[ i ] = ( textureColor[ i ] / 255 ) * ( radLuxel[ i ] / 255 );

						AddPointToBounds( color, mins, maxs );
						VectorAdd( average, color, average );

						/* get alpha */
						alpha += ( textureColor[ 3 ] / 255 ) * ( alphaI / 255 );
					}
				}
			}
		}

		/* set style */
		*style = ds->lightmapStyles[ lightmapNum ];
	}

	/* any samples? */
	if ( samples <= 0 ) {
		return;
	}

	/* average the color */
	VectorScale( average, ( 1.0 / samples ), average );

	/* create the color gradient */
	//%	VectorSubtract( maxs, mins, delta );

	/* new: color gradient will always be 0-1.0, expressed as the range of light relative to overall light */
	//%	gradient[ 0 ] = maxs[ 0 ] > 0.0f ? (maxs[ 0 ] - mins[ 0 ]) / maxs[ 0 ] : 0.0f;
	//%	gradient[ 1 ] = maxs[ 1 ] > 0.0f ? (maxs[ 1 ] - mins[ 1 ]) / maxs[ 1 ] : 0.0f;
	//%	gradient[ 2 ] = maxs[ 2 ] > 0.0f ? (maxs[ 2 ] - mins[ 2 ]) / maxs[ 2 ] : 0.0f;

	/* newer: another contrast function */
	for ( i = 0; i < 3; i++ )
		gradient[ i ] = ( maxs[ i ] - mins[ i ] ) * maxs[ i ];
}
Beispiel #17
0
/*
=================
R_LoadPSK
=================
*/
qboolean R_LoadPSK(model_t * mod, byte *buffer, int bufferSize, const char *modName)
{
	int             i, j, k;
	memStream_t    *stream;

	axChunkHeader_t	chunkHeader;

	int				numPoints;
	axPoint_t      *point;
	axPoint_t      *points;

	int				numVertexes;
	axVertex_t     *vertex;
	axVertex_t     *vertexes;

	//int				numSmoothGroups;
	int				numTriangles;
	axTriangle_t   *triangle;
	axTriangle_t   *triangles;

	int				numMaterials;
	axMaterial_t   *material;
	axMaterial_t   *materials;

	int				numReferenceBones;
	axReferenceBone_t *refBone;
	axReferenceBone_t *refBones;

	int				numWeights;
	axBoneWeight_t *axWeight;
	axBoneWeight_t *axWeights;

	md5Model_t     *md5;
	md5Bone_t      *md5Bone;
	md5Weight_t    *weight;

	vec3_t          boneOrigin;
	quat_t          boneQuat;
	//matrix_t        boneMat;

	int				materialIndex, oldMaterialIndex;

	int				numRemaining;

	growList_t		sortedTriangles;
	growList_t      vboVertexes;
	growList_t      vboTriangles;
	growList_t      vboSurfaces;

	int				numBoneReferences;
	int				boneReferences[MAX_BONES];

	matrix_t		unrealToQuake;

	//MatrixSetupScale(unrealToQuake, 1, -1, 1);
	MatrixFromAngles(unrealToQuake, 0, 90, 0);

	stream = AllocMemStream(buffer, bufferSize);
	GetChunkHeader(stream, &chunkHeader);

	// check indent again
	if(Q_stricmpn(chunkHeader.ident, "ACTRHEAD", 8))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "ACTRHEAD");
		FreeMemStream(stream);
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

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

	// read points
	GetChunkHeader(stream, &chunkHeader);
	if(Q_stricmpn(chunkHeader.ident, "PNTS0000", 8))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "PNTS0000");
		FreeMemStream(stream);
		return qfalse;
	}

	if(chunkHeader.dataSize != sizeof(axPoint_t))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axPoint_t));
		FreeMemStream(stream);
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

	numPoints = chunkHeader.numData;
	points = (axPoint_t*)Com_Allocate(numPoints * sizeof(axPoint_t));
	for(i = 0, point = points; i < numPoints; i++, point++)
	{
		point->point[0] = MemStreamGetFloat(stream);
		point->point[1] = MemStreamGetFloat(stream);
		point->point[2] = MemStreamGetFloat(stream);

#if 0
		// Tr3B: HACK convert from Unreal coordinate system to the Quake one
		MatrixTransformPoint2(unrealToQuake, point->point);
#endif
	}

	// read vertices
	GetChunkHeader(stream, &chunkHeader);
	if(Q_stricmpn(chunkHeader.ident, "VTXW0000", 8))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "VTXW0000");
		FreeMemStream(stream);
		Com_Dealloc(points);
		return qfalse;
	}

	if(chunkHeader.dataSize != sizeof(axVertex_t))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axVertex_t));
		FreeMemStream(stream);
		Com_Dealloc(points);
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

	numVertexes = chunkHeader.numData;
	vertexes = (axVertex_t*)Com_Allocate(numVertexes * sizeof(axVertex_t));
	for(i = 0, vertex = vertexes; i < numVertexes; i++, vertex++)
	{
		vertex->pointIndex = MemStreamGetShort(stream);
		if(vertex->pointIndex < 0 || vertex->pointIndex >= numPoints)
		{
			ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has vertex with point index out of range (%i while max %i)\n", modName, vertex->pointIndex, numPoints);
			FreeMemStream(stream);
			Com_Dealloc(points);
			Com_Dealloc(vertexes);
			return qfalse;
		}

		vertex->unknownA = MemStreamGetShort(stream);
		vertex->st[0] = MemStreamGetFloat(stream);
		vertex->st[1] = MemStreamGetFloat(stream);
		vertex->materialIndex = MemStreamGetC(stream);
		vertex->reserved = MemStreamGetC(stream);
		vertex->unknownB = MemStreamGetShort(stream);

#if 0
		ri.Printf(PRINT_ALL, "R_LoadPSK: axVertex_t(%i):\n"
				"axVertex:pointIndex: %i\n"
				"axVertex:unknownA: %i\n"
				"axVertex::st: %f %f\n"
				"axVertex:materialIndex: %i\n"
				"axVertex:reserved: %d\n"
				"axVertex:unknownB: %d\n",
				i,
				vertex->pointIndex,
				vertex->unknownA,
				vertex->st[0], vertex->st[1],
				vertex->materialIndex,
				vertex->reserved,
				vertex->unknownB);
#endif
	}

	// read triangles
	GetChunkHeader(stream, &chunkHeader);
	if(Q_stricmpn(chunkHeader.ident, "FACE0000", 8))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "FACE0000");
		FreeMemStream(stream);
		Com_Dealloc(points);
		Com_Dealloc(vertexes);
		return qfalse;
	}

	if(chunkHeader.dataSize != sizeof(axTriangle_t))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axTriangle_t));
		FreeMemStream(stream);
		Com_Dealloc(points);
		Com_Dealloc(vertexes);
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

	numTriangles = chunkHeader.numData;
	triangles = (axTriangle_t*)Com_Allocate(numTriangles * sizeof(axTriangle_t));
	for(i = 0, triangle = triangles; i < numTriangles; i++, triangle++)
	{
		for(j = 0; j < 3; j++)
		//for(j = 2; j >= 0; j--)
		{
			triangle->indexes[j] = MemStreamGetShort(stream);
			if(triangle->indexes[j] < 0 || triangle->indexes[j] >= numVertexes)
			{
				ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has triangle with vertex index out of range (%i while max %i)\n", modName, triangle->indexes[j], numVertexes);
				FreeMemStream(stream);
				Com_Dealloc(points);
				Com_Dealloc(vertexes);
				Com_Dealloc(triangles);
				return qfalse;
			}
		}

		triangle->materialIndex = MemStreamGetC(stream);
		triangle->materialIndex2 = MemStreamGetC(stream);
		triangle->smoothingGroups = MemStreamGetLong(stream);
	}

	// read materials
	GetChunkHeader(stream, &chunkHeader);
	if(Q_stricmpn(chunkHeader.ident, "MATT0000", 8))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "MATT0000");
		FreeMemStream(stream);
		Com_Dealloc(points);
		Com_Dealloc(vertexes);
		Com_Dealloc(triangles);
		return qfalse;
	}

	if(chunkHeader.dataSize != sizeof(axMaterial_t))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axMaterial_t));
		FreeMemStream(stream);
		Com_Dealloc(points);
		Com_Dealloc(vertexes);
		Com_Dealloc(triangles);
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

	numMaterials = chunkHeader.numData;
	materials = (axMaterial_t*)Com_Allocate(numMaterials * sizeof(axMaterial_t));
	for(i = 0, material = materials; i < numMaterials; i++, material++)
	{
		MemStreamRead(stream, material->name, sizeof(material->name));

		ri.Printf(PRINT_ALL, "R_LoadPSK: material name: '%s'\n", material->name);

		material->shaderIndex = MemStreamGetLong(stream);
		material->polyFlags = MemStreamGetLong(stream);
		material->auxMaterial = MemStreamGetLong(stream);
		material->auxFlags = MemStreamGetLong(stream);
		material->lodBias = MemStreamGetLong(stream);
		material->lodStyle = MemStreamGetLong(stream);
	}

	for(i = 0, vertex = vertexes; i < numVertexes; i++, vertex++)
	{
		if(vertex->materialIndex < 0 || vertex->materialIndex >= numMaterials)
		{
			ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has vertex with material index out of range (%i while max %i)\n", modName, vertex->materialIndex, numMaterials);
			FreeMemStream(stream);
			Com_Dealloc(points);
			Com_Dealloc(vertexes);
			Com_Dealloc(triangles);
			Com_Dealloc(materials);
			return qfalse;
		}
	}

	for(i = 0, triangle = triangles; i < numTriangles; i++, triangle++)
	{
		if(triangle->materialIndex < 0 || triangle->materialIndex >= numMaterials)
		{
			ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has triangle with material index out of range (%i while max %i)\n", modName, triangle->materialIndex, numMaterials);
			FreeMemStream(stream);
			Com_Dealloc(points);
			Com_Dealloc(vertexes);
			Com_Dealloc(triangles);
			Com_Dealloc(materials);
			return qfalse;
		}
	}

	// read reference bones
	GetChunkHeader(stream, &chunkHeader);
	if(Q_stricmpn(chunkHeader.ident, "REFSKELT", 8))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "REFSKELT");
		FreeMemStream(stream);
		Com_Dealloc(points);
		Com_Dealloc(vertexes);
		Com_Dealloc(triangles);
		Com_Dealloc(materials);
		return qfalse;
	}

	if(chunkHeader.dataSize != sizeof(axReferenceBone_t))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axReferenceBone_t));
		FreeMemStream(stream);
		Com_Dealloc(points);
		Com_Dealloc(vertexes);
		Com_Dealloc(triangles);
		Com_Dealloc(materials);
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

	numReferenceBones = chunkHeader.numData;
	refBones = (axReferenceBone_t*)Com_Allocate(numReferenceBones * sizeof(axReferenceBone_t));
	for(i = 0, refBone = refBones; i < numReferenceBones; i++, refBone++)
	{
		MemStreamRead(stream, refBone->name, sizeof(refBone->name));

		//ri.Printf(PRINT_ALL, "R_LoadPSK: reference bone name: '%s'\n", refBone->name);

		refBone->flags = MemStreamGetLong(stream);
		refBone->numChildren = MemStreamGetLong(stream);
		refBone->parentIndex = MemStreamGetLong(stream);

		GetBone(stream, &refBone->bone);

#if 0
		ri.Printf(PRINT_ALL, "R_LoadPSK: axReferenceBone_t(%i):\n"
				"axReferenceBone_t::name: '%s'\n"
				"axReferenceBone_t::flags: %i\n"
				"axReferenceBone_t::numChildren %i\n"
				"axReferenceBone_t::parentIndex: %i\n"
				"axReferenceBone_t::quat: %f %f %f %f\n"
				"axReferenceBone_t::position: %f %f %f\n"
				"axReferenceBone_t::length: %f\n"
				"axReferenceBone_t::xSize: %f\n"
				"axReferenceBone_t::ySize: %f\n"
				"axReferenceBone_t::zSize: %f\n",
				i,
				refBone->name,
				refBone->flags,
				refBone->numChildren,
				refBone->parentIndex,
				refBone->bone.quat[0], refBone->bone.quat[1], refBone->bone.quat[2], refBone->bone.quat[3],
				refBone->bone.position[0], refBone->bone.position[1], refBone->bone.position[2],
				refBone->bone.length,
				refBone->bone.xSize,
				refBone->bone.ySize,
				refBone->bone.zSize);
#endif
	}

	// read  bone weights
	GetChunkHeader(stream, &chunkHeader);
	if(Q_stricmpn(chunkHeader.ident, "RAWWEIGHTS", 10))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "RAWWEIGHTS");
		FreeMemStream(stream);
		Com_Dealloc(points);
		Com_Dealloc(vertexes);
		Com_Dealloc(triangles);
		Com_Dealloc(materials);
		Com_Dealloc(refBones);
		return qfalse;
	}

	if(chunkHeader.dataSize != sizeof(axBoneWeight_t))
	{
		ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axBoneWeight_t));
		FreeMemStream(stream);
		Com_Dealloc(points);
		Com_Dealloc(vertexes);
		Com_Dealloc(triangles);
		Com_Dealloc(materials);
		Com_Dealloc(refBones);
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

	numWeights = chunkHeader.numData;
	axWeights = (axBoneWeight_t*)Com_Allocate(numWeights * sizeof(axBoneWeight_t));
	for(i = 0, axWeight = axWeights; i < numWeights; i++, axWeight++)
	{
		axWeight->weight = MemStreamGetFloat(stream);
		axWeight->pointIndex = MemStreamGetLong(stream);
		axWeight->boneIndex = MemStreamGetLong(stream);

#if 0
		ri.Printf(PRINT_ALL, "R_LoadPSK: axBoneWeight_t(%i):\n"
				"axBoneWeight_t::weight: %f\n"
				"axBoneWeight_t::pointIndex %i\n"
				"axBoneWeight_t::boneIndex: %i\n",
				i,
				axWeight->weight,
				axWeight->pointIndex,
				axWeight->boneIndex);
#endif
	}


	//
	// convert the model to an internal MD5 representation
	//
	md5->numBones = numReferenceBones;

	// calc numMeshes <number>
	/*
	numSmoothGroups = 0;
	for(i = 0, triangle = triangles; i < numTriangles; i++, triangle++)
	{
		if(triangle->smoothingGroups)
		{

		}
	}
	*/

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

	// copy all reference bones
	md5->bones = (md5Bone_t*)ri.Hunk_Alloc(sizeof(*md5Bone) * md5->numBones, h_low);
	for(i = 0, md5Bone = md5->bones, refBone = refBones; i < md5->numBones; i++, md5Bone++, refBone++)
	{
		Q_strncpyz(md5Bone->name, refBone->name, sizeof(md5Bone->name));

		if(i == 0)
		{
			md5Bone->parentIndex = refBone->parentIndex -1;
		}
		else
		{
			md5Bone->parentIndex = refBone->parentIndex;
		}

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

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

		for(j = 0; j < 3; j++)
		{
			boneOrigin[j] = refBone->bone.position[j];
		}

		// Tr3B: I have really no idea why the .psk format stores the first quaternion with inverted quats.
		// Furthermore only the X and Z components of the first quat are inverted ?!?!
		if(i == 0)
		{
			boneQuat[0] = refBone->bone.quat[0];
			boneQuat[1] = -refBone->bone.quat[1];
			boneQuat[2] = refBone->bone.quat[2];
			boneQuat[3] = refBone->bone.quat[3];
		}
		else
		{
			boneQuat[0] = -refBone->bone.quat[0];
			boneQuat[1] = -refBone->bone.quat[1];
			boneQuat[2] = -refBone->bone.quat[2];
			boneQuat[3] = refBone->bone.quat[3];
		}

		VectorCopy(boneOrigin, md5Bone->origin);
		//MatrixTransformPoint(unrealToQuake, boneOrigin, md5Bone->origin);

		QuatCopy(boneQuat, md5Bone->rotation);

		//QuatClear(md5Bone->rotation);


#if 0
		ri.Printf(PRINT_ALL, "R_LoadPSK: md5Bone_t(%i):\n"
						"md5Bone_t::name: '%s'\n"
						"md5Bone_t::parentIndex: %i\n"
						"md5Bone_t::quat: %f %f %f %f\n"
						"md5bone_t::position: %f %f %f\n",
						i,
						md5Bone->name,
						md5Bone->parentIndex,
						md5Bone->rotation[0], md5Bone->rotation[1], md5Bone->rotation[2], md5Bone->rotation[3],
						md5Bone->origin[0], md5Bone->origin[1], md5Bone->origin[2]);
#endif

		if(md5Bone->parentIndex >= 0)
		{
			vec3_t          rotated;
			quat_t          quat;

			md5Bone_t      *parent;

			parent = &md5->bones[md5Bone->parentIndex];

			QuatTransformVector(parent->rotation, md5Bone->origin, rotated);
			//QuatTransformVector(md5Bone->rotation, md5Bone->origin, rotated);

			VectorAdd(parent->origin, rotated, md5Bone->origin);

			QuatMultiply1(parent->rotation, md5Bone->rotation, quat);
			QuatCopy(quat, md5Bone->rotation);
		}

		MatrixSetupTransformFromQuat(md5Bone->inverseTransform, md5Bone->rotation, md5Bone->origin);
		MatrixInverse(md5Bone->inverseTransform);

#if 0
		ri.Printf(PRINT_ALL, "R_LoadPSK: md5Bone_t(%i):\n"
						"md5Bone_t::name: '%s'\n"
						"md5Bone_t::parentIndex: %i\n"
						"md5Bone_t::quat: %f %f %f %f\n"
						"md5bone_t::position: %f %f %f\n",
						i,
						md5Bone->name,
						md5Bone->parentIndex,
						md5Bone->rotation[0], md5Bone->rotation[1], md5Bone->rotation[2], md5Bone->rotation[3],
						md5Bone->origin[0], md5Bone->origin[1], md5Bone->origin[2]);
#endif
	}

	Com_InitGrowList(&vboVertexes, 10000);
	for(i = 0, vertex = vertexes; i < numVertexes; i++, vertex++)
	{
		md5Vertex_t *vboVert = (md5Vertex_t*)Com_Allocate(sizeof(*vboVert));

		for(j = 0; j < 3; j++)
		{
			vboVert->position[j] = points[vertex->pointIndex].point[j];
		}

		vboVert->texCoords[0] = vertex->st[0];
		vboVert->texCoords[1] = vertex->st[1];

		// find number of associated weights
		vboVert->numWeights = 0;
		for(j = 0, axWeight = axWeights; j < numWeights; j++, axWeight++)
		{
			if(axWeight->pointIndex == vertex->pointIndex && axWeight->weight > 0.0f)
			{
				vboVert->numWeights++;
			}
		}

		if(vboVert->numWeights > MAX_WEIGHTS)
		{
			ri.Error(ERR_DROP, "R_LoadPSK: vertex %i requires more weights %i than the maximum of %i in model '%s'", i, vboVert->numWeights, MAX_WEIGHTS, modName);
			//ri.Printf(PRINT_WARNING, "R_LoadPSK: vertex %i requires more weights %i than the maximum of %i in model '%s'\n", i, vboVert->numWeights, MAX_WEIGHTS, modName);
		}

		vboVert->weights = (md5Weight_t**)ri.Hunk_Alloc(sizeof(*vboVert->weights) * vboVert->numWeights, h_low);
		for(j = 0, axWeight = axWeights, k = 0; j < numWeights; j++, axWeight++)
		{
			if(axWeight->pointIndex == vertex->pointIndex && axWeight->weight > 0.0f)
			{
				weight = (md5Weight_t*)ri.Hunk_Alloc(sizeof(*weight), h_low);

				weight->boneIndex = axWeight->boneIndex;
				weight->boneWeight = axWeight->weight;

				// FIXME?
				weight->offset[0] = refBones[axWeight->boneIndex].bone.xSize;
				weight->offset[1] = refBones[axWeight->boneIndex].bone.ySize;
				weight->offset[2] = refBones[axWeight->boneIndex].bone.zSize;

				vboVert->weights[k++] = weight;
			}
		}

		Com_AddToGrowList(&vboVertexes, vboVert);
	}

	ClearBounds(md5->bounds[0], md5->bounds[1]);
	for(i = 0, vertex = vertexes; i < numVertexes; i++, vertex++)
	{
		AddPointToBounds(points[vertex->pointIndex].point, md5->bounds[0], md5->bounds[1]);
	}

#if 0
	ri.Printf(PRINT_ALL, "R_LoadPSK: AABB (%i %i %i) (%i %i %i)\n",
			(int)md5->bounds[0][0],
			(int)md5->bounds[0][1],
			(int)md5->bounds[0][2],
			(int)md5->bounds[1][0],
			(int)md5->bounds[1][1],
			(int)md5->bounds[1][2]);
#endif

	// sort triangles
	qsort(triangles, numTriangles, sizeof(axTriangle_t), CompareTrianglesByMaterialIndex);

	Com_InitGrowList(&sortedTriangles, 1000);
	for(i = 0, triangle = triangles; i < numTriangles; i++, triangle++)
	{
		skelTriangle_t *sortTri = (skelTriangle_t*)Com_Allocate(sizeof(*sortTri));

		for(j = 0; j < 3; j++)
		{
			sortTri->indexes[j] = triangle->indexes[j];
			sortTri->vertexes[j] = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, triangle->indexes[j]);
		}
		sortTri->referenced = qfalse;

		Com_AddToGrowList(&sortedTriangles, sortTri);
	}

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

		for(j = 0; j < vboVertexes.currentElements; j++)
		{
			v0 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, j);

			VectorClear(v0->tangent);
			VectorClear(v0->binormal);
			VectorClear(v0->normal);
		}

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

			v0 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, tri->indexes[0]);
			v1 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, tri->indexes[1]);
			v2 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, tri->indexes[2]);

			p0 = v0->position;
			p1 = v1->position;
			p2 = v2->position;

			t0 = v0->texCoords;
			t1 = v1->texCoords;
			t2 = v2->texCoords;

#if 1
			R_CalcTangentSpace(tangent, binormal, normal, p0, p1, p2, t0, t1, t2);
#else
			R_CalcNormalForTriangle(normal, p0, p1, p2);
			R_CalcTangentsForTriangle(tangent, binormal, p0, p1, p2, t0, t1, t2);
#endif

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

				v0 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, tri->indexes[k]);

				v = v0->tangent;
				VectorAdd(v, tangent, v);

				v = v0->binormal;
				VectorAdd(v, binormal, v);

				v = v0->normal;
				VectorAdd(v, normal, v);
			}
		}

		for(j = 0; j < vboVertexes.currentElements; j++)
		{
			v0 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, j);

			VectorNormalize(v0->tangent);
			VectorNormalize(v0->binormal);
			VectorNormalize(v0->normal);
		}
	}
#else
	{
		float           bb, s, t;
		vec3_t          bary;
		vec3_t			faceNormal;
		md5Vertex_t    *dv[3];

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

			dv[0] = Com_GrowListElement(&vboVertexes, tri->indexes[0]);
			dv[1] = Com_GrowListElement(&vboVertexes, tri->indexes[1]);
			dv[2] = Com_GrowListElement(&vboVertexes, 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; j < vboVertexes.currentElements; j++)
		{
			dv[0] = Com_GrowListElement(&vboVertexes, j);
			//VectorNormalize(dv[0]->tangent);
			//VectorNormalize(dv[0]->binormal);
			VectorNormalize(dv[0]->normal);
		}
#endif
	}
#endif

#if 0
	{
		md5Vertex_t    *v0, *v1;

		// do another extra smoothing for normals to avoid flat shading
		for(j = 0; j < vboVertexes.currentElements; j++)
		{
			v0 = Com_GrowListElement(&vboVertexes, j);

			for(k = 0; k < vboVertexes.currentElements; k++)
			{
				if(j == k)
					continue;

				v1 = Com_GrowListElement(&vboVertexes, k);

				if(VectorCompare(v0->position, v1->position))
				{
					VectorAdd(v0->position, v1->normal, v0->normal);
				}
			}

			VectorNormalize(v0->normal);
		}
	}
#endif

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

	materialIndex = oldMaterialIndex = -1;
	for(i = 0; i < numTriangles; i++)
	{
		triangle = &triangles[i];
		materialIndex = triangle->materialIndex;

		if(materialIndex != oldMaterialIndex)
		{
			oldMaterialIndex = materialIndex;

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

				Com_InitGrowList(&vboTriangles, 1000);

				for(j = i; j < sortedTriangles.currentElements; j++)
				{
					skelTriangle_t *sortTri;

					triangle = &triangles[j];
					materialIndex = triangle->materialIndex;

					if(materialIndex != oldMaterialIndex)
						continue;

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

					if(sortTri->referenced)
						continue;

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

				for(j = 0; j < MAX_BONES; j++)
				{
					if(boneReferences[j] > 0)
					{
						ri.Printf(PRINT_ALL, "R_LoadPSK: referenced bone: '%s'\n", (j < numReferenceBones) ? refBones[j].name : NULL);
					}
				}

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

				// FIXME skinIndex
				AddSurfaceToVBOSurfacesList2(&vboSurfaces, &vboTriangles, &vboVertexes, md5, vboSurfaces.currentElements, materials[oldMaterialIndex].name, numBoneReferences, boneReferences);
				numRemaining -= vboTriangles.currentElements;

				Com_DestroyGrowList(&vboTriangles);
			}
		}
	}

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

	for(j = 0; j < vboVertexes.currentElements; j++)
	{
		md5Vertex_t *v = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, j);
		Com_Dealloc(v);
	}
	Com_DestroyGrowList(&vboVertexes);

	// move VBO surfaces list to hunk
	md5->numVBOSurfaces = vboSurfaces.currentElements;
	md5->vboSurfaces = (srfVBOMD5Mesh_t**)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);

	FreeMemStream(stream);
	Com_Dealloc(points);
	Com_Dealloc(vertexes);
	Com_Dealloc(triangles);
	Com_Dealloc(materials);
	Com_Dealloc(refBones);
	Com_Dealloc(axWeights);

	ri.Printf(PRINT_ALL, "%i VBO surfaces created for PSK model '%s'\n", md5->numVBOSurfaces, modName);

	return qtrue;
}
Beispiel #18
0
static void RadSubdivideDiffuseLight( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si,
									  float scale, float subdivide, qboolean original, radWinding_t *rw, clipWork_t *cw ){
	int i, style;
	float dist, area, value;
	vec3_t mins, maxs, normal, d1, d2, cross, color, gradient;
	light_t         *light, *splash;
	winding_t       *w;


	/* dummy check */
	if ( rw == NULL || rw->numVerts < 3 ) {
		return;
	}

	/* get bounds for winding */
	ClearBounds( mins, maxs );
	for ( i = 0; i < rw->numVerts; i++ )
		AddPointToBounds( rw->verts[ i ].xyz, mins, maxs );

	/* subdivide if necessary */
	for ( i = 0; i < 3; i++ )
	{
		if ( maxs[ i ] - mins[ i ] > subdivide ) {
			radWinding_t front, back;


			/* make axial plane */
			VectorClear( normal );
			normal[ i ] = 1;
			dist = ( maxs[ i ] + mins[ i ] ) * 0.5f;

			/* clip the winding */
			RadClipWindingEpsilon( rw, normal, dist, RADIOSITY_CLIP_EPSILON, &front, &back, cw );

			/* recurse */
			RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &front, cw );
			RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &back, cw );
			return;
		}
	}

	/* check area */
	area = 0.0f;
	for ( i = 2; i < rw->numVerts; i++ )
	{
		VectorSubtract( rw->verts[ i - 1 ].xyz, rw->verts[ 0 ].xyz, d1 );
		VectorSubtract( rw->verts[ i ].xyz, rw->verts[ 0 ].xyz, d2 );
		CrossProduct( d1, d2, cross );
		area += 0.5f * VectorLength( cross );
	}
	if ( area < 1.0f || area > 20000000.0f ) {
		return;
	}

	/* more subdivision may be necessary */
	if ( bouncing ) {
		/* get color sample for the surface fragment */
		RadSample( lightmapNum, ds, lm, si, rw, color, gradient, &style );

		/* if color gradient is too high, subdivide again */
		if ( subdivide > minDiffuseSubdivide &&
			 ( gradient[ 0 ] > RADIOSITY_MAX_GRADIENT || gradient[ 1 ] > RADIOSITY_MAX_GRADIENT || gradient[ 2 ] > RADIOSITY_MAX_GRADIENT ) ) {
			RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, ( subdivide / 2.0f ), qfalse, rw, cw );
			return;
		}
	}

	/* create a regular winding and an average normal */
	w = AllocWinding( rw->numVerts );
	w->numpoints = rw->numVerts;
	VectorClear( normal );
	for ( i = 0; i < rw->numVerts; i++ )
	{
		VectorCopy( rw->verts[ i ].xyz, w->p[ i ] );
		VectorAdd( normal, rw->verts[ i ].normal, normal );
	}
	VectorScale( normal, ( 1.0f / rw->numVerts ), normal );
	if ( VectorNormalize( normal, normal ) == 0.0f ) {
		return;
	}

	/* early out? */
	if ( bouncing && VectorLength( color ) < RADIOSITY_MIN ) {
		return;
	}

	/* debug code */
	//%	Sys_Printf( "Size: %d %d %d\n", (int) (maxs[ 0 ] - mins[ 0 ]), (int) (maxs[ 1 ] - mins[ 1 ]), (int) (maxs[ 2 ] - mins[ 2 ]) );
	//%	Sys_Printf( "Grad: %f %f %f\n", gradient[ 0 ], gradient[ 1 ], gradient[ 2 ] );

	/* increment counts */
	numDiffuseLights++;
	switch ( ds->surfaceType )
	{
	case MST_PLANAR:
		numBrushDiffuseLights++;
		break;

	case MST_TRIANGLE_SOUP:
		numTriangleDiffuseLights++;
		break;

	case MST_PATCH:
		numPatchDiffuseLights++;
		break;
	}

	/* create a light */
	light = safe_malloc( sizeof( *light ) );
	memset( light, 0, sizeof( *light ) );

	/* attach it */
	ThreadLock();
	light->next = lights;
	lights = light;
	ThreadUnlock();

	/* initialize the light */
	light->flags = LIGHT_AREA_DEFAULT;
	light->type = EMIT_AREA;
	light->si = si;
	light->fade = 1.0f;
	light->w = w;

	/* set falloff threshold */
	light->falloffTolerance = falloffTolerance;

	/* bouncing light? */
	if ( bouncing == qfalse ) {
		/* handle first-pass lights in normal q3a style */
		value = si->value;
		light->photons = value * area * areaScale;
		light->add = value * formFactorValueScale * areaScale;
		VectorCopy( si->color, light->color );
		VectorScale( light->color, light->add, light->emitColor );
		light->style = si->lightStyle;
		if ( light->style < 0 || light->style >= LS_NONE ) {
			light->style = 0;
		}

		/* set origin */
		VectorAdd( mins, maxs, light->origin );
		VectorScale( light->origin, 0.5f, light->origin );

		/* nudge it off the plane a bit */
		VectorCopy( normal, light->normal );
		VectorMA( light->origin, 1.0f, light->normal, light->origin );
		light->dist = DotProduct( light->origin, normal );

		/* optionally create a point splashsplash light for first pass */
		if ( original && si->backsplashFraction > 0 ) {
			/* allocate a new point light */
			splash = safe_malloc( sizeof( *splash ) );
			memset( splash, 0, sizeof( *splash ) );
			splash->next = lights;
			lights = splash;

			/* set it up */
			splash->flags = LIGHT_Q3A_DEFAULT;
			splash->type = EMIT_POINT;
			splash->photons = light->photons * si->backsplashFraction;
			splash->fade = 1.0f;
			splash->si = si;
			VectorMA( light->origin, si->backsplashDistance, normal, splash->origin );
			VectorCopy( si->color, splash->color );
			splash->falloffTolerance = falloffTolerance;
			splash->style = light->style;

			/* add to counts */
			numPointLights++;
		}
	}
	else
	{
		/* handle bounced light (radiosity) a little differently */
		value = RADIOSITY_VALUE * si->bounceScale * 0.375f;
		light->photons = value * area * bounceScale;
		light->add = value * formFactorValueScale * bounceScale;
		VectorCopy( color, light->color );
		VectorScale( light->color, light->add, light->emitColor );
		light->style = style;
		if ( light->style < 0 || light->style >= LS_NONE ) {
			light->style = 0;
		}

		/* set origin */
		WindingCenter( w, light->origin );

		/* nudge it off the plane a bit */
		VectorCopy( normal, light->normal );
		VectorMA( light->origin, 1.0f, light->normal, light->origin );
		light->dist = DotProduct( light->origin, normal );
	}

	/* emit light from both sides? */
	if ( si->compileFlags & C_FOG || si->twoSided ) {
		light->flags |= LIGHT_TWOSIDED;
	}

	//%	Sys_Printf( "\nAL: C: (%6f, %6f, %6f) [%6f] N: (%6f, %6f, %6f) %s\n",
	//%		light->color[ 0 ], light->color[ 1 ], light->color[ 2 ], light->add,
	//%		light->normal[ 0 ], light->normal[ 1 ], light->normal[ 2 ],
	//%		light->si->shader );
}
/*
===================
CM_GeneratePatchCollide

Creates an internal structure that will be used to perform
collision detection with a patch mesh.

Points is packed as concatenated rows.
===================
*/
struct patchCollide_s	*CM_GeneratePatchCollide( int width, int height, vec3_t *points ) {
	patchCollide_t	*pf;
	MAC_STATIC cGrid_t			grid;
	int				i, j;

	if ( width <= 2 || height <= 2 || !points ) {
		Com_Error( ERR_DROP, "CM_GeneratePatchFacets: bad parameters: (%i, %i, %p)",
			width, height, points );
	}

	if ( !(width & 1) || !(height & 1) ) {
		Com_Error( ERR_DROP, "CM_GeneratePatchFacets: even sizes are invalid for quadratic meshes" );
	}

	if ( width > MAX_GRID_SIZE || height > MAX_GRID_SIZE ) {
		Com_Error( ERR_DROP, "CM_GeneratePatchFacets: source is > MAX_GRID_SIZE" );
	}

	// build a grid
	grid.width = width;
	grid.height = height;
	grid.wrapWidth = false;
	grid.wrapHeight = false;
	for ( i = 0 ; i < width ; i++ ) {
		for ( j = 0 ; j < height ; j++ ) {
			VectorCopy( points[j*width + i], grid.points[i][j] );
		}
	}

	// subdivide the grid
	CM_SetGridWrapWidth( &grid );
	CM_SubdivideGridColumns( &grid );
	CM_RemoveDegenerateColumns( &grid );

	CM_TransposeGrid( &grid );

	CM_SetGridWrapWidth( &grid );
	CM_SubdivideGridColumns( &grid );
	CM_RemoveDegenerateColumns( &grid );

	// we now have a grid of points exactly on the curve
	// the aproximate surface defined by these points will be
	// collided against
	pf = reinterpret_cast<patchCollide_t*>(Hunk_Alloc( sizeof( *pf ), h_high ));
	ClearBounds( pf->bounds[0], pf->bounds[1] );
	for ( i = 0 ; i < grid.width ; i++ ) {
		for ( j = 0 ; j < grid.height ; j++ ) {
			AddPointToBounds( grid.points[i][j], pf->bounds[0], pf->bounds[1] );
		}
	}

	c_totalPatchBlocks += ( grid.width - 1 ) * ( grid.height - 1 );

	// generate a bsp tree for the surface
	CM_PatchCollideFromGrid( &grid, pf );

	// expand by one unit for epsilon purposes
	pf->bounds[0][0] -= 1;
	pf->bounds[0][1] -= 1;
	pf->bounds[0][2] -= 1;

	pf->bounds[1][0] += 1;
	pf->bounds[1][1] += 1;
	pf->bounds[1][2] += 1;

	return pf;
}
Beispiel #20
0
/*
===============
ParseTriSurf
===============
*/
static void ParseTriSurf( dsurface_t *ds, mapVert_t *verts, msurface_t *surf, int *indexes, world_t &worldData, int index ) {
    srfTriangles_t	*tri;
    int				i, j, k;
    int				numVerts, numIndexes;

    // get fog volume
    surf->fogIndex = LittleLong( ds->fogNum ) + 1;
    if (index && !surf->fogIndex && tr.world && tr.world->globalFog != -1)
    {
        surf->fogIndex = worldData.globalFog;
    }

    // get shader
    surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapsVertex, ds->lightmapStyles, ds->vertexStyles, worldData );
    if ( r_singleShader->integer && !surf->shader->sky ) {
        surf->shader = tr.defaultShader;
    }

    numVerts = LittleLong( ds->numVerts );
    numIndexes = LittleLong( ds->numIndexes );

    if ( numVerts >= SHADER_MAX_VERTEXES ) {
        Com_Error(ERR_DROP, "ParseTriSurf: verts > MAX (%d > %d) on misc_model %s", numVerts, SHADER_MAX_VERTEXES, surf->shader->name );
    }
    if ( numIndexes >= SHADER_MAX_INDEXES ) {
        Com_Error(ERR_DROP, "ParseTriSurf: indices > MAX (%d > %d) on misc_model %s", numIndexes, SHADER_MAX_INDEXES, surf->shader->name );
    }

    tri = (srfTriangles_t *) Z_Malloc( sizeof( *tri ) + numVerts * sizeof( tri->verts[0] ) + numIndexes * sizeof( tri->indexes[0] ), TAG_HUNKMISCMODELS, qfalse );
    tri->dlightBits = 0; //JIC
    tri->surfaceType = SF_TRIANGLES;
    tri->numVerts = numVerts;
    tri->numIndexes = numIndexes;
    tri->verts = (drawVert_t *)(tri + 1);
    tri->indexes = (int *)(tri->verts + tri->numVerts );

    surf->data = (surfaceType_t *)tri;

    // copy vertexes
    verts += LittleLong( ds->firstVert );
    ClearBounds( tri->bounds[0], tri->bounds[1] );
    for ( i = 0 ; i < numVerts ; i++ ) {
        for ( j = 0 ; j < 3 ; j++ ) {
            tri->verts[i].xyz[j] = LittleFloat( verts[i].xyz[j] );
            tri->verts[i].normal[j] = LittleFloat( verts[i].normal[j] );
        }
        AddPointToBounds( tri->verts[i].xyz, tri->bounds[0], tri->bounds[1] );
        for ( j = 0 ; j < 2 ; j++ ) {
            tri->verts[i].st[j] = LittleFloat( verts[i].st[j] );
            for(k=0; k<MAXLIGHTMAPS; k++)
            {
                tri->verts[i].lightmap[k][j] = LittleFloat( verts[i].lightmap[k][j] );
            }
        }
        for(k=0; k<MAXLIGHTMAPS; k++)
        {
            R_ColorShiftLightingBytes( verts[i].color[k], tri->verts[i].color[k] );
        }
    }

    // copy indexes
    indexes += LittleLong( ds->firstIndex );
    for ( i = 0 ; i < numIndexes ; i++ ) {
        tri->indexes[i] = LittleLong( indexes[i] );
        if ( tri->indexes[i] < 0 || tri->indexes[i] >= numVerts ) {
            Com_Error( ERR_DROP, "Bad index in triangle surface" );
        }
    }
}
Beispiel #21
0
/*
===============
GrabFrame
===============
*/
void GrabFrame (char *frame)
{
	triangle_t	*ptri;
	int			i, j;
	trivert_t	*ptrivert;
	int			num_tris;
	char		file1[1024];
	frame_t		*fr;
	vertexnormals_t	vnorms[MAX_VERTS];
	int		index_xyz;
	char	*framefile;

	// the frame 'run1' will be looked for as either
	// run.1 or run1.tri, so the new alias sequence save
	// feature an be used
	framefile = FindFrameFile (frame);

	sprintf (file1, "%s/%s", cdarchive, framefile);
	ExpandPathAndArchive (file1);

	sprintf (file1, "%s/%s",cddir, framefile);

	printf ("grabbing %s\n", file1);

	if (model.num_frames >= MAX_FRAMES)
		Error ("model.num_frames >= MAX_FRAMES");
	fr = &g_frames[model.num_frames];
	model.num_frames++;

	strcpy (fr->name, frame);

//
// load the frame
//
	if (do3ds)
		Load3DSTriangleList (file1, &ptri, &num_tris);
	else
		LoadTriangleList (file1, &ptri, &num_tris);

	if (num_tris != model.num_tris)
		Error ("%s: number of triangles doesn't match base frame\n", file1);

//
// allocate storage for the frame's vertices
//
	ptrivert = fr->v;

	for (i=0 ; i<model.num_xyz ; i++)
	{
		vnorms[i].numnormals = 0;
		VectorClear (vnorms[i].normalsum);
	}
	ClearBounds (fr->mins, fr->maxs);

//
// store the frame's vertices in the same order as the base. This assumes the
// triangles and vertices in this frame are in exactly the same order as in the
// base
//
	for (i=0 ; i<num_tris ; i++)
	{
		vec3_t	vtemp1, vtemp2, normal;
		float	ftemp;

		VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
		VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
		CrossProduct (vtemp1, vtemp2, normal);

		VectorNormalize2 (normal, normal);

	// rotate the normal so the model faces down the positive x axis
		ftemp = normal[0];
		normal[0] = -normal[1];
		normal[1] = ftemp;

		for (j=0 ; j<3 ; j++)
		{
			index_xyz = triangles[i].index_xyz[j];

		// rotate the vertices so the model faces down the positive x axis
		// also adjust the vertices to the desired origin
			ptrivert[index_xyz].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +
										adjust[0];
			ptrivert[index_xyz].v[1] = (ptri[i].verts[j][0] * scale_up) +
										adjust[1];
			ptrivert[index_xyz].v[2] = (ptri[i].verts[j][2] * scale_up) +
										adjust[2];

			AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);

			VectorAdd (vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum);
			vnorms[index_xyz].numnormals++;
		}
	}

//
// calculate the vertex normals, match them to the template list, and store the
// index of the best match
//
	for (i=0 ; i<model.num_xyz ; i++)
	{
		int		j;
		vec3_t	v;
		float	maxdot;
		int		maxdotindex;
		int		c;

		c = vnorms[i].numnormals;
		if (!c)
			Error ("Vertex with no triangles attached");

		VectorScale (vnorms[i].normalsum, 1.0/c, v);
		VectorNormalize2 (v, v);

		maxdot = -999999.0;
		maxdotindex = -1;

		for (j=0 ; j<NUMVERTEXNORMALS ; j++)
		{
			float	dot;

			dot = DotProduct (v, avertexnormals[j]);
			if (dot > maxdot)
			{
				maxdot = dot;
				maxdotindex = j;
			}
		}

		ptrivert[i].lightnormalindex = maxdotindex;
	}

	free (ptri);
}
Beispiel #22
0
/*
=====================
RE_AddPolysToScene

=====================
*/
void RE_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys )
{
	srfPoly_t *poly;
	int       i;
	int       fogIndex;
	fog_t     *fog;
	vec3_t    bounds[ 2 ];
	int       j;

	if ( !tr.registered )
	{
		return;
	}

	if ( !hShader )
	{
		ri.Printf( PRINT_DEVELOPER, "WARNING: RE_AddPolysToScene: NULL poly shader\n" );
		return;
	}

	for ( j = 0; j < numPolys; j++ )
	{
		if ( r_numpolyverts + numVerts > max_polyverts || r_numpolys >= max_polys )
		{
//          ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolysToScene: MAX_POLYS or MAX_POLYVERTS reached\n");
			return;
		}

		poly = &backEndData[ tr.smpFrame ]->polys[ r_numpolys ];
		poly->surfaceType = SF_POLY;
		poly->hShader = hShader;
		poly->numVerts = numVerts;
		poly->verts = &backEndData[ tr.smpFrame ]->polyVerts[ r_numpolyverts ];

		memcpy( poly->verts, &verts[ numVerts * j ], numVerts * sizeof( *verts ) );

		// Ridah
		if ( glConfig.hardwareType == GLHW_RAGEPRO )
		{
			poly->verts->modulate[ 0 ] = 255;
			poly->verts->modulate[ 1 ] = 255;
			poly->verts->modulate[ 2 ] = 255;
			poly->verts->modulate[ 3 ] = 255;
		}

		// done.
		r_numpolys++;
		r_numpolyverts += numVerts;

		// if no world is loaded
		if ( tr.world == NULL )
		{
			fogIndex = 0;
		}
		// see if it is in a fog volume
		else if ( tr.world->numfogs == 1 )
		{
			fogIndex = 0;
		}
		else
		{
			// find which fog volume the poly is in
			VectorCopy( poly->verts[ 0 ].xyz, bounds[ 0 ] );
			VectorCopy( poly->verts[ 0 ].xyz, bounds[ 1 ] );

			for ( i = 1; i < poly->numVerts; i++ )
			{
				AddPointToBounds( poly->verts[ i ].xyz, bounds[ 0 ], bounds[ 1 ] );
			}

			for ( fogIndex = 1; fogIndex < tr.world->numfogs; fogIndex++ )
			{
				fog = &tr.world->fogs[ fogIndex ];

				if ( bounds[ 1 ][ 0 ] >= fog->bounds[ 0 ][ 0 ]
				     && bounds[ 1 ][ 1 ] >= fog->bounds[ 0 ][ 1 ]
				     && bounds[ 1 ][ 2 ] >= fog->bounds[ 0 ][ 2 ]
				     && bounds[ 0 ][ 0 ] <= fog->bounds[ 1 ][ 0 ] && bounds[ 0 ][ 1 ] <= fog->bounds[ 1 ][ 1 ] && bounds[ 0 ][ 2 ] <= fog->bounds[ 1 ][ 2 ] )
				{
					break;
				}
			}

			if ( fogIndex == tr.world->numfogs )
			{
				fogIndex = 0;
			}
		}

		poly->fogIndex = fogIndex;
	}
}
Beispiel #23
0
void Select_AutoCaulk()
{
	/*Sys_Printf*/common->Printf("Caulking...\n");

	FacesToCaulk.Clear();

	int iSystemBrushesSkipped = 0;
	face_t *pSelectedFace;

	brush_t *next;
	for (brush_t *pSelectedBrush = selected_brushes.next ; pSelectedBrush != &selected_brushes ; pSelectedBrush = next)
	{
		next = pSelectedBrush->next;

		if (pSelectedBrush->owner->eclass->fixedsize)
			continue;	// apparently this means it's a model, so skip it...

		// new check, we can't caulk a brush that has any "system/" faces...
		//
		bool bSystemFacePresent = false;
		for ( pSelectedFace = pSelectedBrush->brush_faces; pSelectedFace; pSelectedFace = pSelectedFace->next)
		{
			if (!strnicmp(pSelectedFace->d_texture->GetName(),"system/",7))
			{
				bSystemFacePresent = true;
				break;
			}
		}
		if (bSystemFacePresent)
		{
			iSystemBrushesSkipped++;
			continue;	// verboten to caulk this.
		}

		for (int iBrushListToScan = 0; iBrushListToScan<2; iBrushListToScan++)
		{
			brush_t	*snext;
			for (brush_t *pScannedBrush = (iBrushListToScan?active_brushes.next:selected_brushes.next); pScannedBrush != (iBrushListToScan?&active_brushes:&selected_brushes) ; pScannedBrush = snext)
			{
				snext = pScannedBrush->next;

				if ( pScannedBrush == pSelectedBrush)
					continue;

				if (pScannedBrush->owner->eclass->fixedsize || pScannedBrush->pPatch || pScannedBrush->hiddenBrush)
					continue;

				if (FilterBrush(pScannedBrush))
					continue;

// idMaterial stuff no longer support this, not sure what else to do.
//   Searching for other occurences of QER_NOCARVE just shows people REMing the code and ignoring ths issue...
//
//				if (pScannedBrush->brush_faces->d_texture->bFromShader && (pScannedBrush->brush_faces->d_texture->TestMaterialFlag(QER_NOCARVE)))
//					continue;

				// basic-reject first to see if brushes can even possibly touch (coplanar counts as touching)
				//
				int i;
				for (i=0 ; i<3 ; i++)
				{
					if (pSelectedBrush->mins[i] > pScannedBrush->maxs[i] ||
						pSelectedBrush->maxs[i] < pScannedBrush->mins[i])
					{
						break;
					}
				}
				if (i != 3)
					continue;	// can't be touching

				// ok, now for the clever stuff, we need to detect only those faces that are both coplanar and smaller
				//	or equal to the face they're coplanar with...
				//
				for (pSelectedFace = pSelectedBrush->brush_faces; pSelectedFace; pSelectedFace = pSelectedFace->next)
				{
					idWinding *pSelectedWinding = pSelectedFace->face_winding;

					if (!pSelectedWinding)
						continue;	// freed face, probably won't happen here, but who knows with this program?

	//				SquaredFace_t SelectedSquaredFace;
	//				WindingToSquaredFace( &SelectedSquaredFace, pSelectedWinding);

					for (face_t *pScannedFace = pScannedBrush->brush_faces; pScannedFace; pScannedFace = pScannedFace->next)
					{
						// don't even try caulking against a system face, because these are often transparent and will leave holes
						//
						if (!strnicmp(pScannedFace->d_texture->GetName(),"system/",7))
							continue;

						// and don't try caulking against something inherently transparent...
						//
						if (pScannedFace->d_texture->TestMaterialFlag(QER_TRANS))
							continue;

						idWinding *pScannedWinding = pScannedFace->face_winding;

						if (!pScannedWinding)
							continue;	// freed face, probably won't happen here, but who knows with this program?

	//					SquaredFace_t ScannedSquaredFace;
	//					WindingToSquaredFace( &ScannedSquaredFace, pScannedWinding);

	/*					if (VectorCompare(ScannedSquaredFace.v3NormalisedRotationVector, SelectedSquaredFace.v3NormalisedRotationVector)
							&&
							VectorCompare(ScannedSquaredFace.v3NormalisedElevationVector, SelectedSquaredFace.v3NormalisedElevationVector)
							)
	*/
						{
							// brush faces are in parallel planes to each other, so check that their normals
							//	are opposite, by adding them together and testing for zero...
							// (if normals are opposite, then faces can be against/touching each other?)
							//
							idVec3 v3ZeroTest;
							idVec3 v3Zero;v3Zero.Zero();	//static idVec3 v3Zero={0,0,0};

							VectorAdd(pSelectedFace->plane.Normal(),pScannedFace->plane.Normal(),v3ZeroTest);
							if (v3ZeroTest == v3Zero)
							{
								// planes are facing each other...
								//
								// coplanar? (this is some maths of Gil's, which I don't even pretend to understand)
								//
								float fTotalDist = 0;
								for (int _i=0; _i<3; _i++)
								{
									fTotalDist += fabs(	DotProduct(pSelectedFace->plane.Normal(),(*pSelectedWinding)[0])
														-
														DotProduct(pSelectedFace->plane.Normal(),(*pScannedWinding)[i])
														);
								}
								//OutputDebugString(va("Dist = %g\n",fTotalDist));

								if (fTotalDist > 0.01)
									continue;

								// every point in the selected face must be within (or equal to) the bounds of the
								//	scanned face...
								//
								// work out the bounds first...
								//
								idVec3 v3ScannedBoundsMins, v3ScannedBoundsMaxs;
								ClearBounds (v3ScannedBoundsMins, v3ScannedBoundsMaxs);
								int iPoint;
								for (iPoint=0; iPoint<pScannedWinding->GetNumPoints(); iPoint++)
								{
									AddPointToBounds( (*pScannedWinding)[iPoint].ToVec3(), v3ScannedBoundsMins, v3ScannedBoundsMaxs);
								}
								// floor 'em... (or .001 differences mess things up...
								//
								FloorBounds(v3ScannedBoundsMins, v3ScannedBoundsMaxs);


								// now check points from selected face...
								//
								bool bWithin = true;
								for (iPoint=0; iPoint < pSelectedWinding->GetNumPoints(); iPoint++)
								{
									for (int iXYZ=0; iXYZ<3; iXYZ++)
									{
										float f = floor((*pSelectedWinding)[iPoint][iXYZ] + 0.5);
										if (!
												(
												f >= v3ScannedBoundsMins[iXYZ]
												&&
												f <= v3ScannedBoundsMaxs[iXYZ]
												)
											 )
										{
											bWithin = false;
										}
									}
								}

								if (bWithin)
								{
									PairBrushFace_t PairBrushFace;
													PairBrushFace.pFace = pSelectedFace;
													PairBrushFace.pBrush= pSelectedBrush;
									FacesToCaulk.Append(PairBrushFace);
								}
							}
						}
					}
				}
			}
		}
	}


	// apply caulk...
	//
	int iFacesCaulked = 0;
	if (FacesToCaulk.Num())
	{
		LPCSTR psCaulkName = "textures/common/caulk";
		const idMaterial *pCaulk = Texture_ForName(psCaulkName);

		if (pCaulk)
		{
			//
			// and call some other junk that Radiant wants so so we can use it later...
			//
			texdef_t tex;
			memset (&tex, 0, sizeof(tex));
			tex.scale[0] = 1;
			tex.scale[1] = 1;
			//tex.flags = pCaulk->flags;	// field missing in Q4
			//tex.value = pCaulk->value;	// ditto
			//tex.contents = pCaulk->contents;	// ditto
			tex.SetName( pCaulk->GetName() );

			//Texture_SetTexture (&tex);

			for (int iListEntry = 0; iListEntry < FacesToCaulk.Num(); iListEntry++)
			{
				PairBrushFace_t &PairBrushFace = FacesToCaulk[iListEntry];
				face_t *pFace = PairBrushFace.pFace;
				brush_t*pBrush= PairBrushFace.pBrush;

				pFace->d_texture = pCaulk;
				pFace->texdef = tex;

				Face_FitTexture(pFace, 1, 1);	// this doesn't work here for some reason... duh.
				Brush_Build(pBrush);

				iFacesCaulked++;
			}
		}
		else
		{
			/*Sys_Printf*/common->Printf(" Unable to locate caulk texture at: \"%s\"!\n",psCaulkName);
		}
	}

	/*Sys_Printf*/common->Printf("( %d faces caulked )\n",iFacesCaulked);

	if (iSystemBrushesSkipped)
	{
		/*Sys_Printf*/common->Printf("( %d system-faced brushes skipped )\n",iSystemBrushesSkipped);
	}

	Sys_UpdateWindows (W_ALL);
}
Beispiel #24
0
/*
RE_ProjectDecal()
creates a new decal projector from a triangle
projected polygons should be 3 or 4 points
if a single point is passed in (numPoints == 1) then the decal will be omnidirectional
omnidirectional decals use points[ 0 ] as center and projection[ 3 ] as radius
pass in lifeTime < 0 for a temporary mark
*/
void RE_ProjectDecal(qhandle_t hShader, int numPoints, vec3_t *points, vec4_t projection, vec4_t color, int lifeTime,
                     int fadeTime)
{
	static int       totalProjectors = 0;
	int              i;
	float            radius, iDist;
	vec3_t           xyz;
	decalVert_t      dv[4];
	decalProjector_t *dp, temp;

	if (r_numDecalProjectors >= MAX_DECAL_PROJECTORS)
	{
		Ren_Print("WARNING: RE_ProjectDecal() Max decal projectors reached (%d)\n", MAX_DECAL_PROJECTORS);
		return;
	}

	// dummy check
	if (numPoints != 1 && numPoints != 3 && numPoints != 4)
	{
		Ren_Warning("WARNING: Invalid number of decal points (%d)\n", numPoints);
		return;
	}

	// early outs
	if (lifeTime == 0)
	{
		return;
	}
	if (projection[3] <= 0.0f)
	{
		return;
	}

	// set times properly
	if (lifeTime < 0 || fadeTime < 0)
	{
		lifeTime = 0;
		fadeTime = 0;
	}

	// basic setup
	temp.shader        = R_GetShaderByHandle(hShader);
	temp.color[0]      = color[0] * 255;
	temp.color[1]      = color[1] * 255;
	temp.color[2]      = color[2] * 255;
	temp.color[3]      = color[3] * 255;
	temp.numPlanes     = numPoints + 2;
	temp.fadeStartTime = tr.refdef.time + lifeTime - fadeTime; // fixme: stale refdef time
	temp.fadeEndTime   = temp.fadeStartTime + fadeTime;

	// set up decal texcoords (fixme: support arbitrary projector st coordinates in trapcall)
	dv[0].st[0] = 0.0f;
	dv[0].st[1] = 0.0f;
	dv[1].st[0] = 0.0f;
	dv[1].st[1] = 1.0f;
	dv[2].st[0] = 1.0f;
	dv[2].st[1] = 1.0f;
	dv[3].st[0] = 1.0f;
	dv[3].st[1] = 0.0f;

	// omnidirectional?
	if (numPoints == 1)
	{
		// set up omnidirectional
		numPoints            = 4;
		temp.numPlanes       = 6;
		temp.omnidirectional = qtrue;
		radius               = projection[3];

		Vector4Set(projection, 0.0f, 0.0f, -1.0f, radius * 2.0f);
		iDist = 1.0f / (radius * 2.0f);

		// set corner
		VectorSet(xyz, points[0][0] - radius, points[0][1] - radius, points[0][2] + radius);

		// make x axis texture matrix (yz)
		VectorSet(temp.texMat[0][0], 0.0f, iDist, 0.0f);
		temp.texMat[0][0][3] = -DotProduct(temp.texMat[0][0], xyz);
		VectorSet(temp.texMat[0][1], 0.0f, 0.0f, iDist);
		temp.texMat[0][1][3] = -DotProduct(temp.texMat[0][1], xyz);

		// make y axis texture matrix (xz)
		VectorSet(temp.texMat[1][0], iDist, 0.0f, 0.0f);
		temp.texMat[1][0][3] = -DotProduct(temp.texMat[1][0], xyz);
		VectorSet(temp.texMat[1][1], 0.0f, 0.0f, iDist);
		temp.texMat[1][1][3] = -DotProduct(temp.texMat[1][1], xyz);

		// make z axis texture matrix (xy)
		VectorSet(temp.texMat[2][0], iDist, 0.0f, 0.0f);
		temp.texMat[2][0][3] = -DotProduct(temp.texMat[2][0], xyz);
		VectorSet(temp.texMat[2][1], 0.0f, iDist, 0.0f);
		temp.texMat[2][1][3] = -DotProduct(temp.texMat[2][1], xyz);

		// setup decal points
		VectorSet(dv[0].xyz, points[0][0] - radius, points[0][1] - radius, points[0][2] + radius);
		VectorSet(dv[1].xyz, points[0][0] - radius, points[0][1] + radius, points[0][2] + radius);
		VectorSet(dv[2].xyz, points[0][0] + radius, points[0][1] + radius, points[0][2] + radius);
		VectorSet(dv[3].xyz, points[0][0] + radius, points[0][1] - radius, points[0][2] + radius);
	}
	else
	{
		// set up unidirectional
		temp.omnidirectional = qfalse;

		// set up decal points
		VectorCopy(points[0], dv[0].xyz);
		VectorCopy(points[1], dv[1].xyz);
		VectorCopy(points[2], dv[2].xyz);
		VectorCopy(points[3], dv[3].xyz);

		// make texture matrix
		if (!MakeTextureMatrix(temp.texMat[0], projection, &dv[0], &dv[1], &dv[2]))
		{
			return;
		}
	}

	// bound the projector
	ClearBounds(temp.mins, temp.maxs);
	for (i = 0; i < numPoints; i++)
	{
		AddPointToBounds(dv[i].xyz, temp.mins, temp.maxs);
		VectorMA(dv[i].xyz, projection[3], projection, xyz);
		AddPointToBounds(xyz, temp.mins, temp.maxs);
	}

	// make bounding sphere
	VectorAdd(temp.mins, temp.maxs, temp.center);
	VectorScale(temp.center, 0.5f, temp.center);
	VectorSubtract(temp.maxs, temp.center, xyz);
	temp.radius  = VectorLength(xyz);
	temp.radius2 = temp.radius * temp.radius;

	// make the front plane
	if (!PlaneFromPoints(temp.planes[0], dv[0].xyz, dv[1].xyz, dv[2].xyz))
	{
		return;
	}

	// make the back plane
	VectorSubtract(vec3_origin, temp.planes[0], temp.planes[1]);
	VectorMA(dv[0].xyz, projection[3], projection, xyz);
	temp.planes[1][3] = DotProduct(xyz, temp.planes[1]);

	// make the side planes
	for (i = 0; i < numPoints; i++)
	{
		VectorMA(dv[i].xyz, projection[3], projection, xyz);
		if (!PlaneFromPoints(temp.planes[i + 2], dv[(i + 1) % numPoints].xyz, dv[i].xyz, xyz))
		{
			return;
		}
	}

	// create a new projector
	dp = &backEndData->decalProjectors[r_numDecalProjectors];
	Com_Memcpy(dp, &temp, sizeof(*dp));
	dp->projectorNum = totalProjectors++;

	// we have a winner
	r_numDecalProjectors++;
}
Beispiel #25
0
/*
* Mod_LoadAliasMD3Model
*/
void Mod_LoadAliasMD3Model( model_t *mod, model_t *parent, void *buffer, bspFormatDesc_t *unused )
{
	int version, i, j, l;
	int bufsize, numverts;
	qbyte *buf;
	dmd3header_t *pinmodel;
	dmd3frame_t *pinframe;
	dmd3tag_t *pintag;
	dmd3mesh_t *pinmesh;
	dmd3skin_t *pinskin;
	dmd3coord_t *pincoord;
	dmd3vertex_t *pinvert;
	unsigned int *pinelem;
	elem_t *poutelem;
	maliasvertex_t *poutvert;
	vec2_t *poutcoord;
	maliasskin_t *poutskin;
	maliasmesh_t *poutmesh;
	maliastag_t *pouttag;
	maliasframe_t *poutframe;
	maliasmodel_t *poutmodel;
	drawSurfaceAlias_t *drawSurf;

	pinmodel = ( dmd3header_t * )buffer;
	version = LittleLong( pinmodel->version );

	if( version != MD3_ALIAS_VERSION )
		ri.Com_Error( ERR_DROP, "%s has wrong version number (%i should be %i)",
		mod->name, version, MD3_ALIAS_VERSION );

	mod->type = mod_alias;
	mod->extradata = poutmodel = ( maliasmodel_t * )Mod_Malloc( mod, sizeof( maliasmodel_t ) );
	mod->radius = 0;
	mod->registrationSequence = rsh.registrationSequence;
	mod->touch = &Mod_TouchAliasModel;

	ClearBounds( mod->mins, mod->maxs );

	// byte swap the header fields and sanity check
	poutmodel->numframes = LittleLong( pinmodel->num_frames );
	poutmodel->numtags = LittleLong( pinmodel->num_tags );
	poutmodel->nummeshes = LittleLong( pinmodel->num_meshes );
	poutmodel->numskins = 0;

	if( poutmodel->numframes <= 0 )
		ri.Com_Error( ERR_DROP, "model %s has no frames", mod->name );
	//	else if( poutmodel->numframes > MD3_MAX_FRAMES )
	//		ri.Com_Error( ERR_DROP, "model %s has too many frames", mod->name );

	if( poutmodel->numtags > MD3_MAX_TAGS )
		ri.Com_Error( ERR_DROP, "model %s has too many tags", mod->name );
	else if( poutmodel->numtags < 0 )
		ri.Com_Error( ERR_DROP, "model %s has invalid number of tags", mod->name );

	if( poutmodel->nummeshes < 0 )
		ri.Com_Error( ERR_DROP, "model %s has invalid number of meshes", mod->name );
	else if( !poutmodel->nummeshes && !poutmodel->numtags )
		ri.Com_Error( ERR_DROP, "model %s has no meshes and no tags", mod->name );
	//	else if( poutmodel->nummeshes > MD3_MAX_MESHES )
	//		ri.Com_Error( ERR_DROP, "model %s has too many meshes", mod->name );

	bufsize = poutmodel->numframes * ( sizeof( maliasframe_t ) + sizeof( maliastag_t ) * poutmodel->numtags ) +
		poutmodel->nummeshes * sizeof( maliasmesh_t ) + 
		poutmodel->nummeshes * sizeof( drawSurfaceAlias_t );
	buf = ( qbyte * )Mod_Malloc( mod, bufsize );

	//
	// load the frames
	//
	pinframe = ( dmd3frame_t * )( ( qbyte * )pinmodel + LittleLong( pinmodel->ofs_frames ) );
	poutframe = poutmodel->frames = ( maliasframe_t * )buf; buf += sizeof( maliasframe_t ) * poutmodel->numframes;
	for( i = 0; i < poutmodel->numframes; i++, pinframe++, poutframe++ )
	{
		for( j = 0; j < 3; j++ )
		{
			poutframe->scale[j] = MD3_XYZ_SCALE;
			poutframe->translate[j] = LittleFloat( pinframe->translate[j] );
		}

		// never trust the modeler utility and recalculate bbox and radius
		ClearBounds( poutframe->mins, poutframe->maxs );
	}

	//
	// load the tags
	//
	pintag = ( dmd3tag_t * )( ( qbyte * )pinmodel + LittleLong( pinmodel->ofs_tags ) );
	pouttag = poutmodel->tags = ( maliastag_t * )buf; buf += sizeof( maliastag_t ) * poutmodel->numframes * poutmodel->numtags;
	for( i = 0; i < poutmodel->numframes; i++ )
	{
		for( l = 0; l < poutmodel->numtags; l++, pintag++, pouttag++ )
		{
			mat3_t axis;

			for( j = 0; j < 3; j++ )
			{
				axis[AXIS_FORWARD+j] = LittleFloat( pintag->axis[0][j] );
				axis[AXIS_RIGHT+j] = LittleFloat( pintag->axis[1][j] );
				axis[AXIS_UP+j] = LittleFloat( pintag->axis[2][j] );
				pouttag->origin[j] = LittleFloat( pintag->origin[j] );
			}

			Quat_FromMatrix3( axis, pouttag->quat );
			Quat_Normalize( pouttag->quat );

			Q_strncpyz( pouttag->name, pintag->name, MD3_MAX_PATH );
		}
	}

	//
	// allocate drawSurfs
	//
	drawSurf = poutmodel->drawSurfs = ( drawSurfaceAlias_t * )buf; buf += sizeof( drawSurfaceAlias_t ) * poutmodel->nummeshes;
	for( i = 0; i < poutmodel->nummeshes; i++, drawSurf++ )
	{
		drawSurf->type = ST_ALIAS;
		drawSurf->model = mod;
		drawSurf->mesh = poutmodel->meshes + i;
	}

	//
	// load meshes
	//
	pinmesh = ( dmd3mesh_t * )( ( qbyte * )pinmodel + LittleLong( pinmodel->ofs_meshes ) );
	poutmesh = poutmodel->meshes = ( maliasmesh_t * )buf; buf += sizeof( maliasmesh_t ) * poutmodel->nummeshes;
	for( i = 0; i < poutmodel->nummeshes; i++, poutmesh++ )
	{
		if( strncmp( (const char *)pinmesh->id, IDMD3HEADER, 4 ) )
			ri.Com_Error( ERR_DROP, "mesh %s in model %s has wrong id (%s should be %s)",
			pinmesh->name, mod->name, pinmesh->id, IDMD3HEADER );

		Q_strncpyz( poutmesh->name, pinmesh->name, MD3_MAX_PATH );

		Mod_StripLODSuffix( poutmesh->name );

		poutmesh->numtris = LittleLong( pinmesh->num_tris );
		poutmesh->numskins = LittleLong( pinmesh->num_skins );
		poutmesh->numverts = numverts = LittleLong( pinmesh->num_verts );

		/*		if( poutmesh->numskins <= 0 )
		ri.Com_Error( ERR_DROP, "mesh %i in model %s has no skins", i, mod->name );
		else*/ if( poutmesh->numskins > MD3_MAX_SHADERS )
			ri.Com_Error( ERR_DROP, "mesh %i in model %s has too many skins", i, mod->name );
		if( poutmesh->numtris <= 0 )
			ri.Com_Error( ERR_DROP, "mesh %i in model %s has no elements", i, mod->name );
		else if( poutmesh->numtris > MD3_MAX_TRIANGLES )
			ri.Com_Error( ERR_DROP, "mesh %i in model %s has too many triangles", i, mod->name );
		if( poutmesh->numverts <= 0 )
			ri.Com_Error( ERR_DROP, "mesh %i in model %s has no vertices", i, mod->name );
		else if( poutmesh->numverts > MD3_MAX_VERTS )
			ri.Com_Error( ERR_DROP, "mesh %i in model %s has too many vertices", i, mod->name );

		bufsize = ALIGN( sizeof( maliasskin_t ) * poutmesh->numskins, sizeof( vec_t ) ) +
			numverts * ( sizeof( vec2_t ) + sizeof( maliasvertex_t ) * poutmodel->numframes ) +
			poutmesh->numtris * sizeof( elem_t ) * 3;
		buf = ( qbyte * )Mod_Malloc( mod, bufsize );

		//
		// load the skins
		//
		pinskin = ( dmd3skin_t * )( ( qbyte * )pinmesh + LittleLong( pinmesh->ofs_skins ) );
		poutskin = poutmesh->skins = ( maliasskin_t * )buf;
		buf += ALIGN( sizeof( maliasskin_t ) * poutmesh->numskins, sizeof( vec_t ) );
		for( j = 0; j < poutmesh->numskins; j++, pinskin++, poutskin++ ) {
			Q_strncpyz( poutskin->name, pinskin->name, sizeof( poutskin->name ) );
			poutskin->shader = R_RegisterSkin( poutskin->name );
		}

		//
		// load the texture coordinates
		//
		pincoord = ( dmd3coord_t * )( ( qbyte * )pinmesh + LittleLong( pinmesh->ofs_tcs ) );
		poutcoord = poutmesh->stArray = ( vec2_t * )buf; buf += poutmesh->numverts * sizeof( vec2_t );
		for( j = 0; j < poutmesh->numverts; j++, pincoord++ )
		{
			poutcoord[j][0] = LittleFloat( pincoord->st[0] );
			poutcoord[j][1] = LittleFloat( pincoord->st[1] );
		}

		//
		// load the vertexes and normals
		//
		pinvert = ( dmd3vertex_t * )( ( qbyte * )pinmesh + LittleLong( pinmesh->ofs_verts ) );
		poutvert = poutmesh->vertexes = ( maliasvertex_t * )buf;
		buf += poutmesh->numverts * sizeof( maliasvertex_t ) * poutmodel->numframes;
		for( l = 0, poutframe = poutmodel->frames; l < poutmodel->numframes; l++, poutframe++, pinvert += poutmesh->numverts, poutvert += poutmesh->numverts )
		{
			vec3_t v;

			for( j = 0; j < poutmesh->numverts; j++ )
			{
				poutvert[j].point[0] = LittleShort( pinvert[j].point[0] );
				poutvert[j].point[1] = LittleShort( pinvert[j].point[1] );
				poutvert[j].point[2] = LittleShort( pinvert[j].point[2] );

				poutvert[j].latlong[0] = pinvert[j].norm[0];
				poutvert[j].latlong[1] = pinvert[j].norm[1];

				VectorCopy( poutvert[j].point, v );
				AddPointToBounds( v, poutframe->mins, poutframe->maxs );
			}
		}
		
		//
		// load the elems
		//
		pinelem = ( unsigned int * )( ( qbyte * )pinmesh + LittleLong( pinmesh->ofs_elems ) );
		poutelem = poutmesh->elems = ( elem_t * )buf;
		for( j = 0; j < poutmesh->numtris; j++, pinelem += 3, poutelem += 3 )
		{
			poutelem[0] = (elem_t)LittleLong( pinelem[0] );
			poutelem[1] = (elem_t)LittleLong( pinelem[1] );
			poutelem[2] = (elem_t)LittleLong( pinelem[2] );
		}

		pinmesh = ( dmd3mesh_t * )( ( qbyte * )pinmesh + LittleLong( pinmesh->meshsize ) );
	}

	//
	// setup drawSurfs
	//
	for( i = 0; i < poutmodel->nummeshes; i++ )
	{
		drawSurf = poutmodel->drawSurfs + i;
		drawSurf->type = ST_ALIAS;
		drawSurf->model = mod;
		drawSurf->mesh = poutmodel->meshes + i;
	}

	//
	// build S and T vectors for frame 0
	//
	Mod_AliasBuildMeshesForFrame0( mod );

	//
	// calculate model bounds
	//
	poutframe = poutmodel->frames;
	for( i = 0; i < poutmodel->numframes; i++, poutframe++ )
	{
		VectorMA( poutframe->translate, MD3_XYZ_SCALE, poutframe->mins, poutframe->mins );
		VectorMA( poutframe->translate, MD3_XYZ_SCALE, poutframe->maxs, poutframe->maxs );
		poutframe->radius = RadiusFromBounds( poutframe->mins, poutframe->maxs );

		AddPointToBounds( poutframe->mins, mod->mins, mod->maxs );
		AddPointToBounds( poutframe->maxs, mod->mins, mod->maxs );
		mod->radius = max( mod->radius, poutframe->radius );
	}
}
Beispiel #26
0
/*
=====================
R_AddPolysToScene
=====================
*/
static void R_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys )
{
	srfPoly_t *poly;
	int       i, j;
	int       fogIndex;
	fog_t     *fog;
	vec3_t    bounds[ 2 ];

	if ( !tr.registered )
	{
		return;
	}

	if ( !r_drawpolies->integer )
	{
		return;
	}

	if ( !hShader )
	{
		Log::Warn("RE_AddPolyToScene: NULL poly shader" );
		return;
	}

	for ( j = 0; j < numPolys; j++ )
	{
		if ( r_numPolyVerts + numVerts >= r_maxPolyVerts->integer || r_numPolys >= r_maxPolys->integer )
		{
			/*
			   NOTE TTimo this was initially Log::Warn
			   but it happens a lot with high fighting scenes and particles
			   since we don't plan on changing the const and making for room for those effects
			   simply cut this message to developer only
			 */
			Log::Debug("RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n" );
			return;
		}

		poly = &backEndData[ tr.smpFrame ]->polys[ r_numPolys ];
		poly->surfaceType = surfaceType_t::SF_POLY;
		poly->hShader = hShader;
		poly->numVerts = numVerts;
		poly->verts = &backEndData[ tr.smpFrame ]->polyVerts[ r_numPolyVerts ];

		Com_Memcpy( poly->verts, &verts[ numVerts * j ], numVerts * sizeof( *verts ) );

		// done.
		r_numPolys++;
		r_numPolyVerts += numVerts;

		// if no world is loaded
		if ( tr.world == nullptr )
		{
			fogIndex = 0;
		}
		// see if it is in a fog volume
		else if ( tr.world->numFogs == 1 )
		{
			fogIndex = 0;
		}
		else
		{
			// find which fog volume the poly is in
			VectorCopy( poly->verts[ 0 ].xyz, bounds[ 0 ] );
			VectorCopy( poly->verts[ 0 ].xyz, bounds[ 1 ] );

			for ( i = 1; i < poly->numVerts; i++ )
			{
				AddPointToBounds( poly->verts[ i ].xyz, bounds[ 0 ], bounds[ 1 ] );
			}

			for ( fogIndex = 1; fogIndex < tr.world->numFogs; fogIndex++ )
			{
				fog = &tr.world->fogs[ fogIndex ];

				if ( BoundsIntersect( bounds[ 0 ], bounds[ 1 ], fog->bounds[ 0 ], fog->bounds[ 1 ] ) )
				{
					break;
				}
			}

			if ( fogIndex == tr.world->numFogs )
			{
				fogIndex = 0;
			}
		}

		poly->fogIndex = fogIndex;
	}
}
Beispiel #27
0
/*
* R_AddLightOccluder
*/
qboolean R_AddLightOccluder( const entity_t *ent )
{
	int i;
	float maxSide;
	vec3_t origin;
	unsigned int hash_key;
	shadowGroup_t *group;
	mleaf_t *leaf;
	vec3_t mins, maxs, bbox[8];
	qboolean bmodelRotated = qfalse;

	if( rn.refdef.rdflags & RDF_NOWORLDMODEL )
		return qfalse;
	if( !ent->model || ent->model->type == mod_brush )
		return qfalse;

	VectorCopy( ent->lightingOrigin, origin );
	if( ent->model->type == mod_brush )
	{
		vec3_t t;
		VectorAdd( ent->model->mins, ent->model->maxs, t );
		VectorMA( ent->origin, 0.5, t, origin );
	}

	if( VectorCompare( origin, vec3_origin ) )
		return qfalse;

	// find lighting group containing entities with same lightingOrigin as ours
	hash_key = (unsigned int)( origin[0] * 7 + origin[1] * 5 + origin[2] * 3 );
	hash_key &= ( SHADOWGROUPS_HASH_SIZE-1 );

	for( group = r_shadowGroups_hash[hash_key]; group; group = group->hashNext )
	{
		if( VectorCompare( group->origin, origin ) )
			goto add; // found an existing one, add
	}

	if( rsc.numShadowGroups == MAX_SHADOWGROUPS )
		return qfalse; // no free groups

	leaf = Mod_PointInLeaf( origin, rsh.worldModel );

	// start a new group
	group = &rsc.shadowGroups[rsc.numShadowGroups];
	memset( group, 0, sizeof( *group ) );
	group->id = group - rsc.shadowGroups + 1;
	group->bit = ( 1<<rsc.numShadowGroups );
	group->vis = Mod_ClusterPVS( leaf->cluster, rsh.worldModel );
	group->useOrtho = qtrue;
	group->alpha = r_shadows_alpha->value;

	// clear group bounds
	VectorCopy( origin, group->origin );
	ClearBounds( group->mins, group->maxs );
	ClearBounds( group->visMins, group->visMaxs );

	// add to hash table
	group->hashNext = r_shadowGroups_hash[hash_key];
	r_shadowGroups_hash[hash_key] = group;

	rsc.numShadowGroups++;
add:
	// get model bounds
	if( ent->model->type == mod_alias )
		R_AliasModelBBox( ent, mins, maxs );
	else if( ent->model->type == mod_skeletal )
		R_SkeletalModelBBox( ent, mins, maxs );
	else if( ent->model->type == mod_brush )
		R_BrushModelBBox( ent, mins, maxs, &bmodelRotated );
	else
		ClearBounds( mins, maxs );

	maxSide = 0;
	for( i = 0; i < 3; i++ ) {
		if( mins[i] >= maxs[i] )
			return qfalse;
		maxSide = max( maxSide, maxs[i] - mins[i] );
	}

	// ignore tiny objects
	if( maxSide < 10 ) {
		return qfalse;
	}

	rsc.entShadowGroups[R_ENT2NUM(ent)] = group->id;
	if( ent->flags & RF_WEAPONMODEL )
		return qtrue;

	if( ent->model->type == mod_brush )
	{
		VectorCopy( mins, group->mins );
		VectorCopy( maxs, group->maxs );
	}
	else
	{
		// rotate local bounding box and compute the full bounding box for this group
		R_TransformBounds( ent->origin, ent->axis, mins, maxs, bbox );
		for( i = 0; i < 8; i++ ) {
			AddPointToBounds( bbox[i], group->mins, group->maxs );
		}
	}

	// increase projection distance if needed
	VectorSubtract( group->mins, origin, mins );
	VectorSubtract( group->maxs, origin, maxs );
	group->radius = RadiusFromBounds( mins, maxs );
	group->projDist = max( group->projDist, group->radius + min( r_shadows_projection_distance->value, 256 ) );

	return qtrue;
}
Beispiel #28
0
/*
=================
R_MarkFragments

=================
*/
int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection,
				   int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) {
	int				numsurfaces, numPlanes;
	int				i, j, k, m, n;
	surfaceType_t	*surfaces[64];
	vec3_t			mins, maxs;
	int				returnedFragments;
	int				returnedPoints;
	vec3_t			normals[MAX_VERTS_ON_POLY+2];
	float			dists[MAX_VERTS_ON_POLY+2];
	vec3_t			clipPoints[2][MAX_VERTS_ON_POLY];
	int				numClipPoints;
	float			*v;
	srfSurfaceFace_t *surf;
	srfGridMesh_t	*cv;
	drawVert_t		*dv;
	vec3_t			normal;
	vec3_t			projectionDir;
	vec3_t			v1, v2;
	int				*indexes;

	//increment view count for double check prevention
	tr.viewCount++;

	//
	VectorNormalize2( projection, projectionDir );
	// find all the brushes that are to be considered
	ClearBounds( mins, maxs );
	for ( i = 0 ; i < numPoints ; i++ ) {
		vec3_t	temp;

		AddPointToBounds( points[i], mins, maxs );
		VectorAdd( points[i], projection, temp );
		AddPointToBounds( temp, mins, maxs );
		// make sure we get all the leafs (also the one(s) in front of the hit surface)
		VectorMA( points[i], -20, projectionDir, temp );
		AddPointToBounds( temp, mins, maxs );
	}

	if (numPoints > MAX_VERTS_ON_POLY) numPoints = MAX_VERTS_ON_POLY;
	// create the bounding planes for the to be projected polygon
	for ( i = 0 ; i < numPoints ; i++ ) {
		VectorSubtract(points[(i+1)%numPoints], points[i], v1);
		VectorAdd(points[i], projection, v2);
		VectorSubtract(points[i], v2, v2);
		CrossProduct(v1, v2, normals[i]);
		VectorNormalizeFast(normals[i]);
		dists[i] = DotProduct(normals[i], points[i]);
	}
	// add near and far clipping planes for projection
	VectorCopy(projectionDir, normals[numPoints]);
	dists[numPoints] = DotProduct(normals[numPoints], points[0]) - 32;
	VectorCopy(projectionDir, normals[numPoints+1]);
	VectorInverse(normals[numPoints+1]);
	dists[numPoints+1] = DotProduct(normals[numPoints+1], points[0]) - 20;
	numPlanes = numPoints + 2;

	numsurfaces = 0;
	R_BoxSurfaces_r(tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir);
	//assert(numsurfaces <= 64);
	//assert(numsurfaces != 64);

	returnedPoints = 0;
	returnedFragments = 0;

	for ( i = 0 ; i < numsurfaces ; i++ ) {

		if (*surfaces[i] == SF_GRID) {

			cv = (srfGridMesh_t *) surfaces[i];
			for ( m = 0 ; m < cv->height - 1 ; m++ ) {
				for ( n = 0 ; n < cv->width - 1 ; n++ ) {
					// We triangulate the grid and chop all triangles within
					// the bounding planes of the to be projected polygon.
					// LOD is not taken into account, not such a big deal though.
					//
					// It's probably much nicer to chop the grid itself and deal
					// with this grid as a normal SF_GRID surface so LOD will
					// be applied. However the LOD of that chopped grid must
					// be synced with the LOD of the original curve.
					// One way to do this; the chopped grid shares vertices with
					// the original curve. When LOD is applied to the original
					// curve the unused vertices are flagged. Now the chopped curve
					// should skip the flagged vertices. This still leaves the
					// problems with the vertices at the chopped grid edges.
					//
					// To avoid issues when LOD applied to "hollow curves" (like
					// the ones around many jump pads) we now just add a 2 unit
					// offset to the triangle vertices.
					// The offset is added in the vertex normal vector direction
					// so all triangles will still fit together.
					// The 2 unit offset should avoid pretty much all LOD problems.

					numClipPoints = 3;

					dv = cv->verts + m * cv->width + n;

					VectorCopy(dv[0].xyz, clipPoints[0][0]);
					VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0]);
					VectorCopy(dv[cv->width].xyz, clipPoints[0][1]);
					VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]);
					VectorCopy(dv[1].xyz, clipPoints[0][2]);
					VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2]);
					// check the normal of this triangle
					VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
					VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
					CrossProduct(v1, v2, normal);
					VectorNormalizeFast(normal);
					if (DotProduct(normal, projectionDir) < -0.1) {
						// add the fragments of this triangle
						R_AddMarkFragments(numClipPoints, clipPoints,
										   numPlanes, normals, dists,
										   maxPoints, pointBuffer,
										   maxFragments, fragmentBuffer,
										   &returnedPoints, &returnedFragments, mins, maxs);

						if ( returnedFragments == maxFragments ) {
							return returnedFragments;	// not enough space for more fragments
						}
					}

					VectorCopy(dv[1].xyz, clipPoints[0][0]);
					VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0]);
					VectorCopy(dv[cv->width].xyz, clipPoints[0][1]);
					VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]);
					VectorCopy(dv[cv->width+1].xyz, clipPoints[0][2]);
					VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[cv->width+1].normal, clipPoints[0][2]);
					// check the normal of this triangle
					VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
					VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
					CrossProduct(v1, v2, normal);
					VectorNormalizeFast(normal);
					if (DotProduct(normal, projectionDir) < -0.05) {
						// add the fragments of this triangle
						R_AddMarkFragments(numClipPoints, clipPoints,
										   numPlanes, normals, dists,
										   maxPoints, pointBuffer,
										   maxFragments, fragmentBuffer,
										   &returnedPoints, &returnedFragments, mins, maxs);

						if ( returnedFragments == maxFragments ) {
							return returnedFragments;	// not enough space for more fragments
						}
					}
				}
			}
		}
		else if (*surfaces[i] == SF_FACE) {

			surf = ( srfSurfaceFace_t * ) surfaces[i];
			// check the normal of this face
			if (DotProduct(surf->plane.normal, projectionDir) > -0.5) {
				continue;
			}

			/*
			VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
			VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
			CrossProduct(v1, v2, normal);
			VectorNormalize(normal);
			if (DotProduct(normal, projectionDir) > -0.5) continue;
			*/
#ifdef _XBOX
			const unsigned char * const indexes = (unsigned char *)( (byte *)surf + surf->ofsIndices );
			int nextSurfPoint = NEXT_SURFPOINT(surf->flags);
#else
			indexes = (int *)( (byte *)surf + surf->ofsIndices );
#endif
			for ( k = 0 ; k < surf->numIndices ; k += 3 ) {
				for ( j = 0 ; j < 3 ; j++ ) {
#ifdef _XBOX
					const unsigned short* v = surf->srfPoints + nextSurfPoint * indexes[k+j];
					float fVec[3];
					Q_CastShort2Float(&fVec[0], (short*)v + 0);
					Q_CastShort2Float(&fVec[1], (short*)v + 1);
					Q_CastShort2Float(&fVec[2], (short*)v + 2);
					VectorMA( fVec, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] );
#else
					v = surf->points[0] + VERTEXSIZE * indexes[k+j];;
					VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] );
#endif
				}
				// add the fragments of this face
				R_AddMarkFragments( 3 , clipPoints,
								   numPlanes, normals, dists,
								   maxPoints, pointBuffer,
								   maxFragments, fragmentBuffer,
								   &returnedPoints, &returnedFragments, mins, maxs);
				if ( returnedFragments == maxFragments ) {
					return returnedFragments;	// not enough space for more fragments
				}
			}
			continue;
		}
		else {
			// ignore all other world surfaces
			// might be cool to also project polygons on a triangle soup
			// however this will probably create huge amounts of extra polys
			// even more than the projection onto curves
			continue;
		}
	}
	return returnedFragments;
}
Beispiel #29
0
/*
=================
CreateBrushFaces
=================
*/
void CreateBrushFaces (void)
{
	int				i,j, k;
	vec_t			r;
	face_t			*f, *next;
	winding_t		*w;
	plane_t			clipplane, faceplane;
	mface_t			*mf;
	vec3_t			offset, point;

	offset[0] = offset[1] = offset[2] = 0;
	ClearBounds( brush_mins, brush_maxs );

	brush_faces = NULL;

	if (!strncmp(ValueForKey(CurrentEntity, "classname"), "rotate_", 7))
	{
		entity_t	*FoundEntity;
		char 		*searchstring;
		char		text[20];

		searchstring = ValueForKey (CurrentEntity, "target");
		FoundEntity = FindTargetEntity(searchstring);
		if (FoundEntity)
			GetVectorForKey(FoundEntity, "origin", offset);

		sprintf(text, "%g %g %g", offset[0], offset[1], offset[2]);
		SetKeyValue(CurrentEntity, "origin", text);
	}

	GetVectorForKey(CurrentEntity, "origin", offset);
	//printf("%i brushfaces at offset %f %f %f\n", numbrushfaces, offset[0], offset[1], offset[2]);

	for (i = 0;i < numbrushfaces;i++)
	{
		mf = &faces[i];

		//printf("plane %f %f %f %f\n", mf->plane.normal[0], mf->plane.normal[1], mf->plane.normal[2], mf->plane.dist);
		faceplane = mf->plane;
		w = BaseWindingForPlane (&faceplane);

		//VectorNegate( faceplane.normal, point );
		for (j = 0;j < numbrushfaces && w;j++)
		{
			clipplane = faces[j].plane;
			if( j == i/* || VectorCompare( clipplane.normal, point )*/ )
				continue;

			// flip the plane, because we want to keep the back side
			VectorNegate(clipplane.normal, clipplane.normal);
			clipplane.dist *= -1;

			w = ClipWindingEpsilon (w, &clipplane, ON_EPSILON, true);
		}

		if (!w)
		{
			//printf("----- skipped plane -----\n");
			continue;	// overcontrained plane
		}

		// this face is a keeper
		f = AllocFace ();
		f->winding = w;

		for (j = 0;j < w->numpoints;j++)
		{
			for (k = 0;k < 3;k++)
			{
				point[k] = w->points[j][k] - offset[k];
				r = Q_rint( point[k] );
				if ( fabs( point[k] - r ) < ZERO_EPSILON)
					w->points[j][k] = r;
				else
					w->points[j][k] = point[k];

				// check for incomplete brushes
				if( w->points[j][k] >= BOGUS_RANGE || w->points[j][k] <= -BOGUS_RANGE )
					break;
			}

			// remove this brush
			if (k < 3)
			{
				FreeFace (f);
				for (f = brush_faces; f; f = next)
				{
					next = f->next;
					FreeFace (f);
				}
				brush_faces = NULL;
				//printf("----- skipped brush -----\n");
				return;
			}

			AddPointToBounds( w->points[j], brush_mins, brush_maxs );
		}

		CheckWinding( w );

		faceplane.dist -= DotProduct(faceplane.normal, offset);
		f->texturenum = mf->texinfo;
		f->planenum = FindPlane (&faceplane, &f->planeside);
		f->next = brush_faces;
		brush_faces = f;
	}

	// Rotatable objects have to have a bounding box big enough
	// to account for all its rotations.
	if (DotProduct(offset, offset))
	{
		vec_t delta;

		delta = RadiusFromBounds( brush_mins, brush_maxs );

		for (k = 0;k < 3;k++)
		{
			brush_mins[k] = -delta;
			brush_maxs[k] = delta;
		}
	}

	//printf("%i : %f %f %f : %f %f %f\n", numbrushfaces, brush_mins[0], brush_mins[1], brush_mins[2], brush_maxs[0], brush_maxs[1], brush_maxs[2]);
}
Beispiel #30
0
/*
RE_ProjectDecal()
creates a new decal projector from a triangle
projected polygons should be 3 or 4 points
if a single point is passed in (numPoints == 1) then the decal will be omnidirectional
omnidirectional decals use points[ 0 ] as center and projection[ 3 ] as radius
pass in lifeTime < 0 for a temporary mark
*/
void RE_ProjectDecal(qhandle_t hShader, int numPoints, vec3_t *points, vec4_t projection, vec4_t color, int lifeTime,
                     int fadeTime)
{
	int              i;
	float            radius, iDist;
	vec3_t           xyz;
	vec4_t           omniProjection;
	decalVert_t      dv[4];
	decalProjector_t *dp, temp;

	// first frame rendered does not have a valid decals list
	if (tr.refdef.decalProjectors == NULL)
	{
		return;
	}

	// dummy check
	if (numPoints != 1 && numPoints != 3 && numPoints != 4)
	{
		ri.Printf(PRINT_WARNING, "WARNING: Invalid number of decal points (%d)\n", numPoints);
		return;
	}

	// early outs
	if (lifeTime == 0)
	{
		return;
	}
	if (projection[3] <= 0.0f)
	{
		return;
	}

	// set times properly
	if (lifeTime < 0 || fadeTime < 0)
	{
		lifeTime = 0;
		fadeTime = 0;
	}

	// basic setup
	temp.shader        = R_GetShaderByHandle(hShader); // debug code
	temp.numPlanes     = temp.shader->entityMergable;
	temp.color[0]      = color[0] * 255;
	temp.color[1]      = color[1] * 255;
	temp.color[2]      = color[2] * 255;
	temp.color[3]      = color[3] * 255;
	temp.numPlanes     = numPoints + 2;
	temp.fadeStartTime = tr.refdef.time + lifeTime - fadeTime;
	temp.fadeEndTime   = temp.fadeStartTime + fadeTime;

	// set up decal texcoords (fixme: support arbitrary projector st coordinates in trapcall)
	dv[0].st[0] = 0.0f;
	dv[0].st[1] = 0.0f;
	dv[1].st[0] = 0.0f;
	dv[1].st[1] = 1.0f;
	dv[2].st[0] = 1.0f;
	dv[2].st[1] = 1.0f;
	dv[3].st[0] = 1.0f;
	dv[3].st[1] = 0.0f;

	// omnidirectional?
	if (numPoints == 1)
	{
		// set up omnidirectional
		numPoints            = 4;
		temp.numPlanes       = 6;
		temp.omnidirectional = qtrue;
		radius               = projection[3];
		Vector4Set(omniProjection, 0.0f, 0.0f, -1.0f, radius * 2.0f);
		projection = omniProjection;
		iDist      = 1.0f / (radius * 2.0f);

		// set corner
		VectorSet(xyz, points[0][0] - radius, points[0][1] - radius, points[0][2] + radius);

		// make x axis texture matrix (yz)
		VectorSet(temp.texMat[0][0], 0.0f, iDist, 0.0f);
		temp.texMat[0][0][3] = -DotProduct(temp.texMat[0][0], xyz);
		VectorSet(temp.texMat[0][1], 0.0f, 0.0f, iDist);
		temp.texMat[0][1][3] = -DotProduct(temp.texMat[0][1], xyz);

		// make y axis texture matrix (xz)
		VectorSet(temp.texMat[1][0], iDist, 0.0f, 0.0f);
		temp.texMat[1][0][3] = -DotProduct(temp.texMat[1][0], xyz);
		VectorSet(temp.texMat[1][1], 0.0f, 0.0f, iDist);
		temp.texMat[1][1][3] = -DotProduct(temp.texMat[1][1], xyz);

		// make z axis texture matrix (xy)
		VectorSet(temp.texMat[2][0], iDist, 0.0f, 0.0f);
		temp.texMat[2][0][3] = -DotProduct(temp.texMat[2][0], xyz);
		VectorSet(temp.texMat[2][1], 0.0f, iDist, 0.0f);
		temp.texMat[2][1][3] = -DotProduct(temp.texMat[2][1], xyz);

		// setup decal points
		VectorSet(dv[0].xyz, points[0][0] - radius, points[0][1] - radius, points[0][2] + radius);
		VectorSet(dv[1].xyz, points[0][0] - radius, points[0][1] + radius, points[0][2] + radius);
		VectorSet(dv[2].xyz, points[0][0] + radius, points[0][1] + radius, points[0][2] + radius);
		VectorSet(dv[3].xyz, points[0][0] + radius, points[0][1] - radius, points[0][2] + radius);
	}
	else
	{
		// set up unidirectional
		temp.omnidirectional = qfalse;

		// set up decal points
		VectorCopy(points[0], dv[0].xyz);
		VectorCopy(points[1], dv[1].xyz);
		VectorCopy(points[2], dv[2].xyz);
		VectorCopy(points[3], dv[3].xyz);

		// make texture matrix
		if (!MakeTextureMatrix(temp.texMat[0], projection, &dv[0], &dv[1], &dv[2]))
		{
			return;
		}
	}

	// bound the projector
	ClearBounds(temp.mins, temp.maxs);
	for (i = 0; i < numPoints; i++)
	{
		AddPointToBounds(dv[i].xyz, temp.mins, temp.maxs);
		VectorMA(dv[i].xyz, projection[3], projection, xyz);
		AddPointToBounds(xyz, temp.mins, temp.maxs);
	}

	// make bounding sphere
	VectorAdd(temp.mins, temp.maxs, temp.center);
	VectorScale(temp.center, 0.5f, temp.center);
	VectorSubtract(temp.maxs, temp.center, xyz);
	temp.radius  = VectorLength(xyz);
	temp.radius2 = temp.radius * temp.radius;

	// frustum cull the projector (fixme: this uses a stale frustum!)
	if (R_CullPointAndRadius(temp.center, temp.radius) == CULL_OUT)
	{
		return;
	}

	// make the front plane
	if (!PlaneFromPoints(temp.planes[0], dv[0].xyz, dv[1].xyz, dv[2].xyz))
	{
		return;
	}

	// make the back plane
	VectorSubtract(vec3_origin, temp.planes[0], temp.planes[1]);
	VectorMA(dv[0].xyz, projection[3], projection, xyz);
	temp.planes[1][3] = DotProduct(xyz, temp.planes[1]);

	// make the side planes
	for (i = 0; i < numPoints; i++)
	{
		VectorMA(dv[i].xyz, projection[3], projection, xyz);
		if (!PlaneFromPoints(temp.planes[i + 2], dv[(i + 1) % numPoints].xyz, dv[i].xyz, xyz))
		{
			return;
		}
	}

	// create a new projector
	dp = &tr.refdef.decalProjectors[r_numDecalProjectors & DECAL_PROJECTOR_MASK];
	Com_Memcpy(dp, &temp, sizeof(*dp));

	// we have a winner
	r_numDecalProjectors++;
}