Beispiel #1
0
/**
 * @brief ParseVector
 * @param[in,out] text
 * @param[in] count
 * @param[out] v
 * @return qtrue for success else qfalse
 */
static qboolean ParseVector(char **text, int count, float *v)
{
	char *token;
	int  i;

	token = COM_ParseExt(text, qfalse);
	if (strcmp(token, "("))
	{
		Ren_Warning("WARNING: missing parenthesis '(' in shader '%s' of token '%s'\n", shader.name, token);
		return qfalse;
	}

	for (i = 0; i < count; i++)
	{
		token = COM_ParseExt(text, qfalse);
		if (!token[0])
		{
			Ren_Warning("WARNING: missing vector element in shader '%s' - no token\n", shader.name);
			return qfalse;
		}
		v[i] = atof(token);
	}

	token = COM_ParseExt(text, qfalse);
	if (strcmp(token, ")"))
	{
		Ren_Warning("WARNING: missing parenthesis ')' in shader '%s' of token '%s'\n", shader.name, token);
		return qfalse;
	}

	return qtrue;
}
Beispiel #2
0
qboolean R_CheckFBO(const FBO_t *fbo)
{
	int code;
	int id;

	glGetIntegerv(GL_FRAMEBUFFER_BINDING, &id);
	glBindFramebuffer(GL_FRAMEBUFFER, fbo->frameBuffer);

	code = glCheckFramebufferStatus(GL_FRAMEBUFFER);

	if (code == GL_FRAMEBUFFER_COMPLETE)
	{
		glBindFramebuffer(GL_FRAMEBUFFER_EXT, id);
		return qtrue;
	}

	// an error occured
	switch (code)
	{
	case GL_FRAMEBUFFER_COMPLETE:
		break;
	case GL_FRAMEBUFFER_UNSUPPORTED:
		Ren_Warning("R_CheckFBO: (%s) Unsupported framebuffer format\n", fbo->name);
		break;
	case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
		Ren_Warning("R_CheckFBO: (%s) Framebuffer incomplete attachment\n", fbo->name);
		break;
	case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
		Ren_Warning("R_CheckFBO: (%s) Framebuffer incomplete, missing attachment\n", fbo->name);
		break;
	//case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
	//  Ren_Warning( "R_CheckFBO: (%s) Framebuffer incomplete, duplicate attachment\n", fbo->name);
	//  break;
	case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
		Ren_Warning("R_CheckFBO: (%s) Framebuffer incomplete, attached images must have same dimensions\n",
		            fbo->name);
		break;
	case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
		Ren_Warning("R_CheckFBO: (%s) Framebuffer incomplete, attached images must have same format\n",
		            fbo->name);
		break;
	case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
		Ren_Warning("R_CheckFBO: (%s) Framebuffer incomplete, missing draw buffer\n", fbo->name);
		break;
	case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
		Ren_Warning("R_CheckFBO: (%s) Framebuffer incomplete, missing read buffer\n", fbo->name);
		break;
	default:
		Ren_Warning("R_CheckFBO: (%s) unknown error 0x%X\n", fbo->name, code);
		//Ren_Fatal( "R_CheckFBO: (%s) unknown error 0x%X", fbo->name, code);
		//assert(0);
		break;
	}

	glBindFramebuffer(GL_FRAMEBUFFER, id);

	return qfalse;
}
Beispiel #3
0
/*
==============
RE_BlendSkeleton
==============
*/
int RE_BlendSkeleton(refSkeleton_t *skel, const refSkeleton_t *blend, float frac)
{
	int    i;
	vec3_t lerpedOrigin;
	quat_t lerpedQuat;
	vec3_t bounds[2];

	if (skel->numBones != blend->numBones)
	{
		Ren_Warning("RE_BlendSkeleton: different number of bones %d != %d\n", skel->numBones, blend->numBones);
		return qfalse;
	}

	// lerp between the 2 bone poses
	for (i = 0; i < skel->numBones; i++)
	{
		VectorLerp(skel->bones[i].origin, blend->bones[i].origin, frac, lerpedOrigin);
		quat_slerp(skel->bones[i].rotation, blend->bones[i].rotation, frac, lerpedQuat);

		VectorCopy(lerpedOrigin, skel->bones[i].origin);
		quat_copy(lerpedQuat, skel->bones[i].rotation);
	}

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

	return qtrue;
}
Beispiel #4
0
/**
 * @brief TableForFunc
 * @param[in] func
 * @return
 */
static float *TableForFunc(genFunc_t func)
{
	switch (func)
	{
	case GF_SIN:
		return tr.sinTable;
	case GF_TRIANGLE:
		return tr.triangleTable;
	case GF_SQUARE:
		return tr.squareTable;
	case GF_SAWTOOTH:
		return tr.sawToothTable;
	case GF_INVERSE_SAWTOOTH:
		return tr.inverseSawToothTable;
	case GF_NOISE:
		return tr.noiseTable;
	case GF_NONE:
	default:
		break;
	}

#if 0
	Ren_Drop("TableForFunc called with invalid function '%d' in shader '%s'\n", func, tess.surfaceShader->name);
	return NULL;
#else
	// FIXME
	Ren_Warning("TableForFunc called with invalid function '%d' in shader '%s'\n", func, tess.surfaceShader->name);
	return tr.sinTable;
#endif
}
Beispiel #5
0
/**
 * @brief GL_Bind
 * @param[in,out] image
 */
void GL_Bind(image_t *image)
{
	int texnum;

	if (!image)
	{
		Ren_Warning("GL_Bind: NULL image\n");
		texnum = tr.defaultImage->texnum;
	}
	else
	{
		texnum = image->texnum;
	}

	if (r_noBind->integer && tr.dlightImage)            // performance evaluation option
	{
		texnum = tr.dlightImage->texnum;
	}

	if (glState.currenttextures[glState.currenttmu] != texnum)
	{
		if (image)
		{
			image->frameUsed = tr.frameCount;
		}

		glState.currenttextures[glState.currenttmu] = texnum;
		qglBindTexture(GL_TEXTURE_2D, texnum);
	}
}
Beispiel #6
0
void R_CreateFBODepthBuffer(FBO_t *fbo, int format)
{
	qboolean      absent;
	BufferImage_t *bufferImage;

	if (format != GL_DEPTH_COMPONENT &&
	    format != GL_DEPTH_COMPONENT16_ARB && format != GL_DEPTH_COMPONENT24_ARB && format != GL_DEPTH_COMPONENT32_ARB)
	{
		Ren_Warning("R_CreateFBODepthBuffer: format %i is not depth-renderable\n", format);
		return;
	}

	bufferImage = &fbo->depthBuffer;

	bufferImage->format = format;

	absent = bufferImage->buffer == 0;
	if (absent)
	{
		glGenRenderbuffers(1, &bufferImage->buffer);
	}

	glBindRenderbuffer(GL_RENDERBUFFER, bufferImage->buffer);
	glRenderbufferStorage(GL_RENDERBUFFER, format, fbo->width, fbo->height);

	if (absent)
	{
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, bufferImage->buffer);
	}

	GL_CheckErrors();
}
Beispiel #7
0
void R_CreateFBOPackedDepthStencilBuffer(FBO_t *fbo, int format)
{
	qboolean      absent;
	BufferImage_t *bufferImage;

	if (format != GL_DEPTH_STENCIL && format != GL_DEPTH24_STENCIL8)
	{
		Ren_Warning("R_CreateFBOPackedDepthStencilBuffer: format %i is not depth-stencil-renderable\n", format);
		return;
	}

	bufferImage         = &fbo->packedDepthStencilBuffer;
	bufferImage->format = format;

	absent = bufferImage->buffer == 0;
	if (absent)
	{
		glGenRenderbuffers(1, &bufferImage->buffer);
	}

	glBindRenderbuffer(GL_RENDERBUFFER, bufferImage->buffer);
	glRenderbufferStorage(GL_RENDERBUFFER, format, fbo->width, fbo->height);
	GL_CheckErrors();

	if (absent)
	{
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, bufferImage->buffer);
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, bufferImage->buffer);
	}

	GL_CheckErrors();
}
Beispiel #8
0
void R_AttachFBOTexture2D(int target, int texId, int index)
{
	if (target != GL_TEXTURE_2D && (target < GL_TEXTURE_CUBE_MAP_POSITIVE_X || target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
	{
		Ren_Warning("R_AttachFBOTexture2D: invalid target %i\n", target);
		return;
	}

	if (index < 0 || index >= glConfig2.maxColorAttachments)
	{
		Ren_Warning("R_AttachFBOTexture2D: invalid attachment index %i\n", index);
		return;
	}

	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, target, texId, 0);
}
Beispiel #9
0
void GL_Bind(image_t *image)
{
	int texnum;

	if (!image)
	{
		Ren_Warning("GL_Bind: NULL image\n");
		image = tr.defaultImage;
	}
	else
	{
		Ren_LogComment("--- GL_Bind( %s ) ---\n", image->name);
	}

	texnum = image->texnum;

	if (r_nobind->integer && tr.blackImage)
	{
		// performance evaluation option
		texnum = tr.blackImage->texnum;
		image  = tr.blackImage;
	}

	if (glState.currenttextures[glState.currenttmu] != texnum)
	{
		image->frameUsed                            = tr.frameCount;
		glState.currenttextures[glState.currenttmu] = texnum;
		glBindTexture(image->type, texnum);
	}
}
Beispiel #10
0
void R_CreateFBOStencilBuffer(FBO_t *fbo, int format)
{
	qboolean absent;

	if (format != GL_STENCIL_INDEX &&
	    //format != GL_STENCIL_INDEX &&
	    format != GL_STENCIL_INDEX1 &&
	    format != GL_STENCIL_INDEX4 && format != GL_STENCIL_INDEX8 && format != GL_STENCIL_INDEX16)
	{
		Ren_Warning("R_CreateFBOStencilBuffer: format %i is not stencil-renderable\n", format);
		return;
	}

	fbo->stencilFormat = format;

	absent = fbo->stencilBuffer == 0;
	if (absent)
	{
		glGenRenderbuffers(1, &fbo->stencilBuffer);
	}

	glBindRenderbuffer(GL_RENDERBUFFER, fbo->stencilBuffer);
	glRenderbufferStorage(GL_RENDERBUFFER, fbo->stencilFormat, fbo->width, fbo->height);
	GL_CheckErrors();

	if (absent)
	{
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->stencilBuffer);
	}

	GL_CheckErrors();
}
Beispiel #11
0
void R_AttachFBOTexture3D(int texId, int index, int zOffset)
{
	if (index < 0 || index >= glConfig2.maxColorAttachments)
	{
		Ren_Warning("R_AttachFBOTexture3D: invalid attachment index %i\n", index);
		return;
	}

	glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, GL_TEXTURE_3D, texId, 0, zOffset);
}
Beispiel #12
0
/*
=================
R_XMLError
=================
*/
void R_XMLError(void *ctx, const char *fmt, ...)
{
	va_list     argptr;
	static char msg[4096];

	va_start(argptr, fmt);
	Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
	va_end(argptr);

	Ren_Warning("%s", msg);
}
Beispiel #13
0
/*
================
R_CreateFBOColorBuffer

Framebuffer must be bound
================
*/
void R_CreateFBOColorBuffer(FBO_t *fbo, int format, int index)
{
	qboolean      absent;
	BufferImage_t *bufferImage;

	if (index < 0 || index >= glConfig2.maxColorAttachments)
	{
		Ren_Warning("R_CreateFBOColorBuffer: invalid attachment index %i\n", index);
		return;
	}

#if 0
	if (format != GL_RGB &&
	    format != GL_RGBA &&
	    format != GL_RGB16F_ARB && format != GL_RGBA16F_ARB && format != GL_RGB32F_ARB && format != GL_RGBA32F_ARB)
	{
		Ren_Warning("R_CreateFBOColorBuffer: format %i is not color-renderable\n", format);
		//return;
	}
#endif
	bufferImage = &fbo->colorBuffers[index];

	bufferImage->format = format;

	absent = bufferImage->buffer == 0;
	if (absent)
	{
		glGenRenderbuffers(1, &bufferImage->buffer);
	}

	glBindRenderbuffer(GL_RENDERBUFFER, bufferImage->buffer);
	glRenderbufferStorage(GL_RENDERBUFFER, format, fbo->width, fbo->height);

	if (absent)
	{
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, GL_RENDERBUFFER, bufferImage->buffer);
	}

	GL_CheckErrors();
}
Beispiel #14
0
/*
=====================
RE_AddPolyBufferToScene
=====================
*/
void RE_AddPolyBufferToScene(polyBuffer_t *pPolyBuffer)
{
    srfPolyBuffer_t *pPolySurf;
    int             fogIndex;
    fog_t           *fog;
    vec3_t          bounds[2];
    int             i;

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

    if (r_numPolybuffers >= MAX_POLYBUFFERS)
    {
        Ren_Warning("WARNING RE_AddPolyBufferToScene: MAX_POLYBUFFERS (%d) reached\n", MAX_POLYBUFFERS);
        return;
    }

    pPolySurf = &backEndData->polybuffers[r_numPolybuffers];
    r_numPolybuffers++;

    pPolySurf->surfaceType = SF_POLYBUFFER;
    pPolySurf->pPolyBuffer = pPolyBuffer;

    VectorCopy(pPolyBuffer->xyz[0], bounds[0]);
    VectorCopy(pPolyBuffer->xyz[0], bounds[1]);
    for (i = 1; i < pPolyBuffer->numVerts; i++)
    {
        AddPointToBounds(pPolyBuffer->xyz[i], 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;
    }

    pPolySurf->fogIndex = fogIndex;
}
Beispiel #15
0
void R_LoadTGA(const char *name, byte **pic, int *width, int *height, byte alphaByte)
{
	unsigned columns, rows, numPixels;
	byte     *pixbuf;
	int      row, column;
	byte     *buf_p;
	byte     *end;
	union
	{
		byte *b;
		void *v;
	} buffer;
	TargaHeader targa_header;
	byte        *targa_rgba;
	int         length;

	*pic = NULL;

	if (width)
	{
		*width = 0;
	}
	if (height)
	{
		*height = 0;
	}

	//
	// load the file
	//
	length = ri.FS_ReadFile(( char * ) name, &buffer.v);
	if (!buffer.b || length < 0)
	{
		return;
	}

	if (length < 18)
	{
		ri.FS_FreeFile(buffer.v);
		Ren_Drop("LoadTGA: header too short (%s)\n", name);
	}

	buf_p = buffer.b;
	end   = buffer.b + length;

	targa_header.id_length     = buf_p[0];
	targa_header.colormap_type = buf_p[1];
	targa_header.image_type    = buf_p[2];

	memcpy(&targa_header.colormap_index, &buf_p[3], 2);
	memcpy(&targa_header.colormap_length, &buf_p[5], 2);
	targa_header.colormap_size = buf_p[7];
	memcpy(&targa_header.x_origin, &buf_p[8], 2);
	memcpy(&targa_header.y_origin, &buf_p[10], 2);
	memcpy(&targa_header.width, &buf_p[12], 2);
	memcpy(&targa_header.height, &buf_p[14], 2);
	targa_header.pixel_size = buf_p[16];
	targa_header.attributes = buf_p[17];

	targa_header.colormap_index  = LittleShort(targa_header.colormap_index);
	targa_header.colormap_length = LittleShort(targa_header.colormap_length);
	targa_header.x_origin        = LittleShort(targa_header.x_origin);
	targa_header.y_origin        = LittleShort(targa_header.y_origin);
	targa_header.width           = LittleShort(targa_header.width);
	targa_header.height          = LittleShort(targa_header.height);

	buf_p += 18;

	if (targa_header.image_type != 2
	    && targa_header.image_type != 10
	    && targa_header.image_type != 3)
	{
		ri.FS_FreeFile(buffer.v);
		Ren_Drop("LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n");
	}

	if (targa_header.colormap_type != 0)
	{
		ri.FS_FreeFile(buffer.v);
		Ren_Drop("LoadTGA: colormaps not supported\n");
	}

	if ((targa_header.pixel_size != 32 && targa_header.pixel_size != 24) && targa_header.image_type != 3)
	{
		ri.FS_FreeFile(buffer.v);
		Ren_Drop("LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
	}

	columns   = targa_header.width;
	rows      = targa_header.height;
	numPixels = columns * rows * 4;

	if (!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows)
	{
		ri.FS_FreeFile(buffer.v);
		Ren_Drop("LoadTGA: %s has an invalid image size\n", name);
	}

	targa_rgba = R_GetImageBuffer(numPixels, BUFFER_IMAGE, name);

	if (targa_header.id_length != 0)
	{
		if (buf_p + targa_header.id_length > end)
		{
			ri.Free(targa_rgba);
			ri.FS_FreeFile(buffer.v);
			Ren_Drop("LoadTGA: header too short (%s)\n", name);
		}

		buf_p += targa_header.id_length;  // skip TARGA image comment
	}

	if (targa_header.image_type == 2 || targa_header.image_type == 3)
	{
		if (buf_p + columns * rows * targa_header.pixel_size / 8 > end)
		{
			ri.Free(targa_rgba);
			ri.FS_FreeFile(buffer.v);
			Ren_Drop("LoadTGA: file truncated (%s)\n", name);
		}

		// Uncompressed RGB or gray scale image
		for (row = rows - 1; row >= 0; row--)
		{
			pixbuf = targa_rgba + row * columns * 4;
			for (column = 0; column < columns; column++)
			{
				unsigned char red, green, blue, alpha;
				switch (targa_header.pixel_size)
				{
				case 8:
					blue      = *buf_p++;
					green     = blue;
					red       = blue;
					*pixbuf++ = red;
					*pixbuf++ = green;
					*pixbuf++ = blue;
					*pixbuf++ = alphaByte;
					break;
				case 24:
					blue      = *buf_p++;
					green     = *buf_p++;
					red       = *buf_p++;
					*pixbuf++ = red;
					*pixbuf++ = green;
					*pixbuf++ = blue;
					*pixbuf++ = alphaByte;
					break;
				case 32:
					blue      = *buf_p++;
					green     = *buf_p++;
					red       = *buf_p++;
					alpha     = *buf_p++;
					*pixbuf++ = red;
					*pixbuf++ = green;
					*pixbuf++ = blue;
					*pixbuf++ = alpha;
					break;
				default:
					ri.Free(targa_rgba);
					ri.FS_FreeFile(buffer.v);
					Ren_Drop("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name);
					break;
				}
			}
		}
	}
	else if (targa_header.image_type == 10)   // Runlength encoded RGB images
	{
		unsigned char red, green, blue, alpha, packetHeader, packetSize, j;

		red   = 0;
		green = 0;
		blue  = 0;
		alpha = alphaByte;

		for (row = rows - 1; row >= 0; row--)
		{
			pixbuf = targa_rgba + row * columns * 4;
			for (column = 0; column < columns; )
			{
				if (buf_p + 1 > end)
				{
					Ren_Drop("LoadTGA: file truncated (%s)\n", name);
				}
				packetHeader = *buf_p++;
				packetSize   = 1 + (packetHeader & 0x7f);
				if (packetHeader & 0x80)          // run-length packet
				{
					if (buf_p + targa_header.pixel_size / 8 > end)
					{
						Ren_Drop("LoadTGA: file truncated (%s)\n", name);
					}
					switch (targa_header.pixel_size)
					{
					case 24:
						blue  = *buf_p++;
						green = *buf_p++;
						red   = *buf_p++;
						alpha = alphaByte;
						break;
					case 32:
						blue  = *buf_p++;
						green = *buf_p++;
						red   = *buf_p++;
						alpha = *buf_p++;
						break;
					default:
						ri.Free(targa_rgba);
						ri.FS_FreeFile(buffer.v);
						Ren_Drop("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name);
						break;
					}

					for (j = 0; j < packetSize; j++)
					{
						*pixbuf++ = red;
						*pixbuf++ = green;
						*pixbuf++ = blue;
						*pixbuf++ = alpha;
						column++;
						if (column == columns)   // run spans across rows
						{
							column = 0;
							if (row > 0)
							{
								row--;
							}
							else
							{
								goto breakOut;
							}
							pixbuf = targa_rgba + row * columns * 4;
						}
					}
				}
				else                              // non run-length packet
				{
					if (buf_p + targa_header.pixel_size / 8 * packetSize > end)
					{
						ri.Free(targa_rgba);
						ri.FS_FreeFile(buffer.v);
						Ren_Drop("LoadTGA: file truncated (%s)\n", name);
					}
					for (j = 0; j < packetSize; j++)
					{
						switch (targa_header.pixel_size)
						{
						case 24:
							blue      = *buf_p++;
							green     = *buf_p++;
							red       = *buf_p++;
							*pixbuf++ = red;
							*pixbuf++ = green;
							*pixbuf++ = blue;
							*pixbuf++ = alphaByte;
							break;
						case 32:
							blue      = *buf_p++;
							green     = *buf_p++;
							red       = *buf_p++;
							alpha     = *buf_p++;
							*pixbuf++ = red;
							*pixbuf++ = green;
							*pixbuf++ = blue;
							*pixbuf++ = alpha;
							break;
						default:
							ri.Free(targa_rgba);
							ri.FS_FreeFile(buffer.v);
							Ren_Drop("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name);
							break;
						}
						column++;
						if (column == columns)   // pixel packet run spans across rows
						{
							column = 0;
							if (row > 0)
							{
								row--;
							}
							else
							{
								goto breakOut;
							}
							pixbuf = targa_rgba + row * columns * 4;
						}
					}
				}
			}
breakOut:;
		}
	}

#if 1
	// this is the chunk of code to ensure a behavior that meets TGA specs
	// bk0101024 - fix from Leonardo
	// bit 5 set => top-down
	if (targa_header.attributes & 0x20)
	{
		unsigned char *flip;
		unsigned char *src, *dst;

		//Ren_Warning( "WARNING: '%s' TGA file header declares top-down image, flipping\n", name);

		flip = (unsigned char *)ri.Hunk_AllocateTempMemory(columns * 4);
		for (row = 0; row < rows / 2; row++)
		{
			src = targa_rgba + row * 4 * columns;
			dst = targa_rgba + (rows - row - 1) * 4 * columns;

			memcpy(flip, src, columns * 4);
			memcpy(src, dst, columns * 4);
			memcpy(dst, flip, columns * 4);
		}
		ri.Hunk_FreeTempMemory(flip);
	}
#else
	// instead we just print a warning
	if (targa_header.attributes & 0x20)
	{
		Ren_Warning("WARNING: '%s' TGA file header declares top-down image, ignoring\n", name);
	}
#endif

	if (width)
	{
		*width = columns;
	}
	if (height)
	{
		*height = rows;
	}

	*pic = targa_rgba;

	ri.FS_FreeFile(buffer.v);
}
Beispiel #16
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)
	{
		Ren_Warning("WARNING RE_AddPolysToScene: NULL poly shader\n");
		return;
	}

	for (j = 0; j < numPolys; j++)
	{
		if (r_numpolyverts + numVerts >= r_maxpolyverts->integer)
		{
			Ren_Developer("WARNING RE_AddPolysToScene: r_maxpolyverts[%i] reached. r_numpolyverts: %i - numVerts: %i - numPolys %i - shader %i\n", r_maxpolyverts->integer, r_numpolyverts, numVerts, numPolys, hShader);
			return;
		}
		if (r_numpolys >= r_maxpolys->integer)
		{
			Ren_Developer("WARNING RE_AddPolysToScene: r_maxpolys[%i] reached. r_numpolys: %i\n", r_maxpolys->integer, r_numpolys);
			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 * j], numVerts * sizeof(*verts));

		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 #17
0
/*
===============
RE_RegisterAnimation
===============
*/
qhandle_t RE_RegisterAnimation(const char *name)
{
	qhandle_t       hAnim;
	skelAnimation_t *anim;
	byte            *buffer;
	int             bufferLen;
	qboolean        loaded = qfalse;

	if (!name || !name[0])
	{
		Ren_Warning("Empty name passed to RE_RegisterAnimation\n");
		return 0;
	}

	//Ren_Print("RE_RegisterAnimation(%s)\n", name);

	if (strlen(name) >= MAX_QPATH)
	{
		Ren_Warning("Animation name exceeds MAX_QPATH\n");
		return 0;
	}

	// search the currently loaded animations
	for (hAnim = 1; hAnim < tr.numAnimations; hAnim++)
	{
		anim = tr.animations[hAnim];
		if (!Q_stricmp(anim->name, name))
		{
			if (anim->type == AT_BAD)
			{
				return 0;
			}
			return hAnim;
		}

		if (anim->type == AT_PSA && anim->psa)
		{
			const char *animName;

			animName = strstr(name, "::");

			//Ren_Print("animName = '%s'\n", animName ? (animName + 2) : NULL);
			if (animName && *(animName + 2) && !Q_stricmp(anim->psa->info.name, (animName + 2)))
			{
				return hAnim;
			}
		}
	}

	// allocate a new model_t
	if ((anim = R_AllocAnimation()) == NULL)
	{
		Ren_Warning("RE_RegisterAnimation: R_AllocAnimation() failed for '%s'\n", name);
		return 0;
	}

	// only set the name after the animation has been successfully allocated
	Q_strncpyz(anim->name, name, sizeof(anim->name));

	// make sure the render thread is stopped
	R_IssuePendingRenderCommands();

	// load and parse the .md5anim file
	bufferLen = ri.FS_ReadFile(name, (void **)&buffer);
	if (!buffer)
	{
		return 0;
	}

	if (!Q_stricmpn((const char *)buffer, "MD5Version", 10))
	{
		loaded = R_LoadMD5Anim(anim, buffer, bufferLen, name);
	}
	else if (!Q_stricmpn((const char *)buffer, "ANIMHEAD", 8))
	{
		loaded = R_LoadPSA(anim, buffer, bufferLen, name);
	}
	else
	{
		Ren_Warning("RE_RegisterAnimation: unknown fileid for '%s'\n", name);
	}

	ri.FS_FreeFile(buffer);

	if (!loaded)
	{
		Ren_Warning("couldn't load '%s'\n", name);

		// we still keep the model_t around, so if the model name is asked for
		// again, we won't bother scanning the filesystem
		anim->type = AT_BAD;
	}

	return anim->index;
}
Beispiel #18
0
qboolean R_LoadPSK(model_t *mod, void *buffer, int bufferSize, const char *modName)
{
	int         i, j, k;
	memStream_t *stream = NULL;

	axChunkHeader_t chunkHeader;

	int       numPoints;
	axPoint_t *point;
	axPoint_t *points = NULL;

	int        numVertexes;
	axVertex_t *vertex;
	axVertex_t *vertexes = NULL;

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

	int          numMaterials;
	axMaterial_t *material;
	axMaterial_t *materials = NULL;

	int               numReferenceBones;
	axReferenceBone_t *refBone;
	axReferenceBone_t *refBones = NULL;

	int            numWeights;
	axBoneWeight_t *axWeight;
	axBoneWeight_t *axWeights = NULL;

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

	vec3_t boneOrigin;
	quat_t boneQuat;
	//mat4_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];

	mat4_t unrealToQuake;

#define DeallocAll() Com_Dealloc(materials); \
	Com_Dealloc(points); \
	Com_Dealloc(vertexes); \
	Com_Dealloc(triangles); \
	Com_Dealloc(refBones); \
	Com_Dealloc(axWeights); \
	FreeMemStream(stream);

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

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

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

	PrintChunkHeader(&chunkHeader);

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

	// read points
	GetChunkHeader(stream, &chunkHeader);

	if (Q_stricmpn(chunkHeader.ident, "PNTS0000", 8))
	{
		Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "PNTS0000");
		DeallocAll();
		return qfalse;
	}

	if (chunkHeader.dataSize != sizeof(axPoint_t))
	{
		Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axPoint_t));
		DeallocAll();
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

	numPoints = chunkHeader.numData;
	points    = 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
		// 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))
	{
		Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "VTXW0000");
		DeallocAll();
		return qfalse;
	}

	if (chunkHeader.dataSize != sizeof(axVertex_t))
	{
		Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axVertex_t));
		DeallocAll();
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

	numVertexes = chunkHeader.numData;
	vertexes    = Com_Allocate(numVertexes * sizeof(axVertex_t));

	{
		int tmpVertexInt = -1; // tmp vertex member values - MemStreamGet functions return -1 if they fail
		                       // now we print a warning if they do or abort if pointIndex is invalid

		for (i = 0, vertex = vertexes; i < numVertexes; i++, vertex++)
		{
			tmpVertexInt = MemStreamGetShort(stream);
			if (tmpVertexInt < 0 || tmpVertexInt >= numPoints)
			{
				ri.Printf(PRINT_ERROR, "R_LoadPSK: '%s' has vertex with point index out of range (%i while max %i)\n", modName, tmpVertexInt, numPoints);
				DeallocAll();
				return qfalse;
			}
			vertex->pointIndex = tmpVertexInt;

			tmpVertexInt = MemStreamGetShort(stream);
			if (tmpVertexInt < 0)
			{
				Ren_Warning("R_LoadPSK: MemStream NULL or empty (vertex->unknownA)\n");
			}
			vertex->unknownA = tmpVertexInt;

			vertex->st[0] = MemStreamGetFloat(stream);
			if (vertex->st[0] == -1)
			{
				Ren_Warning("R_LoadPSK: MemStream possibly NULL or empty (vertex->st[0])\n");
			}

			vertex->st[1] = MemStreamGetFloat(stream);
			if (vertex->st[1] == -1)
			{
				Ren_Warning("R_LoadPSK: MemStream possibly NULL or empty (vertex->st[1])\n");
			}

			tmpVertexInt = MemStreamGetC(stream);
			if (tmpVertexInt < 0)
			{
				Ren_Warning("R_LoadPSK: MemStream NULL or empty (vertex->materialIndex)\n");
			}
			vertex->materialIndex = tmpVertexInt;

			tmpVertexInt = MemStreamGetC(stream);
			if (tmpVertexInt < 0)
			{
				Ren_Warning("R_LoadPSK: MemStream NULL or empty (vertex->materialIndex)\n");
			}
			vertex->reserved = tmpVertexInt;

			tmpVertexInt = MemStreamGetShort(stream);
			if (tmpVertexInt < 0)
			{
				Ren_Warning("R_LoadPSK: MemStream NULL or empty (vertex->materialIndex)\n");
			}
			vertex->unknownB = tmpVertexInt;
#if 0
			Ren_Print("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))
		{
			Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "FACE0000");
			DeallocAll();
			return qfalse;
		}

		if (chunkHeader.dataSize != sizeof(axTriangle_t))
		{
			Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axTriangle_t));
			DeallocAll();
			return qfalse;
		}

		PrintChunkHeader(&chunkHeader);

		numTriangles = chunkHeader.numData;
		triangles    = 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--)
			{
				tmpVertexInt = MemStreamGetShort(stream);

				if (tmpVertexInt < 0)
				{
					Ren_Warning("R_LoadPSK: '%s' MemStream NULL or empty (triangle->indexes[%i])\n", modName, j);
					DeallocAll();
					return qfalse;
				}

				if (tmpVertexInt >= numVertexes)
				{
					Ren_Warning("R_LoadPSK: '%s' has triangle with vertex index out of range (%i while max %i)\n", modName, tmpVertexInt, numVertexes);
					DeallocAll();
					return qfalse;
				}

				triangle->indexes[j] = tmpVertexInt;
			}

			triangle->materialIndex   = MemStreamGetC(stream);
			triangle->materialIndex2  = MemStreamGetC(stream);
			triangle->smoothingGroups = MemStreamGetLong(stream);
		}
	}
	// read materials
	GetChunkHeader(stream, &chunkHeader);

	if (Q_stricmpn(chunkHeader.ident, "MATT0000", 8))
	{
		Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "MATT0000");
		DeallocAll();
		return qfalse;
	}

	if (chunkHeader.dataSize != sizeof(axMaterial_t))
	{
		Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axMaterial_t));
		DeallocAll();
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

	numMaterials = chunkHeader.numData;
	materials    = Com_Allocate(numMaterials * sizeof(axMaterial_t));

	for (i = 0, material = materials; i < numMaterials; i++, material++)
	{
		MemStreamRead(stream, material->name, sizeof(material->name));

		Ren_Print("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)
		{
			Ren_Warning("R_LoadPSK: '%s' has vertex with material index out of range (%i while max %i)\n", modName, vertex->materialIndex, numMaterials);
			DeallocAll();
			return qfalse;
		}
	}

	for (i = 0, triangle = triangles; i < numTriangles; i++, triangle++)
	{
		if (triangle->materialIndex < 0 || triangle->materialIndex >= numMaterials)
		{
			Ren_Warning("R_LoadPSK: '%s' has triangle with material index out of range (%i while max %i)\n", modName, triangle->materialIndex, numMaterials);
			DeallocAll();
			return qfalse;
		}
	}

	// read reference bones
	GetChunkHeader(stream, &chunkHeader);

	if (Q_stricmpn(chunkHeader.ident, "REFSKELT", 8))
	{
		Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "REFSKELT");
		DeallocAll();
		return qfalse;
	}

	if (chunkHeader.dataSize != sizeof(axReferenceBone_t))
	{
		Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axReferenceBone_t));
		DeallocAll();
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

	numReferenceBones = chunkHeader.numData;
	refBones          = Com_Allocate(numReferenceBones * sizeof(axReferenceBone_t));

	for (i = 0, refBone = refBones; i < numReferenceBones; i++, refBone++)
	{
		MemStreamRead(stream, refBone->name, sizeof(refBone->name));

		//Ren_Print("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
		Ren_Print("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))
	{
		Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "RAWWEIGHTS");
		DeallocAll();
		return qfalse;
	}

	if (chunkHeader.dataSize != sizeof(axBoneWeight_t))
	{
		Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axBoneWeight_t));
		DeallocAll();
		return qfalse;
	}

	PrintChunkHeader(&chunkHeader);

	numWeights = chunkHeader.numData;
	axWeights  = 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
		Ren_Print("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)
	{
		Ren_Warning("R_LoadPSK: '%s' has no bones\n", modName);
		DeallocAll();
		return qfalse;
	}

	if (md5->numBones > MAX_BONES)
	{
		Ren_Warning("R_LoadPSK: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones);
		DeallocAll();
		return qfalse;
	}

	//Ren_Print("R_LoadPSK: '%s' has %i bones\n", modName, md5->numBones);

	// copy all reference bones
	md5->bones = 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;
		}

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

		if (md5Bone->parentIndex >= md5->numBones)
		{
			DeallocAll();
			Ren_Drop("R_LoadPSK: '%s' has bone '%s' with bad parent index %i while numBones is %i", modName,
			         md5Bone->name, md5Bone->parentIndex, md5->numBones);
		}

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

		// 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);

		quat_copy(boneQuat, md5Bone->rotation);

		//QuatClear(md5Bone->rotation);

#if 0
		Ren_Print("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);
			quat_copy(quat, md5Bone->rotation);
		}

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

#if 0
		Ren_Print("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 = 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)
		{
			DeallocAll();
			Ren_Drop("R_LoadPSK: vertex %i requires more weights %i than the maximum of %i in model '%s'", i, vboVert->numWeights, MAX_WEIGHTS, modName);
			//Ren_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 = 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 = 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
	Ren_Print("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 = Com_Allocate(sizeof(*sortTri));

		for (j = 0; j < 3; j++)
		{
			sortTri->indexes[j]  = triangle->indexes[j];
			sortTri->vertexes[j] = 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 = { 0, 0, 0 };
		vec3_t      binormal;
		vec3_t      normal;

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

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

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

			v0 = Com_GrowListElement(&vboVertexes, tri->indexes[0]);
			v1 = Com_GrowListElement(&vboVertexes, tri->indexes[1]);
			v2 = 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 = 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 = 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 = 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)
					{
						Ren_Print("R_LoadPSK: referenced bone: '%s'\n", (j < numReferenceBones) ? refBones[j].name : NULL);
					}
				}

				if (!vboTriangles.currentElements)
				{
					Ren_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 = Com_GrowListElement(&sortedTriangles, j);
		Com_Dealloc(sortTri);
	}

	Com_DestroyGrowList(&sortedTriangles);

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

	Com_DestroyGrowList(&vboVertexes);

	// 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);

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

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

	return qtrue;
}
Beispiel #19
0
static qboolean R_LoadMD5Anim(skelAnimation_t *skelAnim, byte *buffer, int bufferSize, const char *name)
{
	int            i, j;
	md5Animation_t *anim;
	md5Frame_t     *frame;
	md5Channel_t   *channel;
	char           *token;
	int            version;
	char           *buf_p = (char *)buffer;

	skelAnim->type = AT_MD5;
	skelAnim->md5  = anim = (md5Animation_t *)ri.Hunk_Alloc(sizeof(*anim), h_low);

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

	// check version
	token   = COM_ParseExt2(&buf_p, qfalse);
	version = atoi(token);
	if (version != MD5_VERSION)
	{
		Ren_Warning("RE_RegisterAnimation: '%s' has wrong version (%i should be %i)\n", name, version, MD5_VERSION);
		return qfalse;
	}

	// skip commandline <arguments string>
	token = COM_ParseExt2(&buf_p, qtrue);
	token = COM_ParseExt2(&buf_p, qtrue);

	// parse numFrames <number>
	token = COM_ParseExt2(&buf_p, qtrue);
	if (Q_stricmp(token, "numFrames"))
	{
		Ren_Warning("RE_RegisterAnimation: expected 'numFrames' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}
	token           = COM_ParseExt2(&buf_p, qfalse);
	anim->numFrames = atoi(token);

	// parse numJoints <number>
	token = COM_ParseExt2(&buf_p, qtrue);
	if (Q_stricmp(token, "numJoints"))
	{
		Ren_Warning("RE_RegisterAnimation: expected 'numJoints' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}
	token             = COM_ParseExt2(&buf_p, qfalse);
	anim->numChannels = atoi(token);

	// parse frameRate <number>
	token = COM_ParseExt2(&buf_p, qtrue);
	if (Q_stricmp(token, "frameRate"))
	{
		Ren_Warning("RE_RegisterAnimation: expected 'frameRate' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}
	token           = COM_ParseExt2(&buf_p, qfalse);
	anim->frameRate = atoi(token);

	// parse numAnimatedComponents <number>
	token = COM_ParseExt2(&buf_p, qtrue);
	if (Q_stricmp(token, "numAnimatedComponents"))
	{
		Ren_Warning("RE_RegisterAnimation: expected 'numAnimatedComponents' found '%s' in model '%s'\n", token,
		            name);
		return qfalse;
	}
	token                       = COM_ParseExt2(&buf_p, qfalse);
	anim->numAnimatedComponents = atoi(token);

	// parse hierarchy {
	token = COM_ParseExt2(&buf_p, qtrue);
	if (Q_stricmp(token, "hierarchy"))
	{
		Ren_Warning("RE_RegisterAnimation: expected 'hierarchy' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}
	token = COM_ParseExt2(&buf_p, qfalse);
	if (Q_stricmp(token, "{"))
	{
		Ren_Warning("RE_RegisterAnimation: expected '{' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}

	// parse all the channels
	anim->channels = (md5Channel_t *)ri.Hunk_Alloc(sizeof(md5Channel_t) * anim->numChannels, h_low);

	for (i = 0, channel = anim->channels; i < anim->numChannels; i++, channel++)
	{
		token = COM_ParseExt2(&buf_p, qtrue);
		Q_strncpyz(channel->name, token, sizeof(channel->name));

		//Ren_Print("RE_RegisterAnimation: '%s' has channel '%s'\n", name, channel->name);

		token                = COM_ParseExt2(&buf_p, qfalse);
		channel->parentIndex = atoi(token);

		if (channel->parentIndex >= anim->numChannels)
		{
			Ren_Drop("RE_RegisterAnimation: '%s' has channel '%s' with bad parent index %i while numBones is %i\n",
			         name, channel->name, channel->parentIndex, anim->numChannels);
		}

		token                   = COM_ParseExt2(&buf_p, qfalse);
		channel->componentsBits = atoi(token);

		token                     = COM_ParseExt2(&buf_p, qfalse);
		channel->componentsOffset = atoi(token);
	}

	// parse }
	token = COM_ParseExt2(&buf_p, qtrue);
	if (Q_stricmp(token, "}"))
	{
		Ren_Warning("RE_RegisterAnimation: expected '}' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}

	// parse bounds {
	token = COM_ParseExt2(&buf_p, qtrue);
	if (Q_stricmp(token, "bounds"))
	{
		Ren_Warning("RE_RegisterAnimation: expected 'bounds' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}
	token = COM_ParseExt2(&buf_p, qfalse);
	if (Q_stricmp(token, "{"))
	{
		Ren_Warning("RE_RegisterAnimation: expected '{' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}

	anim->frames = (md5Frame_t *)ri.Hunk_Alloc(sizeof(md5Frame_t) * anim->numFrames, h_low);

	for (i = 0, frame = anim->frames; i < anim->numFrames; i++, frame++)
	{
		// skip (
		token = COM_ParseExt2(&buf_p, qtrue);
		if (Q_stricmp(token, "("))
		{
			Ren_Warning("RE_RegisterAnimation: expected '(' found '%s' in model '%s'\n", token, name);
			return qfalse;
		}

		for (j = 0; j < 3; j++)
		{
			token               = COM_ParseExt2(&buf_p, qfalse);
			frame->bounds[0][j] = atof(token);
		}

		// skip )
		token = COM_ParseExt2(&buf_p, qfalse);
		if (Q_stricmp(token, ")"))
		{
			Ren_Warning("RE_RegisterAnimation: expected ')' found '%s' in model '%s'\n", token, name);
			return qfalse;
		}

		// skip (
		token = COM_ParseExt2(&buf_p, qfalse);
		if (Q_stricmp(token, "("))
		{
			Ren_Warning("RE_RegisterAnimation: expected '(' found '%s' in model '%s'\n", token, name);
			return qfalse;
		}

		for (j = 0; j < 3; j++)
		{
			token               = COM_ParseExt2(&buf_p, qfalse);
			frame->bounds[1][j] = atof(token);
		}

		// skip )
		token = COM_ParseExt2(&buf_p, qfalse);
		if (Q_stricmp(token, ")"))
		{
			Ren_Warning("RE_RegisterAnimation: expected ')' found '%s' in model '%s'\n", token, name);
			return qfalse;
		}
	}

	// parse }
	token = COM_ParseExt2(&buf_p, qtrue);
	if (Q_stricmp(token, "}"))
	{
		Ren_Warning("RE_RegisterAnimation: expected '}' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}

	// parse baseframe {
	token = COM_ParseExt2(&buf_p, qtrue);
	if (Q_stricmp(token, "baseframe"))
	{
		Ren_Warning("RE_RegisterAnimation: expected 'baseframe' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}
	token = COM_ParseExt2(&buf_p, qfalse);
	if (Q_stricmp(token, "{"))
	{
		Ren_Warning("RE_RegisterAnimation: expected '{' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}

	for (i = 0, channel = anim->channels; i < anim->numChannels; i++, channel++)
	{
		// skip (
		token = COM_ParseExt2(&buf_p, qtrue);
		if (Q_stricmp(token, "("))
		{
			Ren_Warning("RE_RegisterAnimation: expected '(' found '%s' in model '%s'\n", token, name);
			return qfalse;
		}

		for (j = 0; j < 3; j++)
		{
			token                  = COM_ParseExt2(&buf_p, qfalse);
			channel->baseOrigin[j] = atof(token);
		}

		// skip )
		token = COM_ParseExt2(&buf_p, qfalse);
		if (Q_stricmp(token, ")"))
		{
			Ren_Warning("RE_RegisterAnimation: expected ')' found '%s' in model '%s'\n", token, name);
			return qfalse;
		}

		// skip (
		token = COM_ParseExt2(&buf_p, qfalse);
		if (Q_stricmp(token, "("))
		{
			Ren_Warning("RE_RegisterAnimation: expected '(' found '%s' in model '%s'\n", token, name);
			return qfalse;
		}

		for (j = 0; j < 3; j++)
		{
			token                = COM_ParseExt2(&buf_p, qfalse);
			channel->baseQuat[j] = atof(token);
		}
		QuatCalcW(channel->baseQuat);

		// skip )
		token = COM_ParseExt2(&buf_p, qfalse);
		if (Q_stricmp(token, ")"))
		{
			Ren_Warning("RE_RegisterAnimation: expected ')' found '%s' in model '%s'\n", token, name);
			return qfalse;
		}
	}

	// parse }
	token = COM_ParseExt2(&buf_p, qtrue);
	if (Q_stricmp(token, "}"))
	{
		Ren_Warning("RE_RegisterAnimation: expected '}' found '%s' in model '%s'\n", token, name);
		return qfalse;
	}

	for (i = 0, frame = anim->frames; i < anim->numFrames; i++, frame++)
	{
		// parse frame <number> {
		token = COM_ParseExt2(&buf_p, qtrue);
		if (Q_stricmp(token, "frame"))
		{
			Ren_Warning("RE_RegisterAnimation: expected 'baseframe' found '%s' in model '%s'\n", token, name);
			return qfalse;
		}

		token = COM_ParseExt2(&buf_p, qfalse);
		if (Q_stricmp(token, va("%i", i)))
		{
			Ren_Warning("RE_RegisterAnimation: expected '%i' found '%s' in model '%s'\n", i, token, name);
			return qfalse;
		}

		token = COM_ParseExt2(&buf_p, qfalse);
		if (Q_stricmp(token, "{"))
		{
			Ren_Warning("RE_RegisterAnimation: expected '{' found '%s' in model '%s'\n", token, name);
			return qfalse;
		}

		frame->components = (float *)ri.Hunk_Alloc(sizeof(float) * anim->numAnimatedComponents, h_low);
		for (j = 0; j < anim->numAnimatedComponents; j++)
		{
			token                = COM_ParseExt2(&buf_p, qtrue);
			frame->components[j] = atof(token);
		}

		// parse }
		token = COM_ParseExt2(&buf_p, qtrue);
		if (Q_stricmp(token, "}"))
		{
			Ren_Warning("RE_RegisterAnimation: expected '}' found '%s' in model '%s'\n", token, name);
			return qfalse;
		}
	}

	// everything went ok
	return qtrue;
}
Beispiel #20
0
/**
 * @brief The current text pointer is at the explicit text definition of the
 * shader. Parse it into the global shader variable.  Later functions
 * will optimize it.
 * @param[in,out] _text
 * @return
 */
qboolean ParseShaderR1(char *_text)
{
	char **text = &_text;
	char *token;
	int  s = 0;

	shader.explicitlyDefined = qtrue;

	token = COM_ParseExt2(text, qtrue);

	if (token[0] != '{')
	{
		Ren_Warning("WARNING: expecting '{', found '%s' instead in shader '%s'\n", token, shader.name);
		return qfalse;
	}

	while (1)
	{
		token = COM_ParseExt2(text, qtrue);
		if (!token[0])
		{
			Ren_Warning("WARNING: no concluding '}' in shader %s\n", shader.name);
			return qfalse;
		}

		// end of shader definition
		if (token[0] == '}')
		{
			break;
		}
		// stage definition
		else if (token[0] == '{')
		{
			if (s >= MAX_SHADER_STAGES)
			{
				Ren_Warning("WARNING: too many stages in shader %s (max is %i)\n", shader.name, MAX_SHADER_STAGES);
				return qfalse;
			}

			if (!ParseStage(&stages[s], text))
			{
				Ren_Warning("WARNING: can't parse stages of shader %s @[%.50s ...]\n", shader.name, _text);
				return qfalse;
			}
			stages[s].active = qtrue;
			s++;
			continue;
		}
		// skip stuff that only the QuakeEdRadient needs
		else if (!Q_stricmpn(token, "qer", 3))
		{
			SkipRestOfLine(text);
			continue;
		}
		// skip description
		else if (!Q_stricmp(token, "description"))
		{
			SkipRestOfLine(text);
			continue;
		}
		// skip renderbump
		else if (!Q_stricmp(token, "renderbump"))
		{
			SkipRestOfLine(text);
			continue;
		}
		// skip unsmoothedTangents
		else if (!Q_stricmp(token, "unsmoothedTangents"))
		{
			Ren_Warning("WARNING: unsmoothedTangents keyword not supported in shader '%s'\n", shader.name);
			continue;
		}
		// skip guiSurf
		else if (!Q_stricmp(token, "guiSurf"))
		{
			SkipRestOfLine(text);
			continue;
		}
		// skip decalInfo
		else if (!Q_stricmp(token, "decalInfo"))
		{
			Ren_Warning("WARNING: decalInfo keyword not supported in shader '%s'\n", shader.name);
			SkipRestOfLine(text);
			continue;
		}
		// skip Quake4's extra material types
		else if (!Q_stricmp(token, "materialType"))
		{
			Ren_Warning("WARNING: materialType keyword not supported in shader '%s'\n", shader.name);
			SkipRestOfLine(text);
			continue;
		}
		// skip Prey's extra material types
		else if (!Q_stricmpn(token, "matter", 6))
		{
			//Ren_Warning( "WARNING: materialType keyword not supported in shader '%s'\n", shader.name);
			SkipRestOfLine(text);
			continue;
		}
		// sun parms
		else if (!Q_stricmp(token, "xmap_sun") || !Q_stricmp(token, "q3map_sun"))
		{
			float a, b;

			token = COM_ParseExt2(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: missing parm for 'xmap_sun' keyword in shader '%s'\n", shader.name);
				continue;
			}
			tr.sunLight[0] = atof(token);

			token = COM_ParseExt2(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: missing parm for 'xmap_sun' keyword in shader '%s'\n", shader.name);
				continue;
			}
			tr.sunLight[1] = atof(token);


			token = COM_ParseExt2(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: missing parm for 'xmap_sun' keyword in shader '%s'\n", shader.name);
				continue;
			}
			tr.sunLight[2] = atof(token);

			VectorNormalize(tr.sunLight);

			token = COM_ParseExt2(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: missing parm for 'xmap_sun' keyword in shader '%s'\n", shader.name);
				continue;
			}
			a = atof(token);
			VectorScale(tr.sunLight, a, tr.sunLight);

			token = COM_ParseExt2(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: missing parm for 'xmap_sun' keyword in shader '%s'\n", shader.name);
				continue;
			}
			a = atof(token);
			a = a / 180 * M_PI;

			token = COM_ParseExt2(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: missing parm for 'xmap_sun' keyword in shader '%s'\n", shader.name);
				continue;
			}
			b = atof(token);
			b = b / 180 * M_PI;

			tr.sunDirection[0] = cos(a) * cos(b);
			tr.sunDirection[1] = sin(a) * cos(b);
			tr.sunDirection[2] = sin(b);
			continue;
		}
		// noShadows
		else if (!Q_stricmp(token, "noShadows"))
		{
			shader.noShadows = qtrue;
			continue;
		}
		// noSelfShadow
		else if (!Q_stricmp(token, "noSelfShadow"))
		{
			Ren_Warning("WARNING: noSelfShadow keyword not supported in shader '%s'\n", shader.name);
			continue;
		}
		// forceShadows
		else if (!Q_stricmp(token, "forceShadows"))
		{
			Ren_Warning("WARNING: forceShadows keyword not supported in shader '%s'\n", shader.name);
			continue;
		}
		// forceOverlays
		else if (!Q_stricmp(token, "forceOverlays"))
		{
			Ren_Warning("WARNING: forceOverlays keyword not supported in shader '%s'\n", shader.name);
			continue;
		}
		// noPortalFog
		else if (!Q_stricmp(token, "noPortalFog"))
		{
			Ren_Warning("WARNING: noPortalFog keyword not supported in shader '%s'\n", shader.name);
			continue;
		}
		// fogLight
		else if (!Q_stricmp(token, "fogLight"))
		{
			Ren_Warning("WARNING: fogLight keyword not supported in shader '%s'\n", shader.name);
			shader.fogLight = qtrue;
			continue;
		}
		// blendLight
		else if (!Q_stricmp(token, "blendLight"))
		{
			Ren_Warning("WARNING: blendLight keyword not supported in shader '%s'\n", shader.name);
			shader.blendLight = qtrue;
			continue;
		}
		// ambientLight
		else if (!Q_stricmp(token, "ambientLight"))
		{
			Ren_Warning("WARNING: ambientLight keyword not supported in shader '%s'\n", shader.name);
			shader.ambientLight = qtrue;
			continue;
		}
		// volumetricLight
		else if (!Q_stricmp(token, "volumetricLight"))
		{
			shader.volumetricLight = qtrue;
			continue;
		}
		// translucent
		else if (!Q_stricmp(token, "translucent"))
		{
			shader.translucent = qtrue;
			continue;
		}
		// forceOpaque
		else if (!Q_stricmp(token, "forceOpaque"))
		{
			shader.forceOpaque = qtrue;
			continue;
		}
		// forceSolid
		else if (!Q_stricmp(token, "forceSolid") || !Q_stricmp(token, "solid"))
		{
			continue;
		}
		else if (!Q_stricmp(token, "deformVertexes") || !Q_stricmp(token, "deform"))
		{
			ParseDeform(text);
			continue;
		}
		else if (!Q_stricmp(token, "tesssize"))
		{
			SkipRestOfLine(text);
			continue;
		}
		// skip noFragment
		if (!Q_stricmp(token, "noFragment"))
		{
			continue;
		}
		// skip stuff that only the xmap needs
		else if (!Q_stricmpn(token, "xmap", 4) || !Q_stricmpn(token, "q3map", 5))
		{
			SkipRestOfLine(text);
			continue;
		}
		// skip stuff that only xmap or the server needs
		else if (!Q_stricmp(token, "surfaceParm"))
		{
			ParseSurfaceParm(text);
			continue;
		}
		// no mip maps
		else if (!Q_stricmp(token, "nomipmap") || !Q_stricmp(token, "nomipmaps"))
		{
			shader.filterType = FT_LINEAR;
			shader.noPicMip   = qtrue;
			continue;
		}
		// no picmip adjustment
		else if (!Q_stricmp(token, "nopicmip"))
		{
			shader.noPicMip = qtrue;
			continue;
		}
		// RF, allow each shader to permit compression if available
		else if (!Q_stricmp(token, "allowcompress"))
		{
			shader.uncompressed = qfalse;
			continue;
		}
		else if (!Q_stricmp(token, "nocompress"))
		{
			shader.uncompressed = qtrue;
			continue;
		}
		// polygonOffset
		else if (!Q_stricmp(token, "polygonOffset"))
		{
			shader.polygonOffset = qtrue;
			continue;
		}
		// parallax mapping
		else if (!Q_stricmp(token, "parallax"))
		{
			shader.parallax = qtrue;
			continue;
		}
		// entityMergable, allowing sprite surfaces from multiple entities
		// to be merged into one batch.  This is a savings for smoke
		// puffs and blood, but can't be used for anything where the
		// shader calcs (not the surface function) reference the entity color or scroll
		else if (!Q_stricmp(token, "entityMergable"))
		{
			shader.entityMergable = qtrue;
			continue;
		}
		// fogParms
		else if (!Q_stricmp(token, "fogParms"))
		{
			if (!ParseVector(text, 3, shader.fogParms.color))
			{
				return qfalse;
			}

			//shader.fogParms.colorInt = ColorBytes4(shader.fogParms.color[0] * tr.identityLight,
			//                                       shader.fogParms.color[1] * tr.identityLight,
			//                                       shader.fogParms.color[2] * tr.identityLight, 1.0);

			token = COM_ParseExt2(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: 'fogParms' incomplete - missing opacity value in shader '%s' set to 1\n", shader.name);
				shader.fogParms.depthForOpaque = 1;
			}
			else
			{
				shader.fogParms.depthForOpaque = atof(token);
				shader.fogParms.depthForOpaque = shader.fogParms.depthForOpaque < 1 ? 1 : shader.fogParms.depthForOpaque;
			}
			//shader.fogParms.tcScale = 1.0f / shader.fogParms.depthForOpaque;

			shader.fogVolume = qtrue;
			shader.sort      = SS_FOG;

			// skip any old gradient directions
			SkipRestOfLine(text);
			continue;
		}
		// noFog
		else if (!Q_stricmp(token, "noFog"))
		{
			shader.noFog = qtrue;
			continue;
		}
		// portal
		else if (!Q_stricmp(token, "portal"))
		{
			shader.sort     = SS_PORTAL;
			shader.isPortal = qtrue;

			token = COM_ParseExt2(text, qfalse);
			if (token[0])
			{
				shader.portalRange = atof(token);
			}
			else
			{
				shader.portalRange = 256;
			}
			continue;
		}
		// portal or mirror
		else if (!Q_stricmp(token, "mirror"))
		{
			shader.sort     = SS_PORTAL;
			shader.isPortal = qtrue;
			continue;
		}
		// skyparms <cloudheight> <outerbox> <innerbox>
		else if (!Q_stricmp(token, "skyparms"))
		{
			ParseSkyParms(text);
			continue;
		}
		// This is fixed fog for the skybox/clouds determined solely by the shader
		// it will not change in a level and will not be necessary
		// to force clients to use a sky fog the server says to.
		// skyfogvars <(r,g,b)> <dist>
		else if (!Q_stricmp(token, "skyfogvars"))
		{
			vec3_t fogColor;

			if (!ParseVector(text, 3, fogColor))
			{
				return qfalse;
			}
			token = COM_ParseExt(text, qfalse);

			if (!token[0])
			{
				Ren_Warning("WARNING: missing density value for sky fog\n");
				continue;
			}

			if (atof(token) > 1)
			{
				Ren_Warning("WARNING: last value for skyfogvars is 'density' which needs to be 0.0-1.0\n");
				continue;
			}

			RE_SetFog(FOG_SKY, 0, 5, fogColor[0], fogColor[1], fogColor[2], atof(token));

			continue;
		}
		// ET waterfogvars
		else if (!Q_stricmp(token, "waterfogvars"))
		{
			vec3_t watercolor;
			float  fogvar;

			if (!ParseVector(text, 3, watercolor))
			{
				return qfalse;
			}
			token = COM_ParseExt(text, qfalse);

			if (!token[0])
			{
				Ren_Warning("WARNING: missing density/distance value for water fog\n");
				continue;
			}

			fogvar = atof(token);

			// right now allow one water color per map.  I'm sure this will need
			//          to change at some point, but I'm not sure how to track fog parameters
			//          on a "per-water volume" basis yet.
			if (fogvar == 0)
			{                   // '0' specifies "use the map values for everything except the fog color
				// TODO
			}
			else if (fogvar > 1)
			{                   // distance "linear" fog
				RE_SetFog(FOG_WATER, 0, fogvar, watercolor[0], watercolor[1], watercolor[2], 1.1);
			}
			else
			{                   // density "exp" fog
				RE_SetFog(FOG_WATER, 0, 5, watercolor[0], watercolor[1], watercolor[2], fogvar);
			}
			continue;
		}
		// ET fogvars
		else if (!Q_stricmp(token, "fogvars"))
		{
			vec3_t fogColor;
			float  fogDensity;
			int    fogFar;

			if (!ParseVector(text, 3, fogColor))
			{
				return qfalse;
			}

			token = COM_ParseExt(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: missing density value for the fog\n");
				continue;
			}

			// NOTE:   fogFar > 1 means the shader is setting the farclip, < 1 means setting
			//         density (so old maps or maps that just need softening fog don't have to care about farclip)

			fogDensity = atof(token);
			if (fogDensity > 1)
			{                   // linear
				fogFar = fogDensity;
			}
			else
			{
				fogFar = 5;
			}

			RE_SetFog(FOG_MAP, 0, fogFar, fogColor[0], fogColor[1], fogColor[2], fogDensity);
			RE_SetFog(FOG_CMD_SWITCHFOG, FOG_MAP, 50, 0, 0, 0, 0);
			continue;
		}
		// ET sunshader <name>
		else if (!Q_stricmp(token, "sunshader"))
		{
			size_t tokenLen;

			token = COM_ParseExt2(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: missing shader name for 'sunshader'\n");
				continue;
			}

			// Don't call tr.sunShader = R_FindShader(token, SHADER_3D_STATIC, qtrue);
			// because it breaks the computation of the current shader
			tokenLen         = strlen(token) + 1;
			tr.sunShaderName = (char *)ri.Hunk_Alloc(sizeof(char) * tokenLen, h_low);
			Q_strncpyz(tr.sunShaderName, token, tokenLen);
		}
		else if (!Q_stricmp(token, "lightgridmulamb"))
		{
			// ambient multiplier for lightgrid
			token = COM_ParseExt2(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: missing value for 'lightgrid ambient multiplier'\n");
				continue;
			}
			if (atof(token) > 0)
			{
				tr.lightGridMulAmbient = atof(token);
			}
		}
		else if (!Q_stricmp(token, "lightgridmuldir"))
		{
			// directional multiplier for lightgrid
			token = COM_ParseExt2(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: missing value for 'lightgrid directional multiplier'\n");
				continue;
			}
			if (atof(token) > 0)
			{
				tr.lightGridMulDirected = atof(token);
			}
		}
		// light <value> determines flaring in xmap, not needed here
		else if (!Q_stricmp(token, "light"))
		{
			(void) COM_ParseExt2(text, qfalse);
			continue;
		}
		// cull <face>
		else if (!Q_stricmp(token, "cull"))
		{
			token = COM_ParseExt2(text, qfalse);
			if (token[0] == 0)
			{
				Ren_Warning("WARNING: missing cull parms in shader '%s'\n", shader.name);
				continue;
			}

			if (!Q_stricmp(token, "none") || !Q_stricmp(token, "twoSided") || !Q_stricmp(token, "disable"))
			{
				shader.cullType = CT_TWO_SIDED;
			}
			else if (!Q_stricmp(token, "back") || !Q_stricmp(token, "backside") || !Q_stricmp(token, "backsided"))
			{
				shader.cullType = CT_BACK_SIDED;
			}
			else if (!Q_stricmp(token, "front"))
			{
				// CT_FRONT_SIDED is set per default see R_FindShader - nothing to do just don't throw a warning
			}
			else
			{
				Ren_Warning("WARNING: invalid cull parm '%s' in shader '%s'\n", token, shader.name);
			}
			continue;
		}
		// distancecull <opaque distance> <transparent distance> <alpha threshold>
		else if (!Q_stricmp(token, "distancecull"))
		{
			int i;

			for (i = 0; i < 3; i++)
			{
				token = COM_ParseExt(text, qfalse);
				if (token[0] == 0)
				{
					Ren_Warning("WARNING: missing distancecull parms in shader '%s'\n", shader.name);
				}
				else
				{
					shader.distanceCull[i] = atof(token);
				}
			}

			if (shader.distanceCull[1] - shader.distanceCull[0] > 0)
			{
				// distanceCull[ 3 ] is an optimization
				shader.distanceCull[3] = 1.0f / (shader.distanceCull[1] - shader.distanceCull[0]);
			}
			else
			{
				shader.distanceCull[0] = 0;
				shader.distanceCull[1] = 0;
				shader.distanceCull[2] = 0;
				shader.distanceCull[3] = 0;
			}
			continue;
		}
		// twoSided
		else if (!Q_stricmp(token, "twoSided"))
		{
			shader.cullType = CT_TWO_SIDED;
			continue;
		}
		// backSided
		else if (!Q_stricmp(token, "backSided"))
		{
			shader.cullType = CT_BACK_SIDED;
			continue;
		}
		// clamp
		else if (!Q_stricmp(token, "clamp"))
		{
			shader.wrapType = WT_CLAMP;
			continue;
		}
		// edgeClamp
		else if (!Q_stricmp(token, "edgeClamp"))
		{
			shader.wrapType = WT_EDGE_CLAMP;
			continue;
		}
		// zeroClamp
		else if (!Q_stricmp(token, "zeroclamp"))
		{
			shader.wrapType = WT_ZERO_CLAMP;
			continue;
		}
		// alphaZeroClamp
		else if (!Q_stricmp(token, "alphaZeroClamp"))
		{
			shader.wrapType = WT_ALPHA_ZERO_CLAMP;
			continue;
		}
		// sort
		else if (!Q_stricmp(token, "sort"))
		{
			ParseSort(text);
			continue;
		}
		// implicit default mapping to eliminate redundant/incorrect explicit shader stages
		else if (!Q_stricmpn(token, "implicit", 8))
		{
			//Ren_Warning( "WARNING: keyword '%s' not supported in shader '%s'\n", token, shader.name);
			//SkipRestOfLine(text);

			// set implicit mapping state
			if (!Q_stricmp(token, "implicitBlend"))
			{
				implicitStateBits = GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
				implicitCullType  = CT_TWO_SIDED;
			}
			else if (!Q_stricmp(token, "implicitMask"))
			{
				implicitStateBits = GLS_DEPTHMASK_TRUE | GLS_ATEST_GE_128;
				implicitCullType  = CT_TWO_SIDED;
			}
			else                // "implicitMap"
			{
				implicitStateBits = GLS_DEPTHMASK_TRUE;
				implicitCullType  = CT_FRONT_SIDED;
			}

			// get image
			token = COM_ParseExt(text, qfalse);
			if (token[0] != '\0')
			{
				Q_strncpyz(implicitMap, token, sizeof(implicitMap));
			}
			else
			{
				implicitMap[0] = '-';
				implicitMap[1] = '\0';
			}

			continue;
		}
		// spectrum
		else if (!Q_stricmp(token, "spectrum"))
		{
			Ren_Warning("WARNING: spectrum keyword not supported in shader '%s'\n", shader.name);

			token = COM_ParseExt2(text, qfalse);
			if (!token[0])
			{
				Ren_Warning("WARNING: missing parm for 'spectrum' keyword in shader '%s'\n", shader.name);
				continue;
			}
			shader.spectrum      = qtrue;
			shader.spectrumValue = atoi(token);
			continue;
		}
		// diffuseMap <image>
		else if (!Q_stricmp(token, "diffuseMap"))
		{
			ParseDiffuseMap(&stages[s], text);
			s++;
			continue;
		}
		// normalMap <image>
		else if (!Q_stricmp(token, "normalMap") || !Q_stricmp(token, "bumpMap"))
		{
			ParseNormalMap(&stages[s], text);
			s++;
			continue;
		}
		// specularMap <image>
		else if (!Q_stricmp(token, "specularMap"))
		{
			ParseSpecularMap(&stages[s], text);
			s++;
			continue;
		}
		// glowMap <image>
		else if (!Q_stricmp(token, "glowMap"))
		{
			ParseGlowMap(&stages[s], text);
			s++;
			continue;
		}
		// reflectionMap <image>
		else if (!Q_stricmp(token, "reflectionMap"))
		{
			ParseReflectionMap(&stages[s], text);
			s++;
			continue;
		}
		// reflectionMapBlended <image>
		else if (!Q_stricmp(token, "reflectionMapBlended"))
		{
			ParseReflectionMapBlended(&stages[s], text);
			s++;
			continue;
		}
		// lightMap <image>
		else if (!Q_stricmp(token, "lightMap"))
		{
			Ren_Warning("WARNING: obsolete lightMap keyword not supported in shader '%s'\n", shader.name);
			SkipRestOfLine(text);
			continue;
		}
		// lightFalloffImage <image>
		else if (!Q_stricmp(token, "lightFalloffImage"))
		{
			ParseLightFalloffImage(&stages[s], text);
			s++;
			continue;
		}
		// Doom 3 DECAL_MACRO
		else if (!Q_stricmp(token, "DECAL_MACRO"))
		{
			shader.polygonOffset      = qtrue;
			shader.sort               = SS_DECAL;
			SurfaceParm("discrete");
			SurfaceParm("noShadows");
			continue;
		}
		// Prey DECAL_ALPHATEST_MACRO
		else if (!Q_stricmp(token, "DECAL_ALPHATEST_MACRO"))
		{
			// what's different?
			shader.polygonOffset      = qtrue;
			shader.sort               = SS_DECAL;
			SurfaceParm("discrete");
			SurfaceParm("noShadows");
			continue;
		}
		else if (SurfaceParm(token))
		{
			continue;
		}
		else
		{
			Ren_Warning("WARNING: unknown general shader parameter '%s' in '%s'\n", token, shader.name);
			SkipRestOfLine(text);
			continue;
		}
	}

	// ignore shaders that don't have any stages, unless it is a sky or fog
	if (s == 0 && !shader.forceOpaque && !shader.isSky && !(shader.contentFlags & CONTENTS_FOG) && implicitMap[0] == '\0')
	{
		return qfalse;
	}

	return qtrue;
}
Beispiel #21
0
/**
 * @brief Finds and loads all .shader files, combining them into
 * a single large text block that can be scanned for shader names
 */
int ScanAndLoadShaderFilesR1()
{
	char         **shaderFiles;
	char         *buffers[MAX_SHADER_FILES];
	char         *p;
	int          numShaderFiles, i;
	char         *oldp, *token, *textEnd;
	char         **hashMem;
	int          shaderTextHashTableSizes[MAX_SHADERTEXT_HASH], hash;
	unsigned int size;
	char         filename[MAX_QPATH];
	long         sum = 0, summand;

	Com_Memset(buffers, 0, MAX_SHADER_FILES);
	Com_Memset(shaderTextHashTableSizes, 0, MAX_SHADER_FILES);

	// scan for shader files
	shaderFiles = ri.FS_ListFiles("scripts", ".shader", &numShaderFiles);

	if (!shaderFiles || !numShaderFiles)
	{
		Ren_Print("----- ScanAndLoadShaderFilesR1 (no files)-----\n");
		return 0;
	}

	Ren_Print("----- ScanAndLoadShaderFilesR1 (%i files)-----\n", numShaderFiles);

	if (numShaderFiles >= MAX_SHADER_FILES)
	{
		Ren_Drop("MAX_SHADER_FILES limit is reached!");
	}

	// load and parse shader files
	for (i = 0; i < numShaderFiles; i++)
	{
		Com_sprintf(filename, sizeof(filename), "scripts/%s", shaderFiles[i]);
		COM_BeginParseSession(filename);

		Ren_Developer("...loading '%s'\n", filename);
		summand = ri.FS_ReadFile(filename, (void **)&buffers[i]);

		if (!buffers[i])
		{
			Ren_Drop("Couldn't load %s", filename); // in this case shader file is cought/listed but the file can't be read - drop!
		}

		p = buffers[i];
		while (1)
		{
			token = COM_ParseExt(&p, qtrue);

			if (!*token)
			{
				break;
			}

			// Step over the "table"/"guide" and the name
			if (!Q_stricmp(token, "table") || !Q_stricmp(token, "guide"))
			{
				token = COM_ParseExt2(&p, qtrue);

				if (!*token)
				{
					break;
				}
			}

			oldp = p;

			token = COM_ParseExt2(&p, qtrue);
			if (token[0] != '{' && token[1] != '\0')
			{
				Ren_Warning("WARNING: Bad shader file %s has incorrect syntax near token '%s' line %i\n", filename, token, COM_GetCurrentParseLine());
				ri.FS_FreeFile(buffers[i]);
				buffers[i] = NULL;
				break;
			}

			SkipBracedSection(&oldp);
			p = oldp;
		}

		if (buffers[i])
		{
			sum += summand;
		}
	}

	// build single large buffer
	s_shaderTextR1    = (char *)ri.Hunk_Alloc(sum + numShaderFiles * 2, h_low);
	s_shaderTextR1[0] = '\0';
	textEnd           = s_shaderTextR1;

	// free in reverse order, so the temp files are all dumped
	for (i = numShaderFiles - 1; i >= 0 ; i--)
	{
		if (!buffers[i])
		{
			continue;
		}

		strcat(textEnd, buffers[i]);
		strcat(textEnd, "\n");
		textEnd += strlen(textEnd);
		ri.FS_FreeFile(buffers[i]);
	}

	COM_Compress(s_shaderTextR1);

	// free up memory
	ri.FS_FreeFileList(shaderFiles);

	Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes));
	size = 0;

	p = s_shaderTextR1;
	// look for shader names
	while (1)
	{
		token = COM_ParseExt(&p, qtrue);
		if (token[0] == 0)
		{
			break;
		}

		// skip shader tables
		if (!Q_stricmp(token, "table"))
		{
			// skip table name
			(void) COM_ParseExt2(&p, qtrue);

			SkipBracedSection(&p);
		}
		// support shader templates
		else if (!Q_stricmp(token, "guide"))
		{
			// parse shader name
			token = COM_ParseExt2(&p, qtrue);
			//Ren_Print("...guided '%s'\n", token);

			hash = generateHashValue(token, MAX_SHADERTEXT_HASH);
			shaderTextHashTableSizes[hash]++;
			size++;

			// skip guide name
			token = COM_ParseExt2(&p, qtrue);

			// skip parameters
			token = COM_ParseExt2(&p, qtrue);
			if (Q_stricmp(token, "("))
			{
				Ren_Warning("expected ( found '%s'\n", token);
				break;
			}

			while (1)
			{
				token = COM_ParseExt2(&p, qtrue);

				if (!token[0])
				{
					break;
				}

				if (!Q_stricmp(token, ")"))
				{
					break;
				}
			}

			if (Q_stricmp(token, ")"))
			{
				Ren_Warning("expected ( found '%s'\n", token);
				break;
			}
		}
		else
		{
			hash = generateHashValue(token, MAX_SHADERTEXT_HASH);
			shaderTextHashTableSizes[hash]++;
			size++;
			SkipBracedSection(&p);
		}
	}

	//Ren_Print("Shader hash table size %i\n", size);

	size += MAX_SHADERTEXT_HASH;

	hashMem = (char **)ri.Hunk_Alloc(size * sizeof(char *), h_low);

	for (i = 0; i < MAX_SHADERTEXT_HASH; i++)
	{
		shaderTextHashTableR1[i] = hashMem;
		hashMem                 += shaderTextHashTableSizes[i] + 1;
	}

	Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes));

	p = s_shaderTextR1;

	// look for shader names
	while (1)
	{
		oldp  = p;
		token = COM_ParseExt(&p, qtrue);
		if (token[0] == 0)
		{
			break;
		}

		// parse shader tables
		if (!Q_stricmp(token, "table"))
		{
			int           depth;
			float         values[FUNCTABLE_SIZE];
			int           numValues;
			shaderTable_t *tb;
			qboolean      alreadyCreated;

			Com_Memset(&values, 0, sizeof(values));
			Com_Memset(&table, 0, sizeof(table));

			token = COM_ParseExt2(&p, qtrue);

			Q_strncpyz(table.name, token, sizeof(table.name));

			// check if already created
			alreadyCreated = qfalse;
			hash           = generateHashValue(table.name, MAX_SHADERTABLE_HASH);
			for (tb = shaderTableHashTable[hash]; tb; tb = tb->next)
			{
				if (Q_stricmp(tb->name, table.name) == 0)
				{
					// match found
					alreadyCreated = qtrue;
					break;
				}
			}

			depth     = 0;
			numValues = 0;
			do
			{
				token = COM_ParseExt2(&p, qtrue);

				if (!Q_stricmp(token, "snap"))
				{
					table.snap = qtrue;
				}
				else if (!Q_stricmp(token, "clamp"))
				{
					table.clamp = qtrue;
				}
				else if (token[0] == '{')
				{
					depth++;
				}
				else if (token[0] == '}')
				{
					depth--;
				}
				else if (token[0] == ',')
				{
					continue;
				}
				else
				{
					if (numValues == FUNCTABLE_SIZE)
					{
						Ren_Warning("WARNING: FUNCTABLE_SIZE hit\n");
						break;
					}
					values[numValues++] = atof(token);
				}
			}
			while (depth && p);

			if (!alreadyCreated)
			{
				Ren_Developer("...generating '%s'\n", table.name);
				GeneratePermanentShaderTable(values, numValues);
			}
		}
		// support shader templates
		else if (!Q_stricmp(token, "guide"))
		{
			// parse shader name
			oldp  = p;
			token = COM_ParseExt2(&p, qtrue);

			//Ren_Print("...guided '%s'\n", token);

			hash                                                          = generateHashValue(token, MAX_SHADERTEXT_HASH);
			shaderTextHashTableR1[hash][shaderTextHashTableSizes[hash]++] = oldp;

			// skip guide name
			token = COM_ParseExt2(&p, qtrue);

			// skip parameters
			token = COM_ParseExt2(&p, qtrue);
			if (Q_stricmp(token, "("))
			{
				Ren_Warning("expected ( found '%s'\n", token);
				break;
			}

			while (1)
			{
				token = COM_ParseExt2(&p, qtrue);

				if (!token[0])
				{
					break;
				}

				if (!Q_stricmp(token, ")"))
				{
					break;
				}
			}

			if (Q_stricmp(token, ")"))
			{
				Ren_Warning("expected ( found '%s'\n", token);
				break;
			}
		}
		else
		{
			hash                                                          = generateHashValue(token, MAX_SHADERTEXT_HASH);
			shaderTextHashTableR1[hash][shaderTextHashTableSizes[hash]++] = oldp;

			SkipBracedSection(&p);
		}
	}

	return numShaderFiles;
}
Beispiel #22
0
static void GLimp_DetectAvailableModes(void)
{
	int             i, j;
	char            buf[MAX_STRING_CHARS] = { 0 };
	SDL_Rect        modes[128];
	int             numModes = 0;
	int             display  = 0;
	SDL_DisplayMode windowMode;

	if (!main_window)
	{
		if (!SDL_GetNumVideoDisplays())
		{
			Ren_Fatal("There is no available display to open a game screen - %s", SDL_GetError());
			return;
		}

		// Use the zero display index
		display = 0;
	}
	else
	{
		// Detect the used display
		display = SDL_GetWindowDisplayIndex(main_window);
	}

	// was SDL_GetWindowDisplayMode
	if (SDL_GetDesktopDisplayMode(display, &windowMode) < 0)
	{
		Ren_Warning("Couldn't get desktop display mode, no resolutions detected - %s\n", SDL_GetError());
		return;
	}

	for (i = 0; i < SDL_GetNumDisplayModes(display); i++)
	{
		SDL_DisplayMode mode;

		if (SDL_GetDisplayMode(display, i, &mode) < 0)
		{
			continue;
		}

		if (!mode.w || !mode.h)
		{
			Ren_Print("Display supports any resolution\n");
			return;
		}

		if (windowMode.format != mode.format)
		{
			continue;
		}

		// SDL can give the same resolution with different refresh rates.
		// Only list resolution once.
		for (j = 0; j < numModes; j++)
		{
			if (mode.w == modes[j].w && mode.h == modes[j].h)
			{
				break;
			}
		}

		if (j != numModes)
		{
			continue;
		}

		modes[numModes].w = mode.w;
		modes[numModes].h = mode.h;
		numModes++;
	}

	if (numModes > 1)
	{
		qsort(modes, numModes, sizeof(SDL_Rect), GLimp_CompareModes);
	}

	for (i = 0; i < numModes; i++)
	{
		const char *newModeString = va("%ux%u ", modes[i].w, modes[i].h);

		if (strlen(newModeString) < (int)sizeof(buf) - strlen(buf))
		{
			Q_strcat(buf, sizeof(buf), newModeString);
		}
		else
		{
			Ren_Warning("Skipping mode %ux%u, buffer too small\n", modes[i].w, modes[i].h);
		}
	}

	if (*buf)
	{
		buf[strlen(buf) - 1] = 0;
		Ren_Print("Available modes [%i]: '%s'\n", numModes, buf);
		ri.Cvar_Set("r_availableModes", buf);
	}
}
Beispiel #23
0
/**
 * @brief R_LoadMDM
 * @param[in,out] mod
 * @param[in,out] buffer
 * @param[in] name
 * @return
 */
qboolean R_LoadMDM(model_t *mod, void *buffer, const char *name)
{
	int         i, j, k;
	mdmHeader_t *mdm = ( mdmHeader_t * ) buffer;
//    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;

	version = LittleLong(mdm->version);

	if (version != MDM_VERSION)
	{
		Ren_Warning("R_LoadMDM: %s has wrong version (%i should be %i)\n", name, 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);
	//Com_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)
		{
			Ren_Drop("R_LoadMDM: %s has more than %i verts on a surface (%i)",
			         name, SHADER_MAX_VERTEXES, mdmSurf->numVerts);
		}

		if (mdmSurf->numTriangles > SHADER_MAX_TRIANGLES)
		{
			Ren_Drop("R_LoadMDM: %s has more than %i triangles on a surface (%i)",
			         name, 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
				Ren_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
				Ren_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, name);
#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);

		//Ren_Print("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;

			//Ren_Print("(%i -> %i) ", j, value);
		}

		//Ren_Print("\n");

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

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

		Ren_Print("\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 (Q_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 0
			for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++)
			{
				//VectorNormalize(v->tangent);
				//VectorNormalize(v->binormal);
				//VectorNormalize(v->normal);
			}
#endif
		}
#endif

		if (r_smoothNormals->integer & FLAGS_SMOOTH_MDM) // 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);
			}
		}
	}

	// 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, 32);

		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);
					//Ren_Print("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)
				{
					Ren_Warning("R_LoadMDM: could not add triangles to a remaining VBO surface for model '%s'\n", name);
					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 #24
0
/**
 * @brief AddSurfaceToVBOSurfacesListMDM
 * @param[in] vboSurfaces
 * @param[in] vboTriangles
 * @param[in] mdm
 * @param[in] surf
 * @param[in] skinIndex
 * @param numBoneReferences - unused
 * @param[in] boneReferences
 */
static void AddSurfaceToVBOSurfacesListMDM(growList_t *vboSurfaces, growList_t *vboTriangles, mdmModel_t *mdm, mdmSurfaceIntern_t *surf, int skinIndex, int numBoneReferences, int boneReferences[MAX_BONES])
{
	int             j, k, lod;
	int             vertexesNum = surf->numVerts;
	byte            *data;
	int             dataSize;
	int             dataOfs;
	GLuint          ofsTexCoords;
	GLuint          ofsTangents;
	GLuint          ofsBinormals;
	GLuint          ofsNormals;
	GLuint          ofsBoneIndexes;
	GLuint          ofsBoneWeights;
	int             indexesNum = vboTriangles->currentElements * 3;
	byte            *indexes;
	int             indexesSize;
	int             indexesOfs;
	skelTriangle_t  *tri;
	vec4_t          tmp;
	int             index;
	srfVBOMDMMesh_t *vboSurf;
	md5Vertex_t     *v;
	//vec4_t          tmpColor = { 1, 1, 1, 1 };
	static int32_t collapse[MDM_MAX_VERTS];

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

	vboSurf->surfaceType = SF_VBO_MDMMESH;
	vboSurf->mdmModel    = mdm;
	vboSurf->mdmSurface  = surf;
	vboSurf->shader      = R_GetShaderByHandle(surf->shaderIndex);
	vboSurf->skinIndex   = skinIndex;
	vboSurf->numIndexes  = indexesNum;
	vboSurf->numVerts    = vertexesNum;

	dataSize = vertexesNum * (sizeof(vec4_t) * 7);
	data     = ri.Hunk_AllocateTempMemory(dataSize);
	dataOfs  = 0;

	//Ren_Print("AddSurfaceToVBOSurfacesList( %i verts, %i tris )\n", surf->numVerts, vboTriangles->currentElements);

	vboSurf->numBoneRemap = 0;
	Com_Memset(vboSurf->boneRemap, 0, sizeof(vboSurf->boneRemap));
	Com_Memset(vboSurf->boneRemapInverse, 0, sizeof(vboSurf->boneRemapInverse));

	//Ren_Print("original referenced bones: [ ");
	//for(j = 0; j < surf->numBoneReferences; j++)
	//{
	//  Ren_Print("%i, ", surf->boneReferences[j]);
	//}
	//Ren_Print("]\n");

	//Ren_Print("new referenced bones: ");
	for (j = 0; j < MAX_BONES; j++)
	{
		if (boneReferences[j] > 0)
		{
			vboSurf->boneRemap[j]                            = vboSurf->numBoneRemap;
			vboSurf->boneRemapInverse[vboSurf->numBoneRemap] = j;

			vboSurf->numBoneRemap++;

			//Ren_Print("(%i -> %i) ", j, vboSurf->boneRemap[j]);
		}
	}

	//Ren_Print("\n");

	// feed vertex XYZ
	for (j = 0; j < vertexesNum; j++)
	{
		for (k = 0; k < 3; k++)
		{
			tmp[k] = surf->verts[j].position[k];
		}

		tmp[3] = 1;
		Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t));
		dataOfs += sizeof(vec4_t);
	}

	// feed vertex texcoords
	ofsTexCoords = dataOfs;

	for (j = 0; j < vertexesNum; j++)
	{
		for (k = 0; k < 2; k++)
		{
			tmp[k] = surf->verts[j].texCoords[k];
		}

		tmp[2] = 0;
		tmp[3] = 1;
		Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t));
		dataOfs += sizeof(vec4_t);
	}

	// feed vertex tangents
	ofsTangents = dataOfs;

	for (j = 0; j < vertexesNum; j++)
	{
		for (k = 0; k < 3; k++)
		{
			tmp[k] = surf->verts[j].tangent[k];
		}

		tmp[3] = 1;
		Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t));
		dataOfs += sizeof(vec4_t);
	}

	// feed vertex binormals
	ofsBinormals = dataOfs;

	for (j = 0; j < vertexesNum; j++)
	{
		for (k = 0; k < 3; k++)
		{
			tmp[k] = surf->verts[j].binormal[k];
		}

		tmp[3] = 1;
		Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t));
		dataOfs += sizeof(vec4_t);
	}

	// feed vertex normals
	ofsNormals = dataOfs;

	for (j = 0; j < vertexesNum; j++)
	{
		for (k = 0; k < 3; k++)
		{
			tmp[k] = surf->verts[j].normal[k];
		}

		tmp[3] = 1;
		Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t));
		dataOfs += sizeof(vec4_t);
	}

	// feed bone indices
	ofsBoneIndexes = dataOfs;

	for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++)
	{
		for (k = 0; k < MAX_WEIGHTS; k++)
		{
			if (k < v->numWeights)
			{
				index = vboSurf->boneRemap[v->weights[k]->boneIndex];
			}
			else
			{
				index = 0;
			}

			Com_Memcpy(data + dataOfs, &index, sizeof(int));
			dataOfs += sizeof(int);
		}
	}

	// feed bone weights
	ofsBoneWeights = dataOfs;

	for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++)
	{
		for (k = 0; k < MAX_WEIGHTS; k++)
		{
			if (k < v->numWeights)
			{
				tmp[k] = v->weights[k]->boneWeight;
			}
			else
			{
				tmp[k] = 0;
			}
		}

		Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t));
		dataOfs += sizeof(vec4_t);
	}

	vboSurf->vbo                     = R_CreateVBO(va("staticMDMMesh_VBO %i", vboSurfaces->currentElements), data, dataSize, VBO_USAGE_STATIC);
	vboSurf->vbo->ofsXYZ             = 0;
	vboSurf->vbo->ofsTexCoords       = ofsTexCoords;
	vboSurf->vbo->ofsLightCoords     = ofsTexCoords;
	vboSurf->vbo->ofsTangents        = ofsTangents;
	vboSurf->vbo->ofsBinormals       = ofsBinormals;
	vboSurf->vbo->ofsNormals         = ofsNormals;
	vboSurf->vbo->ofsColors          = ofsNormals;
	//vboSurf->vbo->ofsLightCoords     = 0; // not required anyway
	//vboSurf->vbo->ofsLightDirections = 0; // not required anyway
	vboSurf->vbo->ofsBoneIndexes     = ofsBoneIndexes;
	vboSurf->vbo->ofsBoneWeights     = ofsBoneWeights;

	// calculate LOD IBOs
	lod = 0;

	do
	{
		float flod;
		int   renderCount;

		flod = mdmLODResolutions[lod];

		renderCount = MIN(( int )(( float ) surf->numVerts * flod), surf->numVerts);

		if (renderCount < surf->minLod)
		{
			renderCount = surf->minLod;
			flod        = ( float ) renderCount / surf->numVerts;
		}

		if (renderCount == surf->numVerts)
		{
			indexesNum  = vboTriangles->currentElements * 3;
			indexesSize = indexesNum * sizeof(int);
			indexes     = ri.Hunk_AllocateTempMemory(indexesSize);
			indexesOfs  = 0;

			for (j = 0; j < vboTriangles->currentElements; j++)
			{
				tri = Com_GrowListElement(vboTriangles, j);

				for (k = 0; k < 3; k++)
				{
					index = tri->indexes[k];

					Com_Memcpy(indexes + indexesOfs, &index, sizeof(int));
					indexesOfs += sizeof(int);
				}
			}
		}
		else
		{
			int     ci[3];
			int32_t *pCollapseMap;
			int32_t *pCollapse;

			pCollapse = collapse;

			for (j = 0; j < renderCount; pCollapse++, j++)
			{
				*pCollapse = j;
			}

			pCollapseMap = &surf->collapseMap[renderCount];

			for (j = renderCount; j < surf->numVerts; j++, pCollapse++, pCollapseMap++)
			{
				int32_t collapseValue = *pCollapseMap;
				*pCollapse = collapse[collapseValue];
			}

			indexesNum = 0;

			for (j = 0; j < vboTriangles->currentElements; j++)
			{
				tri = Com_GrowListElement(vboTriangles, j);

				ci[0] = collapse[tri->indexes[0]];
				ci[1] = collapse[tri->indexes[1]];
				ci[2] = collapse[tri->indexes[2]];

				// FIXME
				// note:  serious optimization opportunity here,
				//  by sorting the triangles the following "continue"
				//  could have been made into a "break" statement.
				if (ci[0] == ci[1] || ci[1] == ci[2] || ci[2] == ci[0])
				{
					continue;
				}

				indexesNum += 3;
			}

			indexesSize = indexesNum * sizeof(int);
			indexes     = ri.Hunk_AllocateTempMemory(indexesSize);
			indexesOfs  = 0;

			for (j = 0; j < vboTriangles->currentElements; j++)
			{
				tri = Com_GrowListElement(vboTriangles, j);

				ci[0] = collapse[tri->indexes[0]];
				ci[1] = collapse[tri->indexes[1]];
				ci[2] = collapse[tri->indexes[2]];

				// FIXME
				// note:  serious optimization opportunity here,
				//  by sorting the triangles the following "continue"
				//  could have been made into a "break" statement.
				if (ci[0] == ci[1] || ci[1] == ci[2] || ci[2] == ci[0])
				{
					continue;
				}

				for (k = 0; k < 3; k++)
				{
					index = ci[k];

					Com_Memcpy(indexes + indexesOfs, &index, sizeof(int));
					indexesOfs += sizeof(int);
				}
			}
		}

		vboSurf->ibo[lod]             = R_CreateIBO(va("staticMDMMesh_IBO_LOD_%f %i", flod, indexesNum / 3), indexes, indexesSize, VBO_USAGE_STATIC);
		vboSurf->ibo[lod]->indexesNum = indexesNum;

		ri.Hunk_FreeTempMemory(indexes);

		if (vboTriangles->currentElements != surf->numTriangles)
		{
			Ren_Warning("Can't calculate LOD IBOs\n");
			break;
		}

		lod++;
	}
	while (lod < MD3_MAX_LODS);

	ri.Hunk_FreeTempMemory(data);

	// megs

	/*
	   Ren_Print("md5 mesh data VBO size: %d.%02d MB\n", dataSize / (1024 * 1024),
	   (dataSize % (1024 * 1024)) * 100 / (1024 * 1024));
	   Ren_Print("md5 mesh tris VBO size: %d.%02d MB\n", indexesSize / (1024 * 1024),
	   (indexesSize % (1024 * 1024)) * 100 / (1024 * 1024));
	 */
}
Beispiel #25
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 * ) buffer;
	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];

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

	// check version
	token   = COM_ParseExt2(&buf_p, qfalse);
	version = atoi(token);

	if (version != MD5_VERSION)
	{
		Ren_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);
	//  Ren_Print("%s\n", token);

	// parse numJoints <number>
	token = COM_ParseExt2(&buf_p, qtrue);

	if (Q_stricmp(token, "numJoints"))
	{
		Ren_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"))
	{
		Ren_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);
	//Ren_Print("R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces);

	if (md5->numBones < 1)
	{
		Ren_Warning("R_LoadMD5: '%s' has no bones\n", modName);
		return qfalse;
	}

	if (md5->numBones > MAX_BONES)
	{
		Ren_Warning("R_LoadMD5: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones);
		return qfalse;
	}

	//Ren_Print("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"))
	{
		Ren_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, "{"))
	{
		Ren_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));

		//Ren_Print("R_LoadMD5: '%s' has bone '%s'\n", modName, bone->name);

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

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

		if (bone->parentIndex >= md5->numBones)
		{
			Ren_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, "("))
		{
			Ren_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, ")"))
		{
			Ren_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, "("))
		{
			Ren_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, ")"))
		{
			Ren_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, "}"))
	{
		Ren_Warning("R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName);
		return qfalse;
	}

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

	//Ren_Print("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"))
		{
			Ren_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, "{"))
		{
			Ren_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"))
		{
			Ren_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));

		//Ren_Print("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);
		//Ren_Print("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"))
		{
			Ren_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)
		{
			Ren_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"))
			{
				Ren_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, "("))
			{
				Ren_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, ")"))
			{
				Ren_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)
			{
				Ren_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"))
		{
			Ren_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)
		{
			Ren_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"))
			{
				Ren_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"))
		{
			Ren_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"))
			{
				Ren_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, "("))
			{
				Ren_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, ")"))
			{
				Ren_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, "}"))
		{
			Ren_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);
				//Ren_Print("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)
			{
				Ren_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 #26
0
void GLAPIENTRY Glimp_DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam)
{
	Ren_Warning("Driver message: %s\n", message);
}
Beispiel #27
0
/**
 * @brief R_LoadMDX
 * @param[in,out] mod
 * @param[out] buffer
 * @param[in] name
 * @return
 */
static qboolean R_LoadMDX(model_t *mod, void *buffer, const char *name)
{
	int           i, j;
	mdxHeader_t   *pinmodel = (mdxHeader_t *) buffer, *mdx;
	mdxFrame_t    *frame;
	short         *bframe;
	mdxBoneInfo_t *bi;
	int           version;
	int           size;
	int           frameSize;

	version = LittleLong(pinmodel->version);
	if (version != MDX_VERSION)
	{
		Ren_Warning("R_LoadMDX: %s has wrong version (%i should be %i)\n", name, version, MDX_VERSION);
		return qfalse;
	}

	mod->type      = MOD_MDX;
	size           = LittleLong(pinmodel->ofsEnd);
	mod->dataSize += size;
	mdx            = mod->mdx = (mdxHeader_t *)ri.Hunk_Alloc(size, h_low);

	Com_Memcpy(mdx, buffer, LittleLong(pinmodel->ofsEnd));

	LL(mdx->ident);
	LL(mdx->version);
	LL(mdx->numFrames);
	LL(mdx->numBones);
	LL(mdx->ofsFrames);
	LL(mdx->ofsBones);
	LL(mdx->ofsEnd);
	LL(mdx->torsoParent);

	if (LittleLong(1) != 1)
	{
		// swap all the frames
		frameSize = (int)(sizeof(mdxBoneFrameCompressed_t)) * mdx->numBones;
		for (i = 0; i < mdx->numFrames; i++)
		{
			frame         = (mdxFrame_t *) ((byte *) mdx + mdx->ofsFrames + i * frameSize + i * sizeof(mdxFrame_t));
			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]);
			}

			bframe = (short *)((byte *) mdx + mdx->ofsFrames + i * frameSize + ((i + 1) * sizeof(mdxFrame_t)));
			for (j = 0; j < mdx->numBones * sizeof(mdxBoneFrameCompressed_t) / sizeof(short); j++)
			{
				((short *)bframe)[j] = LittleShort(((short *)bframe)[j]);
			}
		}

		// swap all the bones
		for (i = 0; i < mdx->numBones; i++)
		{
			bi = (mdxBoneInfo_t *) ((byte *) mdx + mdx->ofsBones + i * sizeof(mdxBoneInfo_t));
			LL(bi->parent);
			bi->torsoWeight = LittleFloat(bi->torsoWeight);
			bi->parentDist  = LittleFloat(bi->parentDist);
			LL(bi->flags);
		}
	}

	return qtrue;
}
Beispiel #28
0
/**
 * @brief Loads in a model for the given name
 * @param[in] name
 * @return Zero will be returned if the model fails to load.
 * An entry will be retained for failed models as an
 * optimization to prevent disk rescanning if they are
 * asked for again.
 */
qhandle_t RE_RegisterModel(const char *name)
{
	model_t   *mod;
	byte      *buffer;
	int       bufferLen = 0;
	int       lod;
	int       ident;
	qboolean  loaded = qfalse;
	qhandle_t hModel;
	int       numLoaded;

	if (!name || !name[0])
	{
		Ren_Developer("RE_RegisterModel: NULL name\n");
		return 0;
	}
	else
	{
		Ren_Developer("RE_RegisterModel model: %s\n", name);
	}

	if (strlen(name) >= MAX_QPATH)
	{
		Ren_Print("Model name exceeds MAX_QPATH\n");
		return 0;
	}

	// search the currently loaded models
	for (hModel = 1; hModel < tr.numModels; hModel++)
	{
		mod = tr.models[hModel];
		if (!strcmp(mod->name, name))
		{
			if (mod->type == MOD_BAD)
			{
				Ren_Warning("RE_RegisterModel: bad model '%s' - already registered but in bad condition - returning 0\n", name);
				return 0;
			}
			return hModel;
		}
	}

	// allocate a new model_t
	if ((mod = R_AllocModel()) == NULL)
	{
		Ren_Warning("RE_RegisterModel: R_AllocModel() failed for '%s'\n", name);
		return 0;
	}

	// only set the name after the model has been successfully loaded
	Q_strncpyz(mod->name, name, sizeof(mod->name));

	// make sure the render thread is stopped
	R_IssuePendingRenderCommands();

	mod->numLods = 0;

	// load the files
	numLoaded = 0;

	if (strstr(name, ".mds") || strstr(name, ".mdm") || strstr(name, ".mdx") || strstr(name, ".md5mesh") || strstr(name, ".psk"))
	{
		// try loading skeletal file

		loaded    = qfalse;
		bufferLen = ri.FS_ReadFile(name, (void **)&buffer);
		if (buffer)
		{
			ident = LittleLong(*(unsigned *)buffer);
#if 0
			if (ident == MDS_IDENT)
			{
				loaded = R_LoadMDS(mod, buffer, name);
			}
			else
#endif
			if (ident == MDM_IDENT)
			{
				loaded = R_LoadMDM(mod, buffer, name);
			}
			else if (ident == MDX_IDENT)
			{
				loaded = R_LoadMDX(mod, buffer, name);
			}

#if defined(USE_REFENTITY_ANIMATIONSYSTEM)
			if (!Q_stricmpn((const char *)buffer, "MD5Version", 10))
			{
				loaded = R_LoadMD5(mod, buffer, bufferLen, name);
			}
			else if (!Q_stricmpn((const char *)buffer, PSK_IDENTSTRING, PSK_IDENTLEN))
			{
				loaded = R_LoadPSK(mod, buffer, bufferLen, name);
			}
#endif
			ri.FS_FreeFile(buffer);
		}

		if (loaded)
		{
			// make sure the VBO glState entries are save
			R_BindNullVBO();
			R_BindNullIBO();

			return mod->index;
		}
	}

	for (lod = MD3_MAX_LODS - 1; lod >= 0; lod--)
	{
		char filename[1024];
		buffer = NULL;

		strcpy(filename, name);
		if (lod != 0)
		{
			char namebuf[80];

			if (strrchr(filename, '.'))
			{
				*strrchr(filename, '.') = 0;
			}
			sprintf(namebuf, "_%d.md3", lod);
			strcat(filename, namebuf);
		}

		filename[strlen(filename) - 1] = '3';   // try MD3 first (changed order for 2.76)
		if (ri.FS_FOpenFileRead(filename, NULL, qfalse) > 0)
		{
			ri.FS_ReadFile(filename, (void **)&buffer);
		}
		if (!buffer)
		{
			filename[strlen(filename) - 1] = 'c';   // try MDC  second
			if (ri.FS_FOpenFileRead(filename, NULL, qfalse) > 0)
			{
				ri.FS_ReadFile(filename, (void **)&buffer);
			}
			if (!buffer)
			{
				continue;
			}
		}

		ident = LittleLong(*(unsigned *)buffer);
		if (ident != MD3_IDENT && ident != MDC_IDENT)
		{
			Ren_Warning("RE_RegisterModel: unknown fileid for %s\n", name);
			ri.FS_FreeFile(buffer);
			goto fail;
		}

		if (ident == MD3_IDENT)
		{
			loaded = R_LoadMD3(mod, lod, buffer, bufferLen, name);
		}
		else if (ident == MDC_IDENT)
		{
			loaded = R_LoadMDC(mod, lod, buffer, bufferLen, name);
		}

		ri.FS_FreeFile(buffer);

		if (!loaded)
		{
			if (lod == 0)
			{
				goto fail;
			}
			else
			{
				break;
			}
		}
		else
		{
			// make sure the VBO glState entries are save
			R_BindNullVBO();
			R_BindNullIBO();

			mod->numLods++;
			numLoaded++;
			// if we have a valid model and are biased
			// so that we won't see any higher detail ones,
			// stop loading them
			//if ( lod <= r_lodbias->integer ) {
			//break;
			//}
		}
	}

	// make sure the VBO glState entries are save
	R_BindNullVBO();
	R_BindNullIBO();

	if (numLoaded)
	{
		// duplicate into higher lod spots that weren't
		// loaded, in case the user changes r_lodbias on the fly
		for (lod--; lod >= 0; lod--)
		{
			mod->numLods++;
			mod->mdv[lod] = mod->mdv[lod + 1];
		}
		return mod->index;
	}
#ifdef LEGACY_DEBUG
	else
	{
		Ren_Warning("couldn't load '%s'\n", name);
	}
#endif

fail:
	// we still keep the model_t around, so if the model name is asked for
	// again, we won't bother scanning the filesystem
	mod->type = MOD_BAD;

	// make sure the VBO glState entries are save
	R_BindNullVBO();
	R_BindNullIBO();

	return 0;
}
Beispiel #29
0
/*
=================
R_LoadMDC
=================
*/
qboolean R_LoadMDC(model_t *mod, int lod, void *buffer, int bufferSize, const char *modName)
{
	int                i, j, k;
	mdcHeader_t        *mdcModel = ( mdcHeader_t * ) buffer;
	md3Frame_t         *mdcFrame;
	mdcSurface_t       *mdcSurf;
	md3Shader_t        *mdcShader;
	md3Triangle_t      *mdcTri;
	md3St_t            *mdcst;
	md3XyzNormal_t     *mdcxyz;
	mdcXyzCompressed_t *mdcxyzComp;
	mdcTag_t           *mdcTag;
	mdcTagName_t       *mdcTagName;
	mdvModel_t         *mdvModel;
	mdvFrame_t         *frame;
	mdvSurface_t       *surf; //, *surface; //unused
	srfTriangle_t      *tri;
	mdvXyz_t           *v;
	mdvSt_t            *st;
	mdvTag_t           *tag;
	mdvTagName_t       *tagName;
	short              *ps;
	int                version;
	int                size;

	version = LittleLong(mdcModel->version);

	if (version != MDC_VERSION)
	{
		Ren_Warning("R_LoadMD3: %s has wrong version (%i should be %i)\n", modName, version, MDC_VERSION);
		return qfalse;
	}

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

	LL(mdcModel->ident);
	LL(mdcModel->version);
	LL(mdcModel->numFrames);
	LL(mdcModel->numTags);
	LL(mdcModel->numSurfaces);
	LL(mdcModel->ofsFrames);
	LL(mdcModel->ofsTags);
	LL(mdcModel->ofsSurfaces);
	LL(mdcModel->ofsEnd);
	LL(mdcModel->ofsEnd);
	LL(mdcModel->flags);
	LL(mdcModel->numSkins);

	if (mdcModel->numFrames < 1)
	{
		Ren_Warning("R_LoadMDC: '%s' has no frames\n", modName);
		return qfalse;
	}

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

	mdcFrame = ( md3Frame_t * )(( byte * ) mdcModel + mdcModel->ofsFrames);

	for (i = 0; i < mdcModel->numFrames; i++, frame++, mdcFrame++)
	{
#if 1
		// ET HACK
		if (strstr(mod->name, "sherman") || strstr(mod->name, "mg42"))
		{
			frame->radius = 256;

			for (j = 0; j < 3; j++)
			{
				frame->bounds[0][j]   = 128;
				frame->bounds[1][j]   = -128;
				frame->localOrigin[j] = LittleFloat(mdcFrame->localOrigin[j]);
			}
		}
		else
#endif
		{
			frame->radius = LittleFloat(mdcFrame->radius);

			for (j = 0; j < 3; j++)
			{
				frame->bounds[0][j]   = LittleFloat(mdcFrame->bounds[0][j]);
				frame->bounds[1][j]   = LittleFloat(mdcFrame->bounds[1][j]);
				frame->localOrigin[j] = LittleFloat(mdcFrame->localOrigin[j]);
			}
		}
	}

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

	mdcTag = ( mdcTag_t * )(( byte * ) mdcModel + mdcModel->ofsTags);

	for (i = 0; i < mdcModel->numTags * mdcModel->numFrames; i++, tag++, mdcTag++)
	{
		vec3_t angles;

		for (j = 0; j < 3; j++)
		{
			tag->origin[j] = ( float ) LittleShort(mdcTag->xyz[j]) * MD3_XYZ_SCALE;
			angles[j]      = ( float ) LittleShort(mdcTag->angles[j]) * MDC_TAG_ANGLE_SCALE;
		}

		AnglesToAxis(angles, tag->axis);
	}

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

	mdcTagName = ( mdcTagName_t * )(( byte * ) mdcModel + mdcModel->ofsTagNames);

	for (i = 0; i < mdcModel->numTags; i++, tagName++, mdcTagName++)
	{
		Q_strncpyz(tagName->name, mdcTagName->name, sizeof(tagName->name));
	}

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

	mdcSurf = ( mdcSurface_t * )(( byte * ) mdcModel + mdcModel->ofsSurfaces);

	for (i = 0; i < mdcModel->numSurfaces; i++)
	{
		LL(mdcSurf->ident);
		LL(mdcSurf->flags);
		LL(mdcSurf->numBaseFrames);
		LL(mdcSurf->numCompFrames);
		LL(mdcSurf->numShaders);
		LL(mdcSurf->numTriangles);
		LL(mdcSurf->ofsTriangles);
		LL(mdcSurf->numVerts);
		LL(mdcSurf->ofsShaders);
		LL(mdcSurf->ofsSt);
		LL(mdcSurf->ofsXyzNormals);
		LL(mdcSurf->ofsXyzNormals);
		LL(mdcSurf->ofsXyzCompressed);
		LL(mdcSurf->ofsFrameBaseFrames);
		LL(mdcSurf->ofsFrameCompFrames);
		LL(mdcSurf->ofsEnd);

		if (mdcSurf->numVerts > SHADER_MAX_VERTEXES)
		{
			Ren_Drop("R_LoadMDC: %s has more than %i verts on a surface (%i)",
			         modName, SHADER_MAX_VERTEXES, mdcSurf->numVerts);
		}

		if (mdcSurf->numTriangles > SHADER_MAX_TRIANGLES)
		{
			Ren_Drop("R_LoadMDC: %s has more than %i triangles on a surface (%i)",
			         modName, SHADER_MAX_TRIANGLES, mdcSurf->numTriangles);
		}

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

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

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

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

		// strip off a trailing _1 or _2
		// this is a crutch for q3data being a mess
		j = strlen(surf->name);

		if (j > 2 && surf->name[j - 2] == '_')
		{
			surf->name[j - 2] = 0;
		}

		// register the shaders

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

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

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

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

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

		mdcTri = ( md3Triangle_t * )(( byte * ) mdcSurf + mdcSurf->ofsTriangles);

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

		// swap all the XyzNormals
		mdcxyz = ( md3XyzNormal_t * )(( byte * ) mdcSurf + mdcSurf->ofsXyzNormals);

		for (j = 0; j < mdcSurf->numVerts * mdcSurf->numBaseFrames; j++, mdcxyz++)
		{
			mdcxyz->xyz[0] = LittleShort(mdcxyz->xyz[0]);
			mdcxyz->xyz[1] = LittleShort(mdcxyz->xyz[1]);
			mdcxyz->xyz[2] = LittleShort(mdcxyz->xyz[2]);

			mdcxyz->normal = LittleShort(mdcxyz->normal);
		}

		// swap all the XyzCompressed
		mdcxyzComp = ( mdcXyzCompressed_t * )(( byte * ) mdcSurf + mdcSurf->ofsXyzCompressed);

		for (j = 0; j < mdcSurf->numVerts * mdcSurf->numCompFrames; j++, mdcxyzComp++)
		{
			LL(mdcxyzComp->ofsVec);
		}

		// swap the frameBaseFrames
		ps = ( short * )(( byte * ) mdcSurf + mdcSurf->ofsFrameBaseFrames);

		for (j = 0; j < mdcModel->numFrames; j++, ps++)
		{
			*ps = LittleShort(*ps);
		}

		// swap the frameCompFrames
		ps = ( short * )(( byte * ) mdcSurf + mdcSurf->ofsFrameCompFrames);

		for (j = 0; j < mdcModel->numFrames; j++, ps++)
		{
			*ps = LittleShort(*ps);
		}

		surf->numVerts = mdcSurf->numVerts;
		surf->verts    = v = ri.Hunk_Alloc(sizeof(*v) * (mdcSurf->numVerts * mdcModel->numFrames), h_low);

		for (j = 0; j < mdcModel->numFrames; j++)
		{
			int baseFrame;
			int compFrame = 0;

			baseFrame = ( int ) *(( short * )(( byte * ) mdcSurf + mdcSurf->ofsFrameBaseFrames) + j);

			mdcxyz = ( md3XyzNormal_t * )(( byte * ) mdcSurf + mdcSurf->ofsXyzNormals + baseFrame * mdcSurf->numVerts * sizeof(md3XyzNormal_t));

			if (mdcSurf->numCompFrames > 0)
			{
				compFrame = ( int ) *(( short * )(( byte * ) mdcSurf + mdcSurf->ofsFrameCompFrames) + j);

				if (compFrame >= 0)
				{
					mdcxyzComp = ( mdcXyzCompressed_t * )(( byte * ) mdcSurf + mdcSurf->ofsXyzCompressed + compFrame * mdcSurf->numVerts * sizeof(mdcXyzCompressed_t));
				}
			}

			for (k = 0; k < mdcSurf->numVerts; k++, v++, mdcxyz++)
			{
				v->xyz[0] = LittleShort(mdcxyz->xyz[0]) * MD3_XYZ_SCALE;
				v->xyz[1] = LittleShort(mdcxyz->xyz[1]) * MD3_XYZ_SCALE;
				v->xyz[2] = LittleShort(mdcxyz->xyz[2]) * MD3_XYZ_SCALE;

				if (mdcSurf->numCompFrames > 0 && compFrame >= 0)
				{
					vec3_t ofsVec;

					R_MDC_DecodeXyzCompressed2(LittleShort(mdcxyzComp->ofsVec), ofsVec);
					VectorAdd(v->xyz, ofsVec, v->xyz);

					mdcxyzComp++;
				}
			}
		}

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

		mdcst = ( md3St_t * )(( byte * ) mdcSurf + mdcSurf->ofsSt);

		for (j = 0; j < mdcSurf->numVerts; j++, mdcst++, st++)
		{
			st->st[0] = LittleFloat(mdcst->st[0]);
			st->st[1] = LittleFloat(mdcst->st[1]);
		}

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

#if 1
	// create VBO surfaces from md3 surfaces
	{
		mdvNormTanBi_t *vertexes;
		mdvNormTanBi_t *vert;

		growList_t      vboSurfaces;
		srfVBOMDVMesh_t *vboSurf;

		byte *data;
		int  dataSize;
		int  dataOfs;

		vec4_t tmp;

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

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

		int vertexesNum;
		int f;

		Com_InitGrowList(&vboSurfaces, 10);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

			// create VBO
			vertexesNum = surf->numVerts;

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

			// feed vertex XYZ
			for (f = 0; f < mdvModel->numFrames; f++)
			{
				for (j = 0; j < vertexesNum; j++)
				{
					for (k = 0; k < 3; k++)
					{
						tmp[k] = surf->verts[f * vertexesNum + j].xyz[k];
					}

					tmp[3] = 1;
					Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t));
					dataOfs += sizeof(vec4_t);
				}

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

			// feed vertex texcoords
			ofsTexCoords = dataOfs;

			for (j = 0; j < vertexesNum; j++)
			{
				for (k = 0; k < 2; k++)
				{
					tmp[k] = surf->st[j].st[k];
				}

				tmp[2] = 0;
				tmp[3] = 1;
				Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t));
				dataOfs += sizeof(vec4_t);
			}

			// feed vertex tangents
			ofsTangents = dataOfs;

			for (f = 0; f < mdvModel->numFrames; f++)
			{
				for (j = 0; j < vertexesNum; j++)
				{
					for (k = 0; k < 3; k++)
					{
						tmp[k] = vertexes[f * vertexesNum + j].tangent[k];
					}

					tmp[3] = 1;
					Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t));
					dataOfs += sizeof(vec4_t);
				}

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

			// feed vertex binormals
			ofsBinormals = dataOfs;

			for (f = 0; f < mdvModel->numFrames; f++)
			{
				for (j = 0; j < vertexesNum; j++)
				{
					for (k = 0; k < 3; k++)
					{
						tmp[k] = vertexes[f * vertexesNum + j].binormal[k];
					}

					tmp[3] = 1;
					Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t));
					dataOfs += sizeof(vec4_t);
				}

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

			// feed vertex normals
			ofsNormals = dataOfs;

			for (f = 0; f < mdvModel->numFrames; f++)
			{
				for (j = 0; j < vertexesNum; j++)
				{
					for (k = 0; k < 3; k++)
					{
						tmp[k] = vertexes[f * vertexesNum + j].normal[k];
					}

					tmp[3] = 1;
					Com_Memcpy(data + dataOfs, ( vec_t * ) tmp, sizeof(vec4_t));
					dataOfs += sizeof(vec4_t);
				}

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

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

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

			ri.Hunk_FreeTempMemory(data);
			ri.Hunk_FreeTempMemory(vertexes);
		}

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

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

		Com_DestroyGrowList(&vboSurfaces);
	}
#endif

	return qtrue;
}
Beispiel #30
0
qhandle_t RE_RegisterSkin(const char *name)
{
	qhandle_t     hSkin;
	skin_t        *skin;
	skinSurface_t *surf;
	skinModel_t   *model;
	char          *text, *text_p;
	char          *token;
	char          surfName[MAX_QPATH];

	if (!name || !name[0])
	{
		Ren_Print("Empty name passed to RE_RegisterSkin\n");
		return 0;
	}

	if (strlen(name) >= MAX_QPATH)
	{
		Ren_Print("Skin name exceeds MAX_QPATH\n");
		return 0;
	}

	// see if the skin is already loaded
	for (hSkin = 1; hSkin < tr.numSkins; hSkin++)
	{
		skin = tr.skins[hSkin];
		if (!Q_stricmp(skin->name, name))
		{
			if (skin->numSurfaces == 0)
			{
				return 0;       // default skin
			}
			return hSkin;
		}
	}

	// allocate a new skin
	if (tr.numSkins == MAX_SKINS)
	{
		Ren_Warning("WARNING: RE_RegisterSkin( '%s' ) MAX_SKINS hit\n", name);
		return 0;
	}

	// - moved things around slightly to fix the problem where you restart
	// a map that has ai characters who had invalid skin names entered
	// in thier "skin" or "head" field

	// make sure the render thread is stopped
	R_IssuePendingRenderCommands();

#if 0
	// If not a .skin file, load as a single shader
	if (strcmp(name + strlen(name) - 5, ".skin"))
	{
		skin->numSurfaces         = 1;
		skin->surfaces[0]         = ri.Hunk_Alloc(sizeof(skin->surfaces[0]), h_low);
		skin->surfaces[0]->shader = R_FindShader(name, SHADER_3D_DYNAMIC, qtrue);
		return hSkin;
	}
#endif

	// load and parse the skin file
	ri.FS_ReadFile(name, (void **)&text);
	if (!text)
	{
		return 0;
	}

	tr.numSkins++;
	skin            = (skin_t *)ri.Hunk_Alloc(sizeof(skin_t), h_low);
	tr.skins[hSkin] = skin;
	Q_strncpyz(skin->name, name, sizeof(skin->name));
	skin->numSurfaces = 0;
	skin->numModels   = 0;

	text_p = text;
	while (text_p && *text_p)
	{
		// get surface name
		token = CommaParse(&text_p);
		Q_strncpyz(surfName, token, sizeof(surfName));

		if (!token[0])
		{
			break;
		}
		// lowercase the surface name so skin compares are faster
		Q_strlwr(surfName);

		if (*text_p == ',')
		{
			text_p++;
		}

		if (!Q_stricmpn(token, "tag_", 4))
		{
			continue;
		}

		if (!Q_stricmpn(token, "md3_", 4))
		{
			if (skin->numModels >= MAX_PART_MODELS)
			{
				Ren_Warning("WARNING: Ignoring models in '%s', the max is %d!\n", name, MAX_PART_MODELS);
				break;
			}

			// this is specifying a model
			model = skin->models[skin->numModels] = (skinModel_t *)ri.Hunk_Alloc(sizeof(*skin->models[0]), h_low);
			Q_strncpyz(model->type, token, sizeof(model->type));
			model->hash = Com_HashKey(model->type, sizeof(model->type));

			// get the model name
			token = CommaParse(&text_p);

			Q_strncpyz(model->model, token, sizeof(model->model));

			skin->numModels++;
			continue;
		}

		// parse the shader name
		token = CommaParse(&text_p);

		if (skin->numSurfaces >= MD3_MAX_SURFACES)
		{
			Ren_Warning("WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MD3_MAX_SURFACES);
			break;
		}

		surf = skin->surfaces[skin->numSurfaces] = (skinSurface_t *)ri.Hunk_Alloc(sizeof(*skin->surfaces[0]), h_low);
		Q_strncpyz(surf->name, surfName, sizeof(surf->name));

		// FIXME: bspSurface not not have ::hash yet
		//surf->hash = Com_HashKey(surf->name, sizeof(surf->name));
		surf->shader = R_FindShader(token, SHADER_3D_DYNAMIC, qtrue);
		skin->numSurfaces++;
	}

	ri.FS_FreeFile(text);

	// never let a skin have 0 shaders
	if (skin->numSurfaces == 0)
	{
		return 0;               // use default skin
	}

	return hSkin;
}