/** * @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; }
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; }
/* ============== 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; }
/** * @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 }
/** * @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); } }
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(); }
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(); }
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); }
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); } }
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(); }
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); }
/* ================= 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); }
/* ================ 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(); }
/* ===================== 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; }
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); }
/* ===================== 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; } }
/* =============== 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; }
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; }
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; }
/** * @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; }
/** * @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; }
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); } }
/** * @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; }
/** * @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)); */ }
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; }
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); }
/** * @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; }
/** * @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; }
/* ================= 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; }
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; }