void R_InitGamma(void) { byte *data; if (!GLEW_ARB_fragment_program) { Ren_Print("WARNING: R_InitGamma() skipped - no ARB_fragment_program\n"); return; } if (ri.Cvar_VariableIntegerValue("r_ignorehwgamma")) { Ren_Print("INFO: R_InitGamma() skipped - r_ignorehwgamma is set\n"); return; } data = (byte *)ri.Hunk_AllocateTempMemory(glConfig.vidWidth * glConfig.vidHeight * 4); if (!data) { Ren_Print("WARNING: R_InitGamma() can't allocate temp memory\n"); // fatal? return; } screenImage = R_CreateImage("screenBufferImage_skies", data, glConfig.vidWidth, glConfig.vidHeight, qfalse, qfalse, GL_CLAMP_TO_EDGE); if (!screenImage) { Ren_Print("WARNING: R_InitGamma() screen image is NULL\n"); } ri.Hunk_FreeTempMemory(data); Com_Memset(&gammaProgram, 0, sizeof(shaderProgram_t)); R_BuildGammaProgram(); }
static void PrintChunkHeader(axChunkHeader_t *chunkHeader) { #if 0 Ren_Print("----------------------\n"); Ren_Print("R_LoadPSK: chunk header ident: '%s'\n", chunkHeader->ident); Ren_Print("R_LoadPSK: chunk header flags: %i\n", chunkHeader->flags); Ren_Print("R_LoadPSK: chunk header data size: %i\n", chunkHeader->dataSize); Ren_Print("R_LoadPSK: chunk header num items: %i\n", chunkHeader->numData); #endif }
/* =============== LogLight =============== */ static void LogLight(trRefEntity_t *ent) { int max1, max2; if (!(ent->e.renderfx & RF_FIRST_PERSON)) { return; } max1 = ent->ambientLight[0]; if (ent->ambientLight[1] > max1) { max1 = ent->ambientLight[1]; } else if (ent->ambientLight[2] > max1) { max1 = ent->ambientLight[2]; } max2 = ent->directedLight[0]; if (ent->directedLight[1] > max2) { max2 = ent->directedLight[1]; } else if (ent->directedLight[2] > max2) { max2 = ent->directedLight[2]; } Ren_Print("amb:%i dir:%i\n", max1, max2); }
/* =============== RE_Shutdown =============== */ void RE_Shutdown(qboolean destroyWindow) { Ren_Print("RE_Shutdown( %i )\n", destroyWindow); ri.Cmd_RemoveSystemCommand("imagelist"); ri.Cmd_RemoveSystemCommand("shaderlist"); ri.Cmd_RemoveSystemCommand("skinlist"); ri.Cmd_RemoveSystemCommand("modellist"); ri.Cmd_RemoveSystemCommand("modelist"); ri.Cmd_RemoveSystemCommand("screenshot"); ri.Cmd_RemoveSystemCommand("screenshotJPEG"); ri.Cmd_RemoveSystemCommand("gfxinfo"); ri.Cmd_RemoveSystemCommand("minimize"); ri.Cmd_RemoveSystemCommand("taginfo"); // keep a backup of the current images if possible // clean out any remaining unused media from the last backup R_PurgeCache(); if (r_cache->integer) { if (tr.registered) { if (destroyWindow) { R_IssuePendingRenderCommands(); R_DeleteTextures(); } else { // backup the current media R_BackupModels(); R_BackupShaders(); R_BackupImages(); } } } else if (tr.registered) { R_IssuePendingRenderCommands(); R_DeleteTextures(); } R_DoneFreeType(); R_ShutdownGamma(); // shut down platform specific OpenGL stuff if (destroyWindow) { R_DoGLimpShutdown(); // release the virtual memory R_Hunk_End(); R_FreeImageBuffer(); ri.Tag_Free(); // wipe all render alloc'd zone memory } tr.registered = qfalse; }
void R_SkinList_f(void) { int i, j; skin_t *skin; Ren_Print("------------------\n"); for (i = 0; i < tr.numSkins; i++) { skin = tr.skins[i]; Ren_Print("%3i:%s\n", i, skin->name); for (j = 0; j < skin->numSurfaces; j++) { Ren_Print(" %s = %s\n", skin->surfaces[j]->name, skin->surfaces[j]->shader->name); } } Ren_Print("------------------\n"); }
static void R_JPGOutputMessage(j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; /* Create the message */ (*cinfo->err->format_message)(cinfo, buffer); /* Send it to stderr, adding a newline */ Ren_Print("%s\n", buffer); }
static qboolean GLimp_StartDriverAndSetMode(int mode, qboolean fullscreen, qboolean noborder) { rserr_t err; if (!SDL_WasInit(SDL_INIT_VIDEO)) { if (SDL_Init(SDL_INIT_VIDEO) < 0) { Ren_Print("SDL_Init( SDL_INIT_VIDEO ) FAILED (%s)\n", SDL_GetError()); return qfalse; } Ren_Print("SDL initialized driver \"%s\"\n", SDL_GetCurrentVideoDriver()); } if (fullscreen && ri.Cvar_VariableIntegerValue("in_nograb")) { Ren_Print("Fullscreen not allowed with in_nograb 1\n"); ri.Cvar_Set("r_fullscreen", "0"); r_fullscreen->modified = qfalse; fullscreen = qfalse; } err = GLimp_SetMode(mode, fullscreen, noborder); switch (err) { case RSERR_INVALID_FULLSCREEN: Ren_Print("...WARNING: fullscreen unavailable in this mode\n"); return qfalse; case RSERR_INVALID_MODE: Ren_Print("...WARNING: could not set the given mode (%d)\n", mode); return qfalse; case RSERR_OLD_GL: ri.Error(ERR_VID_FATAL, "Could not create opengl 3 context"); return qfalse; default: break; } return qtrue; }
/* ================ R_AnimationList_f ================ */ void R_AnimationList_f(void) { int i; skelAnimation_t *anim; for (i = 0; i < tr.numAnimations; i++) { anim = tr.animations[i]; if (anim->type == AT_PSA && anim->psa) { Ren_Print("'%s' : '%s'\n", anim->name, anim->psa->info.name); } else { Ren_Print("'%s'\n", anim->name); } } Ren_Print("%8i : Total animations\n", tr.numAnimations); }
void QDECL Com_Printf(const char *msg, ...) { va_list argptr; char text[1024]; va_start(argptr, msg); Q_vsnprintf(text, sizeof(text), msg, argptr); va_end(argptr); Ren_Print("%s", text); }
/* ===================== RE_AddRefEntityToScene ===================== */ void RE_AddRefEntityToScene(const refEntity_t *ent) { if (!tr.registered) { return; } // fixed was ENTITYNUM_WORLD if (r_numentities >= MAX_REFENTITIES) { // we may change this to developer print Ren_Print("WARNING RE_AddRefEntityToScene: Dropping refEntity, reached MAX_REFENTITIES\n"); return; } if (Q_isnan(ent->origin[0]) || Q_isnan(ent->origin[1]) || Q_isnan(ent->origin[2])) { static qboolean firstTime = qtrue; if (firstTime) { firstTime = qfalse; Ren_Print("WARNING RE_AddRefEntityToScene passed a refEntity which has an origin with a NaN component\n"); } return; } if ((int)ent->reType < 0 || ent->reType >= RT_MAX_REF_ENTITY_TYPE) { Ren_Drop("RE_AddRefEntityToScene: bad reType %i", ent->reType); } backEndData->entities[r_numentities].e = *ent; backEndData->entities[r_numentities].lightingCalculated = qfalse; r_numentities++; // add projected shadows for this model // - casting const away R_AddModelShadow((refEntity_t *) ent); }
/** * @brief Draw all the images to the screen, on top of whatever * was there. This is used to test for texture thrashing. * * Also called by RE_EndRegistration */ void RB_ShowImages(void) { int i; image_t *image; float x, y, w, h; int start, end; if (!backEnd.projection2D) { RB_SetGL2D(); } qglClear(GL_COLOR_BUFFER_BIT); qglFinish(); start = ri.Milliseconds(); for (i = 0 ; i < tr.numImages ; i++) { image = tr.images[i]; w = glConfig.vidWidth / 40; h = glConfig.vidHeight / 30; x = i % 40 * w; y = i / 30 * h; // show in proportional size in mode 2 if (r_showImages->integer == 2) { w *= image->uploadWidth / 512.0f; h *= image->uploadHeight / 512.0f; } GL_Bind(image); qglBegin(GL_QUADS); qglTexCoord2f(0, 0); qglVertex2f(x, y); qglTexCoord2f(1, 0); qglVertex2f(x + w, y); qglTexCoord2f(1, 1); qglVertex2f(x + w, y + h); qglTexCoord2f(0, 1); qglVertex2f(x, y + h); qglEnd(); } qglFinish(); end = ri.Milliseconds(); Ren_Print("%i msec to draw all images\n", end - start); }
static qboolean GLimp_InitOpenGLContext() { #ifdef FEATURE_RENDERER2 int GLmajor, GLminor; #endif // get vendor Q_strncpyz(glConfig.vendor_string, (char *) qglGetString(GL_VENDOR), sizeof(glConfig.vendor_string)); // get renderer Q_strncpyz(glConfig.renderer_string, (char *) qglGetString(GL_RENDERER), sizeof(glConfig.renderer_string)); if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n') { glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0; } // get GL version Q_strncpyz(glConfig.version_string, (char *) qglGetString(GL_VERSION), sizeof(glConfig.version_string)); Ren_Print("GL_VENDOR: %s\n", glConfig.vendor_string); Ren_Print("GL_RENDERER: %s\n", glConfig.renderer_string); Ren_Print("GL_VERSION: %s\n", glConfig.version_string); #ifndef FEATURE_RENDERER2 Ren_Print("Using vanilla renderer\n"); #else // get shading language version Q_strncpyz(glConfig2.shadingLanguageVersion, (char *)glGetString(GL_SHADING_LANGUAGE_VERSION), sizeof(glConfig2.shadingLanguageVersion)); sscanf(glConfig2.shadingLanguageVersion, "%d.%d", &glConfig2.glslMajorVersion, &glConfig2.glslMinorVersion); Ren_Print("GL_SHADING_LANGUAGE_VERSION: %s\n", glConfig2.shadingLanguageVersion); // get GL context version sscanf(( const char * ) glGetString(GL_VERSION), "%d.%d", &GLmajor, &GLminor); glConfig2.contextCombined = (GLmajor * 100) + (GLminor * 10); if (GLmajor < 2) { // missing shader support return qfalse; } if (GLmajor < 3 || (GLmajor == 3 && GLminor < 2)) { // shaders are supported, but not all GL3.x features Ren_Print("Using enhanced renderer in GL 2.x mode\n"); return qtrue; } Ren_Print("Using enhanced renderer in GL 3.x mode\n"); glConfig.driverType = GLDRV_OPENGL3; #endif return qtrue; }
static qboolean GLimp_CheckForVersionExtension(const char *ext, int coresince, qboolean required, cvar_t *var) { qboolean result = qfalse; if ((coresince >= 0 && coresince <= glConfig2.contextCombined) || GL_CheckForExtension(ext)) { if (var && var->integer) { result = qtrue; } else if (!var) { result = qtrue; } } if (required && !result) { Ren_Fatal(MSG_ERR_OLD_VIDEO_DRIVER "\nYour GL driver is missing support for: %s\n", ext); } if (result) { Ren_Print("...found OpenGL extension - %s\n", ext); } else { if (var) { Ren_Print("...ignoring %s\n", ext); } else { Ren_Print("...%s not found\n", ext); } } return result; }
/** * @brief RE_Finish */ void RE_Finish(void) { renderFinishCommand_t *cmd; Ren_Print("RE_Finish\n"); cmd = (renderFinishCommand_t *)R_GetCommandBuffer(sizeof(*cmd)); if (!cmd) { return; } cmd->commandId = RC_FINISH; }
static void R_JPGErrorExit(j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; my_jpeg_error_mgr *mgr = (my_jpeg_error_mgr *)cinfo->err; (*cinfo->err->format_message)(cinfo, buffer); Ren_Print(S_COLOR_YELLOW "WARNING: (libjpeg) %s\n", buffer); /* Let the memory manager delete any temp files before we die */ jpeg_destroy(cinfo); /* Return from libjpeg */ longjmp(mgr->jmpbuf, 23); }
/* ================ R_PrintLongString Workaround for ri.Printf's 1024 characters buffer limit. ================ */ void R_PrintLongString(const char *string) { char buffer[1024]; const char *p = string; int size = strlen(string); while (size > 0) { Q_strncpyz(buffer, p, sizeof(buffer)); Ren_Print("%s", buffer); p += 1023; size -= 1023; } }
void R_FBOList_f(void) { int i; FBO_t *fbo; if (!glConfig2.framebufferObjectAvailable) { Ren_Print("GL_EXT_framebuffer_object is not available.\n"); return; } Ren_Print(" size name\n"); Ren_Print("----------------------------------------------------------\n"); for (i = 0; i < tr.numFBOs; i++) { fbo = tr.fbos[i]; Ren_Print(" %4i: %4i %4i %s\n", i, fbo->width, fbo->height, fbo->name); } Ren_Print(" %i FBOs\n", tr.numFBOs); }
/* ===================== RE_AddRefLightToScene ===================== */ void RE_AddRefLightToScene(const refLight_t *l) { trRefLight_t *light; if (!tr.registered) { return; } if (r_numLights >= MAX_REF_LIGHTS) { Ren_Print("WARNING RE_AddRefLightToScene: Dropping light, reached MAX_REF_LIGHTS\n"); return; } if (l->radius[0] <= 0 && !VectorLength(l->radius) && !VectorLength(l->projTarget)) { return; } if ((unsigned)l->rlType >= RL_MAX_REF_LIGHT_TYPE) { Ren_Drop("RE_AddRefLightToScene: bad rlType %i", l->rlType); } light = &backEndData->lights[r_numLights++]; Com_Memcpy(&light->l, l, sizeof(light->l)); light->isStatic = qfalse; light->additive = qtrue; if (light->l.scale <= 0) { light->l.scale = r_lightScale->value; } if (!HDR_ENABLED()) { if (light->l.scale >= r_lightScale->value) { light->l.scale = r_lightScale->value; } } if (!r_dynamicLightCastShadows->integer && !light->l.inverseShadows) { light->l.noShadows = qtrue; } }
/* ============ R_InitVBOs ============ */ void R_InitVBOs(void) { int dataSize; byte *data; Ren_Print("------- R_InitVBOs -------\n"); Com_InitGrowList(&tr.vbos, 100); Com_InitGrowList(&tr.ibos, 100); dataSize = sizeof(vec4_t) * SHADER_MAX_VERTEXES * 11; data = (byte *)Com_Allocate(dataSize); memset(data, 0, dataSize); tess.vbo = R_CreateVBO("tessVertexArray_VBO", data, dataSize, VBO_USAGE_DYNAMIC); tess.vbo->ofsXYZ = 0; tess.vbo->ofsTexCoords = tess.vbo->ofsXYZ + sizeof(tess.xyz); tess.vbo->ofsLightCoords = tess.vbo->ofsTexCoords + sizeof(tess.texCoords); tess.vbo->ofsTangents = tess.vbo->ofsLightCoords + sizeof(tess.lightCoords); tess.vbo->ofsBinormals = tess.vbo->ofsTangents + sizeof(tess.tangents); tess.vbo->ofsNormals = tess.vbo->ofsBinormals + sizeof(tess.binormals); tess.vbo->ofsColors = tess.vbo->ofsNormals + sizeof(tess.normals); tess.vbo->sizeXYZ = sizeof(tess.xyz); tess.vbo->sizeTangents = sizeof(tess.tangents); tess.vbo->sizeBinormals = sizeof(tess.binormals); tess.vbo->sizeNormals = sizeof(tess.normals); Com_Dealloc(data); dataSize = sizeof(tess.indexes); data = (byte *)Com_Allocate(dataSize); memset(data, 0, dataSize); tess.ibo = R_CreateIBO("tessVertexArray_IBO", data, dataSize, VBO_USAGE_DYNAMIC); Com_Dealloc(data); R_InitUnitCubeVBO(); R_BindNullVBO(); R_BindNullIBO(); GL_CheckErrors(); }
/* RE_AddLightToScene() modified dlight system to support seperate radius and intensity */ void RE_AddLightToScene(const vec3_t org, float radius, float intensity, float r, float g, float b, qhandle_t hShader, int flags) { dlight_t *dl; // early out if (!tr.registered || radius <= 0 || intensity <= 0) { return; } if (r_numdlights >= MAX_DLIGHTS) { Ren_Print("WARNING RE_AddLightToScene: Dropping dlight, reached MAX_DLIGHTS\n"); return; } // allow us to force some dlights under all circumstances if (!(flags & REF_FORCE_DLIGHT)) { if (r_dynamiclight->integer == 0) { return; } } // set up a new dlight dl = &backEndData->dlights[r_numdlights++]; VectorCopy(org, dl->origin); VectorCopy(org, dl->transformed); dl->radius = radius; dl->radiusInverseCubed = (1.0 / dl->radius); dl->radiusInverseCubed = dl->radiusInverseCubed * dl->radiusInverseCubed * dl->radiusInverseCubed; dl->intensity = intensity; dl->color[0] = r; dl->color[1] = g; dl->color[2] = b; dl->shader = R_GetShaderByHandle(hShader); if (dl->shader == tr.defaultShader) { dl->shader = NULL; } dl->flags = flags; }
/** * @brief RE_RenderToTexture * @param[in] textureid * @param[in] x * @param[in] y * @param[in] w * @param[in] h */ void RE_RenderToTexture(int textureid, int x, int y, int w, int h) { renderToTextureCommand_t *cmd; // note: see also Com_GrowListElement checking against tr.images->currentElements if (textureid > tr.numImages || textureid < 0) { Ren_Print("Warning: trap_R_RenderToTexture textureid %d out of range.\n", textureid); return; } cmd = (renderToTextureCommand_t *)R_GetCommandBuffer(sizeof(*cmd)); if (!cmd) { return; } cmd->commandId = RC_RENDERTOTEXTURE; cmd->image = (image_t *) Com_GrowListElement(&tr.images, textureid); cmd->x = x; cmd->y = y; cmd->w = w; cmd->h = h; }
/** * @brief R_RenderGlyph * @param[in] glyph * @param[out] glyphOut * @return */ FT_Bitmap *R_RenderGlyph(FT_GlyphSlot glyph, glyphInfo_t *glyphOut) { FT_Bitmap *bit2; int left, right, width, top, bottom, height, pitch, size; R_GetGlyphInfo(glyph, &left, &right, &width, &top, &bottom, &height, &pitch); if (glyph->format != FT_GLYPH_FORMAT_OUTLINE) { Ren_Print("Non-outline fonts are not supported\n"); return NULL; } size = pitch * height; bit2 = (FT_Bitmap *)ri.Z_Malloc(sizeof(FT_Bitmap)); bit2->width = width; bit2->rows = height; bit2->pitch = pitch; bit2->pixel_mode = FT_PIXEL_MODE_GRAY; bit2->buffer = (unsigned char *)ri.Z_Malloc(size); bit2->num_grays = 256; Com_Memset(bit2->buffer, 0, size); FT_Outline_Translate(&glyph->outline, -left, -bottom); FT_Outline_Get_Bitmap(ftLibrary, &glyph->outline, bit2); glyphOut->height = height; glyphOut->pitch = pitch; glyphOut->top = _TRUNC(glyph->metrics.horiBearingY) + 1; glyphOut->bottom = bottom; glyphOut->xSkip = _TRUNC(glyph->metrics.horiAdvance) + 1; return bit2; }
/* ============== R_CalcBones The list of bones[] should only be built and modified from within here ============== */ void R_CalcBones(mdsHeader_t *header, const refEntity_t *refent, int *boneList, int numBones) { int i; int *boneRefs; float torsoWeight; // if the entity has changed since the last time the bones were built, reset them if (memcmp(&lastBoneEntity, refent, sizeof(refEntity_t))) { // different, cached bones are not valid memset(validBones, 0, header->numBones); lastBoneEntity = *refent; // (SA) also reset these counter statics //----(SA) print stats for the complete model (not per-surface) if (r_bonesDebug->integer == 4 && totalrt) { Ren_Print("Lod %.2f verts %4d/%4d tris %4d/%4d (%.2f%%)\n", lodScale, totalrv, totalv, totalrt, totalt, ( float )(100.0 * totalrt) / (float) totalt); } totalrv = totalrt = totalv = totalt = 0; } memset(newBones, 0, header->numBones); if (refent->oldframe == refent->frame) { backlerp = 0; frontlerp = 1; } else { backlerp = refent->backlerp; frontlerp = 1.0f - backlerp; } if (refent->oldTorsoFrame == refent->torsoFrame) { torsoBacklerp = 0; torsoFrontlerp = 1; } else { torsoBacklerp = refent->torsoBacklerp; torsoFrontlerp = 1.0f - torsoBacklerp; } frameSize = (int) (sizeof(mdsFrame_t) + (header->numBones - 1) * sizeof(mdsBoneFrameCompressed_t)); frame = ( mdsFrame_t * )((byte *)header + header->ofsFrames + refent->frame * frameSize); torsoFrame = ( mdsFrame_t * )((byte *)header + header->ofsFrames + refent->torsoFrame * frameSize); oldFrame = ( mdsFrame_t * )((byte *)header + header->ofsFrames + refent->oldframe * frameSize); oldTorsoFrame = ( mdsFrame_t * )((byte *)header + header->ofsFrames + refent->oldTorsoFrame * frameSize); // lerp all the needed bones (torsoParent is always the first bone in the list) cBoneList = frame->bones; cBoneListTorso = torsoFrame->bones; boneInfo = ( mdsBoneInfo_t * )((byte *)header + header->ofsBones); boneRefs = boneList; // Matrix3Transpose(refent->torsoAxis, torsoAxis); #ifdef HIGH_PRECISION_BONES if (qtrue) { #else if (!backlerp && !torsoBacklerp) { #endif for (i = 0; i < numBones; i++, boneRefs++) { if (validBones[*boneRefs]) { // this bone is still in the cache bones[*boneRefs] = rawBones[*boneRefs]; continue; } // find our parent, and make sure it has been calculated if ((boneInfo[*boneRefs].parent >= 0) && (!validBones[boneInfo[*boneRefs].parent] && !newBones[boneInfo[*boneRefs].parent])) { R_CalcBone(header, refent, boneInfo[*boneRefs].parent); } R_CalcBone(header, refent, *boneRefs); } } else // interpolated { cOldBoneList = oldFrame->bones; cOldBoneListTorso = oldTorsoFrame->bones; for (i = 0; i < numBones; i++, boneRefs++) { if (validBones[*boneRefs]) { // this bone is still in the cache bones[*boneRefs] = rawBones[*boneRefs]; continue; } // find our parent, and make sure it has been calculated if ((boneInfo[*boneRefs].parent >= 0) && (!validBones[boneInfo[*boneRefs].parent] && !newBones[boneInfo[*boneRefs].parent])) { R_CalcBoneLerp(header, refent, boneInfo[*boneRefs].parent); } R_CalcBoneLerp(header, refent, *boneRefs); } } // adjust for torso rotations torsoWeight = 0; boneRefs = boneList; for (i = 0; i < numBones; i++, boneRefs++) { thisBoneInfo = &boneInfo[*boneRefs]; bonePtr = &bones[*boneRefs]; // add torso rotation if (thisBoneInfo->torsoWeight > 0) { if (!newBones[*boneRefs]) { // just copy it back from the previous calc bones[*boneRefs] = oldBones[*boneRefs]; continue; } if (!(thisBoneInfo->flags & BONEFLAG_TAG)) { // 1st multiply with the bone->matrix // 2nd translation for rotation relative to bone around torso parent offset VectorSubtract(bonePtr->translation, torsoParentOffset, t); Matrix4FromAxisPlusTranslation(bonePtr->matrix, t, m1); // 3rd scaled rotation // 4th translate back to torso parent offset // use previously created matrix if available for the same weight if (torsoWeight != thisBoneInfo->torsoWeight) { Matrix4FromScaledAxisPlusTranslation(torsoAxis, thisBoneInfo->torsoWeight, torsoParentOffset, m2); torsoWeight = thisBoneInfo->torsoWeight; } // multiply matrices to create one matrix to do all calculations Matrix4MultiplyInto3x3AndTranslation(m2, m1, bonePtr->matrix, bonePtr->translation); } else // tag's require special handling { // rotate each of the axis by the torsoAngles LocalScaledMatrixTransformVector(bonePtr->matrix[0], thisBoneInfo->torsoWeight, torsoAxis, tmpAxis[0]); LocalScaledMatrixTransformVector(bonePtr->matrix[1], thisBoneInfo->torsoWeight, torsoAxis, tmpAxis[1]); LocalScaledMatrixTransformVector(bonePtr->matrix[2], thisBoneInfo->torsoWeight, torsoAxis, tmpAxis[2]); memcpy(bonePtr->matrix, tmpAxis, sizeof(tmpAxis)); // rotate the translation around the torsoParent VectorSubtract(bonePtr->translation, torsoParentOffset, t); LocalScaledMatrixTransformVector(t, thisBoneInfo->torsoWeight, torsoAxis, bonePtr->translation); VectorAdd(bonePtr->translation, torsoParentOffset, bonePtr->translation); } } } // backup the final bones memcpy(oldBones, bones, sizeof(bones[0]) * header->numBones); } #ifdef DBG_PROFILE_BONES #define DBG_SHOWTIME Ren_Print("%i: %i, ", di++, (dt = ri.Milliseconds()) - ldt); ldt = dt; #else #define DBG_SHOWTIME ; #endif /* ============== RB_SurfaceAnim ============== */ void RB_SurfaceAnim(mdsSurface_t *surface) { int j, k; refEntity_t *refent; int *boneList; mdsHeader_t *header; #ifdef DBG_PROFILE_BONES int di = 0, dt, ldt; dt = ri.Milliseconds(); ldt = dt; #endif refent = &backEnd.currentEntity->e; boneList = ( int * )((byte *)surface + surface->ofsBoneReferences); header = ( mdsHeader_t * )((byte *)surface + surface->ofsHeader); R_CalcBones(header, (const refEntity_t *)refent, boneList, surface->numBoneReferences); DBG_SHOWTIME // calculate LOD // TODO: lerp the radius and origin VectorAdd(refent->origin, frame->localOrigin, vec); lodRadius = frame->radius; lodScale = RB_CalcMDSLod(refent, vec, lodRadius, header->lodBias, header->lodScale); //DBG_SHOWTIME // modification to allow dead skeletal bodies to go below minlod (experiment) if (refent->reFlags & REFLAG_DEAD_LOD) { if (lodScale < 0.35) // allow dead to lod down to 35% (even if below surf->minLod) (%35 is arbitrary and probably not good generally. worked for the blackguard/infantry as a test though) { lodScale = 0.35; } render_count = ROUND_INT(surface->numVerts * lodScale); } else { render_count = ROUND_INT(surface->numVerts * lodScale); if (render_count < surface->minLod) { if (!(refent->reFlags & REFLAG_DEAD_LOD)) { render_count = surface->minLod; } } } if (render_count > surface->numVerts) { render_count = surface->numVerts; } RB_CheckOverflow(render_count, surface->numTriangles); //DBG_SHOWTIME // setup triangle list RB_CheckOverflow(surface->numVerts, surface->numTriangles * 3); //DBG_SHOWTIME collapse_map = ( int * )(( byte * )surface + surface->ofsCollapseMap); triangles = ( int * )((byte *)surface + surface->ofsTriangles); indexes = surface->numTriangles * 3; baseIndex = tess.numIndexes; baseVertex = tess.numVertexes; oldIndexes = baseIndex; tess.numVertexes += render_count; pIndexes = &tess.indexes[baseIndex]; //DBG_SHOWTIME if (render_count == surface->numVerts) { memcpy(pIndexes, triangles, sizeof(triangles[0]) * indexes); if (baseVertex) { glIndex_t *indexesEnd; for (indexesEnd = pIndexes + indexes ; pIndexes < indexesEnd ; pIndexes++) { *pIndexes += baseVertex; } } tess.numIndexes += indexes; } else { int *collapseEnd; pCollapse = collapse; for (j = 0; j < render_count; pCollapse++, j++) { *pCollapse = j; } pCollapseMap = &collapse_map[render_count]; for (collapseEnd = collapse + surface->numVerts ; pCollapse < collapseEnd; pCollapse++, pCollapseMap++) { *pCollapse = collapse[*pCollapseMap]; } for (j = 0 ; j < indexes ; j += 3) { p0 = collapse[*(triangles++)]; p1 = collapse[*(triangles++)]; p2 = collapse[*(triangles++)]; // FIXME // note: serious optimization opportunity here, // by sorting the triangles the following "continue" // could have been made into a "break" statement. if (p0 == p1 || p1 == p2 || p2 == p0) { continue; } *(pIndexes++) = baseVertex + p0; *(pIndexes++) = baseVertex + p1; *(pIndexes++) = baseVertex + p2; tess.numIndexes += 3; } baseIndex = tess.numIndexes; } //DBG_SHOWTIME // deform the vertexes by the lerped bones numVerts = surface->numVerts; v = ( mdsVertex_t * )((byte *)surface + surface->ofsVerts); tempVert = ( float * )(tess.xyz + baseVertex); tempNormal = ( float * )(tess.normal + baseVertex); for (j = 0; j < render_count; j++, tempVert += 4, tempNormal += 4) { mdsWeight_t *w; VectorClear(tempVert); w = v->weights; for (k = 0 ; k < v->numWeights ; k++, w++) { bone = &bones[w->boneIndex]; LocalAddScaledMatrixTransformVectorTranslate(w->offset, w->boneWeight, bone->matrix, bone->translation, tempVert); } LocalMatrixTransformVector(v->normal, bones[v->weights[0].boneIndex].matrix, tempNormal); tess.texCoords0[baseVertex + j].v[0] = v->texCoords[0]; tess.texCoords0[baseVertex + j].v[1] = v->texCoords[1]; v = (mdsVertex_t *)&v->weights[v->numWeights]; } DBG_SHOWTIME if (r_bonesDebug->integer) { if (r_bonesDebug->integer < 3) { int i; // DEBUG: show the bones as a stick figure with axis at each bone boneRefs = ( int * )((byte *)surface + surface->ofsBoneReferences); for (i = 0; i < surface->numBoneReferences; i++, boneRefs++) { bonePtr = &bones[*boneRefs]; GL_Bind(tr.whiteImage); qglLineWidth(1); qglBegin(GL_LINES); for (j = 0; j < 3; j++) { VectorClear(vec); vec[j] = 1; qglColor3fv(vec); qglVertex3fv(bonePtr->translation); VectorMA(bonePtr->translation, 5, bonePtr->matrix[j], vec); qglVertex3fv(vec); } qglEnd(); // connect to our parent if it's valid if (validBones[boneInfo[*boneRefs].parent]) { qglLineWidth(2); qglBegin(GL_LINES); qglColor3f(.6, .6, .6); qglVertex3fv(bonePtr->translation); qglVertex3fv(bones[boneInfo[*boneRefs].parent].translation); qglEnd(); } qglLineWidth(1); } } if (r_bonesDebug->integer == 3 || r_bonesDebug->integer == 4) { int render_indexes = (tess.numIndexes - oldIndexes); // show mesh edges tempVert = ( float * )(tess.xyz + baseVertex); tempNormal = ( float * )(tess.normal + baseVertex); GL_Bind(tr.whiteImage); qglLineWidth(1); qglBegin(GL_LINES); qglColor3f(.0, .0, .8); pIndexes = &tess.indexes[oldIndexes]; for (j = 0; j < render_indexes / 3; j++, pIndexes += 3) { qglVertex3fv(tempVert + 4 * pIndexes[0]); qglVertex3fv(tempVert + 4 * pIndexes[1]); qglVertex3fv(tempVert + 4 * pIndexes[1]); qglVertex3fv(tempVert + 4 * pIndexes[2]); qglVertex3fv(tempVert + 4 * pIndexes[2]); qglVertex3fv(tempVert + 4 * pIndexes[0]); } qglEnd(); // track debug stats if (r_bonesDebug->integer == 4) { totalrv += render_count; totalrt += render_indexes / 3; totalv += surface->numVerts; totalt += surface->numTriangles; } if (r_bonesDebug->integer == 3) { Ren_Print("Lod %.2f verts %4d/%4d tris %4d/%4d (%.2f%%)\n", lodScale, render_count, surface->numVerts, render_indexes / 3, surface->numTriangles, ( float )(100.0 * render_indexes / 3) / (float) surface->numTriangles); } } } if (r_bonesDebug->integer > 1) { // dont draw the actual surface tess.numIndexes = oldIndexes; tess.numVertexes = baseVertex; return; } #ifdef DBG_PROFILE_BONES Ren_Print("\n"); #endif }
/** * @brief RB_EvalExpression * @param[in] exp * @param[in] defaultValue * @return */ float RB_EvalExpression(const expression_t *exp, float defaultValue) { #if 1 int i; expOperation_t op; expOperation_t ops[MAX_EXPRESSION_OPS]; int numOps = 0; float value = 0; float value1 = 0; float value2 = 0; extern const opstring_t opStrings[]; if (!exp || !exp->active) { return defaultValue; } // http://www.qiksearch.com/articles/cs/postfix-evaluation/ // http://www.kyz.uklinux.net/evaluate/ for (i = 0; i < exp->numOps; i++) { op = exp->ops[i]; switch (op.type) { case OP_BAD: return defaultValue; case OP_NEG: { if (numOps < 1) { Ren_Print("WARNING: shader %s has numOps < 1 for unary - operator\n", tess.surfaceShader->name); return defaultValue; } value1 = GetOpValue(&ops[numOps - 1]); numOps--; value = -value1; // push result op.type = OP_NUM; op.value = value; ops[numOps++] = op; break; } case OP_NUM: case OP_TIME: case OP_PARM0: case OP_PARM1: case OP_PARM2: case OP_PARM3: case OP_PARM4: case OP_PARM5: case OP_PARM6: case OP_PARM7: case OP_PARM8: case OP_PARM9: case OP_PARM10: case OP_PARM11: case OP_GLOBAL0: case OP_GLOBAL1: case OP_GLOBAL2: case OP_GLOBAL3: case OP_GLOBAL4: case OP_GLOBAL5: case OP_GLOBAL6: case OP_GLOBAL7: case OP_FRAGMENTSHADERS: case OP_FRAMEBUFFEROBJECTS: case OP_SOUND: case OP_DISTANCE: ops[numOps++] = op; break; case OP_TABLE: { shaderTable_t *table; int numValues; float index; float lerp; int oldIndex; int newIndex; if (numOps < 1) { Ren_Print("WARNING: shader %s has numOps < 1 for table operator\n", tess.surfaceShader->name); return defaultValue; } value1 = GetOpValue(&ops[numOps - 1]); numOps--; table = tr.shaderTables[(int)op.value]; numValues = table->numValues; index = value1 * numValues; // float index into the table?s elements lerp = index - floor(index); // being inbetween two elements of the table oldIndex = (int)index; newIndex = (int)index + 1; if (table->clamp) { // clamp indices to table-range Q_clamp(oldIndex, 0, numValues - 1); Q_clamp(newIndex, 0, numValues - 1); } else { // wrap around indices oldIndex %= numValues; newIndex %= numValues; } if (table->snap) { // use fixed value value = table->values[oldIndex]; } else { // lerp value value = table->values[oldIndex] + ((table->values[newIndex] - table->values[oldIndex]) * lerp); } //Ren_Print("%s: %i %i %f\n", table->name, oldIndex, newIndex, value); // push result op.type = OP_NUM; op.value = value; ops[numOps++] = op; break; } default: { if (numOps < 2) { Ren_Print("WARNING: shader %s has numOps < 2 for binary operator %s\n", tess.surfaceShader->name, opStrings[op.type].s); return defaultValue; } value2 = GetOpValue(&ops[numOps - 1]); numOps--; value1 = GetOpValue(&ops[numOps - 1]); numOps--; switch (op.type) { case OP_LAND: value = value1 && value2; break; case OP_LOR: value = value1 || value2; break; case OP_GE: value = value1 >= value2; break; case OP_LE: value = value1 <= value2; break; case OP_LEQ: value = value1 == value2; break; case OP_LNE: value = value1 != value2; break; case OP_ADD: value = value1 + value2; break; case OP_SUB: value = value1 - value2; break; case OP_DIV: if (value2 == 0) { // don't divide by zero value = value1; } else { value = value1 / value2; } break; case OP_MOD: value = (float)((int)value1 % (int)value2); break; case OP_MUL: value = value1 * value2; break; case OP_LT: value = value1 < value2; break; case OP_GT: value = value1 > value2; break; default: value = value1 = value2 = 0; break; } //Ren_Print("%s: %f %f %f\n", opStrings[op.type].s, value, value1, value2); // push result op.type = OP_NUM; op.value = value; ops[numOps++] = op; break; } } } return GetOpValue(&ops[0]); #else return defaultValue; #endif }
/* ===================== RE_AddDynamicLightToScene modified dlight system to support seperate radius and intensity ===================== */ void RE_AddDynamicLightToScene(const vec3_t org, float radius, float intensity, float r, float g, float b, qhandle_t hShader, int flags) { trRefLight_t *light; if (!tr.registered) { return; } if (r_numLights >= MAX_REF_LIGHTS) { Ren_Print("WARNING RE_AddDynamicLightToScene: Dropping light, reached MAX_REF_LIGHTS\n"); return; } if (intensity <= 0 || radius <= 0) { return; } light = &backEndData->lights[r_numLights++]; light->l.rlType = RL_OMNI; //light->l.lightfx = 0; VectorCopy(org, light->l.origin); QuatClear(light->l.rotation); VectorClear(light->l.center); // HACK: this will tell the renderer backend to use tr.defaultLightShader #if 0 dl->shader = R_GetShaderByHandle(hShader); if (dl->shader == tr.defaultShader) { dl->shader = NULL; } #endif light->l.attenuationShader = 0; light->l.radius[0] = radius; light->l.radius[1] = radius; light->l.radius[2] = radius; light->l.color[0] = r; light->l.color[1] = g; light->l.color[2] = b; light->l.noShadows = r_dynamicLightCastShadows->integer ? qfalse : qtrue; light->l.inverseShadows = qfalse; light->isStatic = qfalse; light->additive = qtrue; light->l.scale = intensity; #if 0 if (light->l.scale <= r_lightScale->value) { light->l.scale = r_lightScale->value; } #endif }
/** * @brief Stretches a raw 32 bit power of 2 bitmap image over the given screen rectangle. * Used for cinematics. * * @param[in] x * @param[in] y * @param[in] w * @param[in] h * @param[in] cols * @param[in] rows * @param[in] data * @param[in] client * @param[in] dirty * * @todo FIXME: not exactly backend */ void RE_StretchRaw(int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { int i, j; int start; if (!tr.registered) { return; } R_IssuePendingRenderCommands(); // we definately want to sync every frame for the cinematics qglFinish(); start = 0; if (r_speeds->integer) { start = ri.Milliseconds(); } if (!GL_ARB_texture_non_power_of_two) { // make sure rows and cols are powers of 2 for (i = 0; (1 << i) < cols; i++) { } for (j = 0; (1 << j) < rows; j++) { } if ((1 << i) != cols || (1 << j) != rows) { Ren_Drop("Draw_StretchRaw: size not a power of 2: %i by %i", cols, rows); } } GL_Bind(tr.scratchImage[client]); // if the scratchImage isn't in the format we want, specify it as a new texture if (cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height) { tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; qglTexImage2D(GL_TEXTURE_2D, 0, 3, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } else { if (dirty) { // otherwise, just subimage upload it so that drivers can tell we are going to be changing // it and don't try and do a texture compression qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data); } } if (r_speeds->integer) { int end = ri.Milliseconds(); Ren_Print("qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start); } RB_SetGL2D(); qglColor3f(tr.identityLight, tr.identityLight, tr.identityLight); qglBegin(GL_QUADS); qglTexCoord2f(0.5f / cols, 0.5f / rows); qglVertex2f(x, y); qglTexCoord2f((cols - 0.5f) / cols, 0.5f / rows); qglVertex2f(x + w, y); qglTexCoord2f((cols - 0.5f) / cols, (rows - 0.5f) / rows); qglVertex2f(x + w, y + h); qglTexCoord2f(0.5f / cols, (rows - 0.5f) / rows); qglVertex2f(x, y + h); qglEnd(); }
/** * @brief Creates a new decal projector from a triangle. * * Projected polygons should be 3 or 4 points. * * If a single point is passed in (numPoints == 1) then the decal will be omnidirectional * omnidirectional decals use points[ 0 ] as center and projection[ 3 ] as radius * pass in lifeTime < 0 for a temporary mark. * * @param[in] hShader * @param[in] numPoints * @param[in] points * @param[in] projection * @param[in] color * @param[in] lifeTime * @param[in] fadeTime */ void RE_ProjectDecal(qhandle_t hShader, int numPoints, vec3_t *points, vec4_t projection, vec4_t color, int lifeTime, int fadeTime) { static int totalProjectors = 0; vec3_t xyz; decalVert_t dv[4]; int i; decalProjector_t *dp, temp; if (r_numDecalProjectors >= MAX_DECAL_PROJECTORS) { Ren_Print("WARNING: RE_ProjectDecal() Max decal projectors reached (%d)\n", MAX_DECAL_PROJECTORS); return; } // dummy check if (numPoints != 1 && numPoints != 3 && numPoints != 4) { Ren_Print("WARNING: RE_ProjectDecal() Invalid number of decal points (%d)\n", numPoints); return; } // early outs if (lifeTime == 0) { Ren_Developer("WARNING: RE_ProjectDecal() lifeTime == 0\n"); // modders should have a look at this - vanilla does these calls return; } if (projection[3] <= 0.0f) { Ren_Print("WARNING: RE_ProjectDecal() projection[3] <= 0.0f\n"); return; } // set times properly if (lifeTime < 0 || fadeTime < 0) { lifeTime = 0; fadeTime = 0; } // basic setup temp.shader = R_GetShaderByHandle(hShader); temp.color[0] = (byte)(color[0] * 255); temp.color[1] = (byte)(color[1] * 255); temp.color[2] = (byte)(color[2] * 255); temp.color[3] = (byte)(color[3] * 255); temp.numPlanes = numPoints + 2; temp.fadeStartTime = tr.refdef.time + lifeTime - fadeTime; // FIXME: stale refdef time temp.fadeEndTime = temp.fadeStartTime + fadeTime; temp.projectorNum = 0; // set up decal texcoords (FIXME: support arbitrary projector st coordinates in trapcall) dv[0].st[0] = 0.0f; dv[0].st[1] = 0.0f; dv[1].st[0] = 0.0f; dv[1].st[1] = 1.0f; dv[2].st[0] = 1.0f; dv[2].st[1] = 1.0f; dv[3].st[0] = 1.0f; dv[3].st[1] = 0.0f; // omnidirectional? if (numPoints == 1) { float radius; float iDist; // set up omnidirectional numPoints = 4; temp.numPlanes = 6; temp.omnidirectional = qtrue; radius = projection[3]; Vector4Set(projection, 0.0f, 0.0f, -1.0f, radius * 2.0f); iDist = 1.0f / (radius * 2.0f); // set corner VectorSet(xyz, points[0][0] - radius, points[0][1] - radius, points[0][2] + radius); // make x axis texture matrix (yz) VectorSet(temp.texMat[0][0], 0.0f, iDist, 0.0f); temp.texMat[0][0][3] = -DotProduct(temp.texMat[0][0], xyz); VectorSet(temp.texMat[0][1], 0.0f, 0.0f, iDist); temp.texMat[0][1][3] = -DotProduct(temp.texMat[0][1], xyz); // make y axis texture matrix (xz) VectorSet(temp.texMat[1][0], iDist, 0.0f, 0.0f); temp.texMat[1][0][3] = -DotProduct(temp.texMat[1][0], xyz); VectorSet(temp.texMat[1][1], 0.0f, 0.0f, iDist); temp.texMat[1][1][3] = -DotProduct(temp.texMat[1][1], xyz); // make z axis texture matrix (xy) VectorSet(temp.texMat[2][0], iDist, 0.0f, 0.0f); temp.texMat[2][0][3] = -DotProduct(temp.texMat[2][0], xyz); VectorSet(temp.texMat[2][1], 0.0f, iDist, 0.0f); temp.texMat[2][1][3] = -DotProduct(temp.texMat[2][1], xyz); // setup decal points VectorSet(dv[0].xyz, points[0][0] - radius, points[0][1] - radius, points[0][2] + radius); VectorSet(dv[1].xyz, points[0][0] - radius, points[0][1] + radius, points[0][2] + radius); VectorSet(dv[2].xyz, points[0][0] + radius, points[0][1] + radius, points[0][2] + radius); VectorSet(dv[3].xyz, points[0][0] + radius, points[0][1] - radius, points[0][2] + radius); } else { // set up unidirectional temp.omnidirectional = qfalse; // set up decal points VectorCopy(points[0], dv[0].xyz); VectorCopy(points[1], dv[1].xyz); VectorCopy(points[2], dv[2].xyz); VectorCopy(points[3], dv[3].xyz); // make texture matrix if (!MakeTextureMatrix(temp.texMat[0], projection, &dv[0], &dv[1], &dv[2])) { Ren_Print("WARNING: RE_ProjectDecal() MakeTextureMatrix returns NULL\n"); return; } } // bound the projector ClearBounds(temp.mins, temp.maxs); for (i = 0; i < numPoints; i++) { AddPointToBounds(dv[i].xyz, temp.mins, temp.maxs); VectorMA(dv[i].xyz, projection[3], projection, xyz); AddPointToBounds(xyz, temp.mins, temp.maxs); } // make bounding sphere VectorAdd(temp.mins, temp.maxs, temp.center); VectorScale(temp.center, 0.5f, temp.center); VectorSubtract(temp.maxs, temp.center, xyz); temp.radius = VectorLength(xyz); temp.radius2 = temp.radius * temp.radius; // make the front plane if (!PlaneFromPoints(temp.planes[0], dv[0].xyz, dv[1].xyz, dv[2].xyz)) { Ren_Developer("WARNING: RE_ProjectDecal() PlaneFromPoints is NULL\n"); // occurs on UJE_fueldump return; } // make the back plane VectorSubtract(vec3_origin, temp.planes[0], temp.planes[1]); VectorMA(dv[0].xyz, projection[3], projection, xyz); temp.planes[1][3] = DotProduct(xyz, temp.planes[1]); // make the side planes for (i = 0; i < numPoints; i++) { VectorMA(dv[i].xyz, projection[3], projection, xyz); if (!PlaneFromPoints(temp.planes[i + 2], dv[(i + 1) % numPoints].xyz, dv[i].xyz, xyz)) { Ren_Developer("WARNING: RE_ProjectDecal() a side plane is NULL\n"); // occurs on map venice return; } } // create a new projector dp = &backEndData->decalProjectors[r_numDecalProjectors]; Com_Memcpy(dp, &temp, sizeof(*dp)); dp->projectorNum = totalProjectors++; // we have a winner r_numDecalProjectors++; }
/* ============ R_VBOList_f ============ */ void R_VBOList_f(void) { int i; VBO_t *vbo; IBO_t *ibo; int vertexesSize = 0; int indexesSize = 0; Ren_Print(" size name\n"); Ren_Print("----------------------------------------------------------\n"); for (i = 0; i < tr.vbos.currentElements; i++) { vbo = (VBO_t *) Com_GrowListElement(&tr.vbos, i); Ren_Print("%d.%02d MB %s\n", vbo->vertexesSize / (1024 * 1024), (vbo->vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024), vbo->name); vertexesSize += vbo->vertexesSize; } #if defined(USE_BSP_CLUSTERSURFACE_MERGING) if (tr.world) { int j; for (j = 0; j < MAX_VISCOUNTS; j++) { // FIXME: clean up this code for (i = 0; i < tr.world->clusterVBOSurfaces[j].currentElements; i++) { srfVBOMesh_t *vboSurf; vboSurf = (srfVBOMesh_t *) Com_GrowListElement(&tr.world->clusterVBOSurfaces[j], i); ibo = vboSurf->ibo; Ren_Print("%d.%02d MB %s\n", ibo->indexesSize / (1024 * 1024), (ibo->indexesSize % (1024 * 1024)) * 100 / (1024 * 1024), ibo->name); indexesSize += ibo->indexesSize; } } } #endif // #if defined(USE_BSP_CLUSTERSURFACE_MERGING) for (i = 0; i < tr.ibos.currentElements; i++) { ibo = (IBO_t *) Com_GrowListElement(&tr.ibos, i); Ren_Print("%d.%02d MB %s\n", ibo->indexesSize / (1024 * 1024), (ibo->indexesSize % (1024 * 1024)) * 100 / (1024 * 1024), ibo->name); indexesSize += ibo->indexesSize; } Ren_Print(" %i total VBOs\n", tr.vbos.currentElements); Ren_Print(" %d.%02d MB total vertices memory\n", vertexesSize / (1024 * 1024), (vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024)); Ren_Print(" %i total IBOs\n", tr.ibos.currentElements); Ren_Print(" %d.%02d MB total triangle indices memory\n", indexesSize / (1024 * 1024), (indexesSize % (1024 * 1024)) * 100 / (1024 * 1024)); }
/* ============ R_ShutdownVBOs ============ */ void R_ShutdownVBOs(void) { int i; VBO_t *vbo; IBO_t *ibo; Ren_Print("------- R_ShutdownVBOs -------\n"); R_BindNullVBO(); R_BindNullIBO(); for (i = 0; i < tr.vbos.currentElements; i++) { vbo = (VBO_t *) Com_GrowListElement(&tr.vbos, i); if (vbo->vertexesVBO) { glDeleteBuffers(1, &vbo->vertexesVBO); } } for (i = 0; i < tr.ibos.currentElements; i++) { ibo = (IBO_t *) Com_GrowListElement(&tr.ibos, i); if (ibo->indexesVBO) { glDeleteBuffers(1, &ibo->indexesVBO); } } #if defined(USE_BSP_CLUSTERSURFACE_MERGING) if (tr.world) { int j; for (j = 0; j < MAX_VISCOUNTS; j++) { // FIXME: clean up this code for (i = 0; i < tr.world->clusterVBOSurfaces[j].currentElements; i++) { srfVBOMesh_t *vboSurf; vboSurf = (srfVBOMesh_t *) Com_GrowListElement(&tr.world->clusterVBOSurfaces[j], i); ibo = vboSurf->ibo; if (ibo->indexesVBO) { glDeleteBuffers(1, &ibo->indexesVBO); } } Com_DestroyGrowList(&tr.world->clusterVBOSurfaces[j]); } } #endif // #if defined(USE_BSP_CLUSTERSURFACE_MERGING) Com_DestroyGrowList(&tr.vbos); Com_DestroyGrowList(&tr.ibos); }
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; }