FBO_t *R_CreateFBO(const char *name, int width, int height) { FBO_t *fbo; if (strlen(name) >= MAX_QPATH) { Ren_Drop("R_CreateFBO: \"%s\" is too long\n", name); } if (width <= 0 || width > glConfig2.maxRenderbufferSize) { Ren_Drop("R_CreateFBO: bad width %i", width); } if (height <= 0 || height > glConfig2.maxRenderbufferSize) { Ren_Drop("R_CreateFBO: bad height %i", height); } if (tr.numFBOs == MAX_FBOS) { Ren_Drop("R_CreateFBO: MAX_FBOS hit"); } fbo = tr.fbos[tr.numFBOs] = (FBO_t *)ri.Hunk_Alloc(sizeof(*fbo), h_low); Q_strncpyz(fbo->name, name, sizeof(fbo->name)); fbo->index = tr.numFBOs++; fbo->width = width; fbo->height = height; glGenFramebuffers(1, &fbo->frameBuffer); return fbo; }
/** * @brief GL_TexEnv * @param[in] env */ void GL_TexEnv(int env) { if (env == glState.texEnv[glState.currenttmu]) { return; } glState.texEnv[glState.currenttmu] = env; switch (env) { case GL_MODULATE: qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); break; case GL_REPLACE: qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); break; case GL_DECAL: qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); break; case GL_ADD: qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD); break; default: Ren_Drop("GL_TexEnv: invalid env '%d' passed\n", env); } }
/* ============ R_BindVBO ============ */ void R_BindVBO(VBO_t *vbo) { if (!vbo) { //R_BindNullVBO(); Ren_Drop("R_BindNullVBO: NULL vbo"); return; } Ren_LogComment("--- R_BindVBO( %s ) ---\n", vbo->name); if (glState.currentVBO != vbo) { glState.currentVBO = vbo; glState.vertexAttribPointersSet = 0; glState.vertexAttribsInterpolation = 0; glState.vertexAttribsOldFrame = 0; glState.vertexAttribsNewFrame = 0; glBindBuffer(GL_ARRAY_BUFFER, vbo->vertexesVBO); backEnd.pc.c_vboVertexBuffers++; //GL_VertexAttribPointers(ATTR_BITS); } }
/** * @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_SelectTexture * @param[in] unit */ void GL_SelectTexture(int unit) { if (glState.currenttmu == unit) { return; } if (unit == 0) { qglActiveTextureARB(GL_TEXTURE0_ARB); Ren_LogComment("glActiveTextureARB( GL_TEXTURE0_ARB )\n"); qglClientActiveTextureARB(GL_TEXTURE0_ARB); Ren_LogComment("glClientActiveTextureARB( GL_TEXTURE0_ARB )\n"); } else if (unit == 1) { qglActiveTextureARB(GL_TEXTURE1_ARB); Ren_LogComment("glActiveTextureARB( GL_TEXTURE1_ARB )\n"); qglClientActiveTextureARB(GL_TEXTURE1_ARB); Ren_LogComment("glClientActiveTextureARB( GL_TEXTURE1_ARB )\n"); } else { Ren_Drop("GL_SelectTexture: unit = %i", unit); } glState.currenttmu = unit; }
void *Com_GrowListElement(const growList_t *list, int index) { if (index < 0 || index >= list->currentElements) { Ren_Drop("Com_GrowListElement: %i out of range of %i", index, list->currentElements); } return list->elements[index]; }
void GL_PushMatrix() { glState.stackIndex++; if (glState.stackIndex >= MAX_GLSTACK) { glState.stackIndex = MAX_GLSTACK - 1; Ren_Drop("GL_PushMatrix: stack overflow = %i", glState.stackIndex); } }
void GL_PopMatrix() { glState.stackIndex--; if (glState.stackIndex < 0) { glState.stackIndex = 0; Ren_Drop("GL_PopMatrix: stack underflow"); } }
static void FillCloudySkySide(const int mins[2], const int maxs[2], qboolean addIndexes) { int s, t; int vertexStart = tess.numVertexes; int tHeight = maxs[1] - mins[1] + 1; int sWidth = maxs[0] - mins[0] + 1; // overflow check RB_CHECKOVERFLOW((maxs[0] - mins[0]) * (maxs[1] - mins[1]), (sWidth - 1) * (tHeight - 1) * 6); for (t = mins[1] + HALF_SKY_SUBDIVISIONS; t <= maxs[1] + HALF_SKY_SUBDIVISIONS; t++) { for (s = mins[0] + HALF_SKY_SUBDIVISIONS; s <= maxs[0] + HALF_SKY_SUBDIVISIONS; s++) { VectorAdd(s_skyPoints[t][s], backEnd.viewParms.orientation.origin, tess.xyz[tess.numVertexes].v); tess.texCoords0[tess.numVertexes].v[0] = s_skyTexCoords[t][s][0]; tess.texCoords0[tess.numVertexes].v[1] = s_skyTexCoords[t][s][1]; tess.numVertexes++; if (tess.numVertexes >= tess.maxShaderVerts) { Ren_Drop("tess.maxShaderVerts(%i) hit in FillCloudySkySide()\n", tess.maxShaderVerts); } } } // only add indexes for one pass, otherwise it would draw multiple times for each pass if (addIndexes) { for (t = 0; t < tHeight - 1; t++) { for (s = 0; s < sWidth - 1; s++) { tess.indexes[tess.numIndexes] = vertexStart + s + t * (sWidth); tess.numIndexes++; tess.indexes[tess.numIndexes] = vertexStart + s + (t + 1) * (sWidth); tess.numIndexes++; tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * (sWidth); tess.numIndexes++; tess.indexes[tess.numIndexes] = vertexStart + s + (t + 1) * (sWidth); tess.numIndexes++; tess.indexes[tess.numIndexes] = vertexStart + s + 1 + (t + 1) * (sWidth); tess.numIndexes++; tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * (sWidth); tess.numIndexes++; } } } }
/* ===================== 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_CreateIBO ============ */ IBO_t *R_CreateIBO(const char *name, byte *indexes, int indexesSize, vboUsage_t usage) { IBO_t *ibo; int glUsage; switch (usage) { case VBO_USAGE_STATIC: glUsage = GL_STATIC_DRAW; break; case VBO_USAGE_DYNAMIC: glUsage = GL_DYNAMIC_DRAW; break; default: glUsage = 0; Ren_Fatal("bad vboUsage_t given: %i", usage); break; } if (strlen(name) >= MAX_QPATH) { Ren_Drop("R_CreateIBO: \"%s\" is too long\n", name); } // make sure the render thread is stopped R_IssuePendingRenderCommands(); ibo = (IBO_t *)ri.Hunk_Alloc(sizeof(*ibo), h_low); Com_AddToGrowList(&tr.ibos, ibo); Q_strncpyz(ibo->name, name, sizeof(ibo->name)); ibo->indexesSize = indexesSize; glGenBuffers(1, &ibo->indexesVBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->indexesVBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexesSize, indexes, glUsage); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GL_CheckErrors(); return ibo; }
int Com_AddToGrowList(growList_t *list, void *data) { void **old; if (list->currentElements != list->maxElements) { list->elements[list->currentElements] = data; return list->currentElements++; } // grow, reallocate and move old = list->elements; if (list->maxElements < 0) { Ren_Fatal("Com_AddToGrowList: maxElements = %i", list->maxElements); } if (list->maxElements == 0) { // initialize the list to hold 100 elements Com_InitGrowList(list, 100); return Com_AddToGrowList(list, data); } list->maxElements *= 2; //Com_DPrintf("Resizing growlist to %i maxElements\n", list->maxElements); list->elements = (void **)Com_Allocate(list->maxElements * sizeof(void *)); if (!list->elements) { Ren_Drop("Growlist alloc failed"); } Com_Memcpy(list->elements, old, list->currentElements * sizeof(void *)); Com_Dealloc(old); return Com_AddToGrowList(list, data); }
void GL_SelectTexture(int unit) { if (glState.currenttmu == unit) { return; } if (unit >= 0 && unit <= 31) { glActiveTexture(GL_TEXTURE0 + unit); Ren_LogComment("glActiveTexture( GL_TEXTURE%i )\n", unit); } else { Ren_Drop("GL_SelectTexture: unit = %i", unit); } glState.currenttmu = unit; }
/* ============ R_BindIBO ============ */ void R_BindIBO(IBO_t *ibo) { if (!ibo) { //R_BindNullIBO(); Ren_Drop("R_BindIBO: NULL ibo"); return; } Ren_LogComment("--- R_BindIBO( %s ) ---\n", ibo->name); if (glState.currentIBO != ibo) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->indexesVBO); glState.currentIBO = ibo; backEnd.pc.c_vboIndexBuffers++; } }
/* ===================== 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); }
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; }
/* ============ R_CreateVBO ============ */ VBO_t *R_CreateVBO(const char *name, byte *vertexes, int vertexesSize, vboUsage_t usage) { VBO_t *vbo; int glUsage; switch (usage) { case VBO_USAGE_STATIC: glUsage = GL_STATIC_DRAW; break; case VBO_USAGE_DYNAMIC: glUsage = GL_DYNAMIC_DRAW; break; default: glUsage = 0; //Prevents warning Ren_Fatal("bad vboUsage_t given: %i", usage); break; } if (strlen(name) >= MAX_QPATH) { Ren_Drop("R_CreateVBO: \"%s\" is too long\n", name); } // make sure the render thread is stopped R_IssuePendingRenderCommands(); vbo = (VBO_t *)ri.Hunk_Alloc(sizeof(*vbo), h_low); Com_AddToGrowList(&tr.vbos, vbo); Q_strncpyz(vbo->name, name, sizeof(vbo->name)); vbo->ofsXYZ = 0; vbo->ofsTexCoords = 0; vbo->ofsLightCoords = 0; vbo->ofsBinormals = 0; vbo->ofsTangents = 0; vbo->ofsNormals = 0; vbo->ofsColors = 0; vbo->ofsPaintColors = 0; vbo->ofsLightDirections = 0; vbo->ofsBoneIndexes = 0; vbo->ofsBoneWeights = 0; vbo->sizeXYZ = 0; vbo->sizeTangents = 0; vbo->sizeBinormals = 0; vbo->sizeNormals = 0; vbo->vertexesSize = vertexesSize; glGenBuffers(1, &vbo->vertexesVBO); glBindBuffer(GL_ARRAY_BUFFER, vbo->vertexesVBO); glBufferData(GL_ARRAY_BUFFER, vertexesSize, vertexes, glUsage); glBindBuffer(GL_ARRAY_BUFFER, 0); GL_CheckErrors(); return vbo; }
/* ============ R_CreateVBO2 ============ */ VBO_t *R_CreateVBO2(const char *name, int numVertexes, srfVert_t *verts, unsigned int stateBits, vboUsage_t usage) { VBO_t *vbo; int i, j; byte *data; int dataSize; int dataOfs; int glUsage; unsigned int bits; switch (usage) { case VBO_USAGE_STATIC: glUsage = GL_STATIC_DRAW; break; case VBO_USAGE_DYNAMIC: glUsage = GL_DYNAMIC_DRAW; break; default: glUsage = 0; Ren_Fatal("bad vboUsage_t given: %i", usage); break; } if (!numVertexes) { return NULL; } if (strlen(name) >= MAX_QPATH) { Ren_Drop("R_CreateVBO2: \"%s\" is too long\n", name); } // make sure the render thread is stopped R_IssuePendingRenderCommands(); vbo = (VBO_t *)ri.Hunk_Alloc(sizeof(*vbo), h_low); Com_AddToGrowList(&tr.vbos, vbo); Q_strncpyz(vbo->name, name, sizeof(vbo->name)); vbo->ofsXYZ = 0; vbo->ofsTexCoords = 0; vbo->ofsLightCoords = 0; vbo->ofsBinormals = 0; vbo->ofsTangents = 0; vbo->ofsNormals = 0; vbo->ofsColors = 0; vbo->ofsPaintColors = 0; vbo->ofsLightDirections = 0; vbo->ofsBoneIndexes = 0; vbo->ofsBoneWeights = 0; vbo->sizeXYZ = 0; vbo->sizeTangents = 0; vbo->sizeBinormals = 0; vbo->sizeNormals = 0; // size VBO dataSize = 0; bits = stateBits; while (bits) { if (bits & 1) { dataSize += sizeof(vec4_t); } bits >>= 1; } dataSize *= numVertexes; data = (byte *)ri.Hunk_AllocateTempMemory(dataSize); dataOfs = 0; // since this is all float, point tmp directly into data // 2-entry -> { memb[0], memb[1], 0, 1 } // 3-entry -> { memb[0], memb[1], memb[2], 1 } #define VERTEXSIZE(memb) (sizeof(verts->memb) / sizeof(verts->memb[0])) #define VERTEXCOPY(memb) \ do { \ vec_t *tmp = (vec_t *) (data + dataOfs); \ for (i = 0; i < numVertexes; i++) \ { \ for (j = 0; j < VERTEXSIZE(memb); j++) { *tmp++ = verts[i].memb[j]; } \ if (VERTEXSIZE(memb) < 3) { *tmp++ = 0; } \ if (VERTEXSIZE(memb) < 4) { *tmp++ = 1; } \ } \ dataOfs += i * sizeof(vec4_t); \ } while (0) if (stateBits & ATTR_POSITION) { vbo->ofsXYZ = dataOfs; VERTEXCOPY(xyz); } // feed vertex texcoords if (stateBits & ATTR_TEXCOORD) { vbo->ofsTexCoords = dataOfs; VERTEXCOPY(st); } // feed vertex lightmap texcoords if (stateBits & ATTR_LIGHTCOORD) { vbo->ofsLightCoords = dataOfs; VERTEXCOPY(lightmap); } // feed vertex tangents if (stateBits & ATTR_TANGENT) { vbo->ofsTangents = dataOfs; VERTEXCOPY(tangent); } // feed vertex binormals if (stateBits & ATTR_BINORMAL) { vbo->ofsBinormals = dataOfs; VERTEXCOPY(binormal); } // feed vertex normals if (stateBits & ATTR_NORMAL) { vbo->ofsNormals = dataOfs; VERTEXCOPY(normal); } // feed vertex colors if (stateBits & ATTR_COLOR) { vbo->ofsColors = dataOfs; VERTEXCOPY(lightColor); } vbo->vertexesSize = dataSize; vbo->vertexesNum = numVertexes; glGenBuffers(1, &vbo->vertexesVBO); glBindBuffer(GL_ARRAY_BUFFER, vbo->vertexesVBO); glBufferData(GL_ARRAY_BUFFER, dataSize, data, glUsage); glBindBuffer(GL_ARRAY_BUFFER, 0); GL_CheckErrors(); ri.Hunk_FreeTempMemory(data); return vbo; }
/** * @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; }
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); }
/** * @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 This routine is responsible for setting the most commonly changed state in Q3. * @param[in] stateBits */ void GL_State(unsigned long stateBits) { unsigned long diff = stateBits ^ glState.glStateBits; if (!diff) { return; } // check depthFunc bits if (diff & GLS_DEPTHFUNC_EQUAL) { if (stateBits & GLS_DEPTHFUNC_EQUAL) { qglDepthFunc(GL_EQUAL); } else { qglDepthFunc(GL_LEQUAL); } } // check blend bits if (diff & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS)) { GLenum srcFactor, dstFactor; if (stateBits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS)) { switch (stateBits & GLS_SRCBLEND_BITS) { case GLS_SRCBLEND_ZERO: srcFactor = GL_ZERO; break; case GLS_SRCBLEND_ONE: srcFactor = GL_ONE; break; case GLS_SRCBLEND_DST_COLOR: srcFactor = GL_DST_COLOR; break; case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: srcFactor = GL_ONE_MINUS_DST_COLOR; break; case GLS_SRCBLEND_SRC_ALPHA: srcFactor = GL_SRC_ALPHA; break; case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: srcFactor = GL_ONE_MINUS_SRC_ALPHA; break; case GLS_SRCBLEND_DST_ALPHA: srcFactor = GL_DST_ALPHA; break; case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA: srcFactor = GL_ONE_MINUS_DST_ALPHA; break; case GLS_SRCBLEND_ALPHA_SATURATE: srcFactor = GL_SRC_ALPHA_SATURATE; break; default: srcFactor = GL_ONE; // to get warning to shut up Ren_Drop("GL_State: invalid src blend state bits\n"); } switch (stateBits & GLS_DSTBLEND_BITS) { case GLS_DSTBLEND_ZERO: dstFactor = GL_ZERO; break; case GLS_DSTBLEND_ONE: dstFactor = GL_ONE; break; case GLS_DSTBLEND_SRC_COLOR: dstFactor = GL_SRC_COLOR; break; case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: dstFactor = GL_ONE_MINUS_SRC_COLOR; break; case GLS_DSTBLEND_SRC_ALPHA: dstFactor = GL_SRC_ALPHA; break; case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: dstFactor = GL_ONE_MINUS_SRC_ALPHA; break; case GLS_DSTBLEND_DST_ALPHA: dstFactor = GL_DST_ALPHA; break; case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA: dstFactor = GL_ONE_MINUS_DST_ALPHA; break; default: dstFactor = GL_ONE; // to get warning to shut up Ren_Drop("GL_State: invalid dst blend state bits\n"); } qglEnable(GL_BLEND); qglBlendFunc(srcFactor, dstFactor); } else { qglDisable(GL_BLEND); } } // check depthmask if (diff & GLS_DEPTHMASK_TRUE) { if (stateBits & GLS_DEPTHMASK_TRUE) { qglDepthMask(GL_TRUE); } else { qglDepthMask(GL_FALSE); } } // fill/line mode if (diff & GLS_POLYMODE_LINE) { if (stateBits & GLS_POLYMODE_LINE) { qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } else { qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } } // depthtest if (diff & GLS_DEPTHTEST_DISABLE) { if (stateBits & GLS_DEPTHTEST_DISABLE) { qglDisable(GL_DEPTH_TEST); } else { qglEnable(GL_DEPTH_TEST); } } // alpha test if (diff & GLS_ATEST_BITS) { switch (stateBits & GLS_ATEST_BITS) { case 0: qglDisable(GL_ALPHA_TEST); break; case GLS_ATEST_GT_0: qglEnable(GL_ALPHA_TEST); qglAlphaFunc(GL_GREATER, 0.0f); break; case GLS_ATEST_LT_80: qglEnable(GL_ALPHA_TEST); qglAlphaFunc(GL_LESS, 0.5f); break; case GLS_ATEST_GE_80: qglEnable(GL_ALPHA_TEST); qglAlphaFunc(GL_GEQUAL, 0.5f); break; default: etl_assert(0); break; } } glState.glStateBits = stateBits; }
void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *height, byte alphaByte) { /* This struct contains the JPEG decompression parameters and pointers to * working space (which is allocated as needed by the JPEG library). */ struct jpeg_decompress_struct cinfo = { NULL }; /* We use our private extension JPEG error handler. * Note that this struct must live as long as the main JPEG parameter * struct, to avoid dangling-pointer problems. */ my_jpeg_error_mgr jerr; /* More stuff */ JSAMPARRAY buffer; /* Output row buffer */ unsigned int row_stride; /* physical row width in output buffer */ unsigned int pixelcount, memcount; unsigned int sindex, dindex; byte *out; int len; union { byte *b; void *v; } fbuffer; byte *buf; /* In this example we want to open the input file before doing anything else, * so that the setjmp() error recovery below can assume the file is open. * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to read binary files. */ len = ri.FS_ReadFile(( char * ) filename, &fbuffer.v); if (!fbuffer.b || len < 0) { return; } /* Step 1: allocate and initialize JPEG decompression object */ /* We have to set up the error handler first, in case the initialization * step fails. (Unlikely, but it could happen if you are out of memory.) * This routine fills in the contents of struct jerr, and returns jerr's * address which we place into the link field in cinfo. */ cinfo.err = jpeg_std_error(&jerr.pub); cinfo.err->error_exit = R_JPGErrorExit; cinfo.err->output_message = R_JPGOutputMessage; /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress(&cinfo); /* deep error handling */ if (setjmp(jerr.jmpbuf)) { // There was an error in jpeg decompression. Abort. return; } /* Step 2: specify data source (eg, a file) */ jpeg_mem_src(&cinfo, fbuffer.b, len); /* Step 3: read file parameters with jpeg_read_header() */ ( void ) jpeg_read_header(&cinfo, TRUE); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* * Make sure it always converts images to RGB color space. This will * automatically convert 8-bit greyscale images to RGB as well. */ cinfo.out_color_space = JCS_RGB; /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ /* Step 5: Start decompressor */ ( void ) jpeg_start_decompress(&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ /* JSAMPLEs per row in output buffer */ pixelcount = cinfo.output_width * cinfo.output_height; if (!cinfo.output_width || !cinfo.output_height || ((pixelcount * 4) / cinfo.output_width) / 4 != cinfo.output_height || pixelcount > 0x1FFFFFFF || cinfo.output_components != 3 ) { // Free the memory to make sure we don't leak memory ri.FS_FreeFile(fbuffer.v); jpeg_destroy_decompress(&cinfo); Ren_Drop("LoadJPG: %s has an invalid image format: %dx%d*4=%d, components: %d", filename, cinfo.output_width, cinfo.output_height, pixelcount * 4, cinfo.output_components); } memcount = pixelcount * 4; row_stride = cinfo.output_width * cinfo.output_components; out = R_GetImageBuffer(memcount, BUFFER_IMAGE, filename); *width = cinfo.output_width; *height = cinfo.output_height; /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ while (cinfo.output_scanline < cinfo.output_height) { /* jpeg_read_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could ask for * more than one scanline at a time if that's more convenient. */ buf = ((out + (row_stride * cinfo.output_scanline))); buffer = &buf; ( void ) jpeg_read_scanlines(&cinfo, buffer, 1); } buf = out; // Expand from RGB to RGBA sindex = pixelcount * cinfo.output_components; dindex = memcount; do { buf[--dindex] = 255; buf[--dindex] = buf[--sindex]; buf[--dindex] = buf[--sindex]; buf[--dindex] = buf[--sindex]; } while (sindex); *pic = out; /* Step 7: Finish decompression */ ( void ) jpeg_finish_decompress(&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress(&cinfo); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ ri.FS_FreeFile(fbuffer.v); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.pub.num_warnings is nonzero). */ /* And we're done! */ }
/** * @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 This routine is responsible for setting the most commonly changed state in Q3. */ void GL_State(uint32_t stateBits) { uint32_t diff = stateBits ^ glState.glStateBits; if (!diff) { return; } // check depthFunc bits if (diff & GLS_DEPTHFUNC_BITS) { switch (stateBits & GLS_DEPTHFUNC_BITS) { default: GL_DepthFunc(GL_LEQUAL); break; case GLS_DEPTHFUNC_LESS: GL_DepthFunc(GL_LESS); break; case GLS_DEPTHFUNC_EQUAL: GL_DepthFunc(GL_EQUAL); break; } } // check blend bits if (diff & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS)) { GLenum srcFactor, dstFactor; if (stateBits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS)) { switch (stateBits & GLS_SRCBLEND_BITS) { case GLS_SRCBLEND_ZERO: srcFactor = GL_ZERO; break; case GLS_SRCBLEND_ONE: srcFactor = GL_ONE; break; case GLS_SRCBLEND_DST_COLOR: srcFactor = GL_DST_COLOR; break; case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: srcFactor = GL_ONE_MINUS_DST_COLOR; break; case GLS_SRCBLEND_SRC_ALPHA: srcFactor = GL_SRC_ALPHA; break; case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: srcFactor = GL_ONE_MINUS_SRC_ALPHA; break; case GLS_SRCBLEND_DST_ALPHA: srcFactor = GL_DST_ALPHA; break; case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA: srcFactor = GL_ONE_MINUS_DST_ALPHA; break; case GLS_SRCBLEND_ALPHA_SATURATE: srcFactor = GL_SRC_ALPHA_SATURATE; break; default: srcFactor = GL_ONE; // to get warning to shut up Ren_Drop("GL_State: invalid src blend state bits\n"); break; } switch (stateBits & GLS_DSTBLEND_BITS) { case GLS_DSTBLEND_ZERO: dstFactor = GL_ZERO; break; case GLS_DSTBLEND_ONE: dstFactor = GL_ONE; break; case GLS_DSTBLEND_SRC_COLOR: dstFactor = GL_SRC_COLOR; break; case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: dstFactor = GL_ONE_MINUS_SRC_COLOR; break; case GLS_DSTBLEND_SRC_ALPHA: dstFactor = GL_SRC_ALPHA; break; case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: dstFactor = GL_ONE_MINUS_SRC_ALPHA; break; case GLS_DSTBLEND_DST_ALPHA: dstFactor = GL_DST_ALPHA; break; case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA: dstFactor = GL_ONE_MINUS_DST_ALPHA; break; default: dstFactor = GL_ONE; // to get warning to shut up Ren_Drop("GL_State: invalid dst blend state bits\n"); break; } glEnable(GL_BLEND); GL_BlendFunc(srcFactor, dstFactor); } else { glDisable(GL_BLEND); } } // check colormask if (diff & GLS_COLORMASK_BITS) { if (stateBits & GLS_COLORMASK_BITS) { GL_ColorMask((stateBits & GLS_REDMASK_FALSE) ? GL_FALSE : GL_TRUE, (stateBits & GLS_GREENMASK_FALSE) ? GL_FALSE : GL_TRUE, (stateBits & GLS_BLUEMASK_FALSE) ? GL_FALSE : GL_TRUE, (stateBits & GLS_ALPHAMASK_FALSE) ? GL_FALSE : GL_TRUE); } else { GL_ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } } // check depthmask if (diff & GLS_DEPTHMASK_TRUE) { if (stateBits & GLS_DEPTHMASK_TRUE) { GL_DepthMask(GL_TRUE); } else { GL_DepthMask(GL_FALSE); } } // fill/line mode if (diff & GLS_POLYMODE_LINE) { if (stateBits & GLS_POLYMODE_LINE) { GL_PolygonMode(GL_FRONT_AND_BACK, GL_LINE); } else { GL_PolygonMode(GL_FRONT_AND_BACK, GL_FILL); } } // depthtest if (diff & GLS_DEPTHTEST_DISABLE) { if (stateBits & GLS_DEPTHTEST_DISABLE) { glDisable(GL_DEPTH_TEST); } else { glEnable(GL_DEPTH_TEST); } } // alpha test - deprecated in OpenGL 3.0 #if 0 if (diff & GLS_ATEST_BITS) { switch (stateBits & GLS_ATEST_BITS) { case GLS_ATEST_GT_0: case GLS_ATEST_LT_128: case GLS_ATEST_GE_128: //case GLS_ATEST_GT_CUSTOM: glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); break; default: case 0: glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); break; } } #endif /* if(diff & GLS_ATEST_BITS) { switch (stateBits & GLS_ATEST_BITS) { case 0: glDisable(GL_ALPHA_TEST); break; case GLS_ATEST_GT_0: glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.0f); break; case GLS_ATEST_LT_80: glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_LESS, 0.5f); break; case GLS_ATEST_GE_80: glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GEQUAL, 0.5f); break; case GLS_ATEST_GT_CUSTOM: // FIXME glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.5f); break; default: assert(0); break; } } */ // stenciltest if (diff & GLS_STENCILTEST_ENABLE) { if (stateBits & GLS_STENCILTEST_ENABLE) { glEnable(GL_STENCIL_TEST); } else { glDisable(GL_STENCIL_TEST); } } glState.glStateBits = stateBits; }
/* ============ R_CreateIBO2 ============ */ IBO_t *R_CreateIBO2(const char *name, int numTriangles, srfTriangle_t *triangles, vboUsage_t usage) { IBO_t *ibo; int i, j; byte *indexes; int indexesSize; int indexesOfs; srfTriangle_t *tri; glIndex_t index; int glUsage; switch (usage) { case VBO_USAGE_STATIC: glUsage = GL_STATIC_DRAW; break; case VBO_USAGE_DYNAMIC: glUsage = GL_DYNAMIC_DRAW; break; default: glUsage = 0; Ren_Fatal("bad vboUsage_t given: %i", usage); break; } if (!numTriangles) { return NULL; } if (strlen(name) >= MAX_QPATH) { Ren_Drop("R_CreateIBO2: \"%s\" is too long\n", name); } // make sure the render thread is stopped R_IssuePendingRenderCommands(); ibo = (IBO_t *)ri.Hunk_Alloc(sizeof(*ibo), h_low); Com_AddToGrowList(&tr.ibos, ibo); Q_strncpyz(ibo->name, name, sizeof(ibo->name)); indexesSize = numTriangles * 3 * sizeof(glIndex_t); indexes = (byte *)ri.Hunk_AllocateTempMemory(indexesSize); indexesOfs = 0; //Ren_Print("sizeof(glIndex_t) = %i\n", sizeof(glIndex_t)); for (i = 0, tri = triangles; i < numTriangles; i++, tri++) { for (j = 0; j < 3; j++) { index = tri->indexes[j]; memcpy(indexes + indexesOfs, &index, sizeof(glIndex_t)); indexesOfs += sizeof(glIndex_t); } } ibo->indexesSize = indexesSize; ibo->indexesNum = numTriangles * 3; glGenBuffers(1, &ibo->indexesVBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->indexesVBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexesSize, indexes, glUsage); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GL_CheckErrors(); ri.Hunk_FreeTempMemory(indexes); return ibo; }
/* @@@@@@@@@@@@@@@@@@@@@ RE_RenderScene Draw a 3D view into a part of the window, then return to 2D drawing. Rendering a scene may require multiple views to be rendered to handle mirrors, @@@@@@@@@@@@@@@@@@@@@ */ void RE_RenderScene(const refdef_t *fd) { viewParms_t parms; int startTime; if (!tr.registered) { return; } Ren_LogComment("====== RE_RenderScene =====\n"); if (r_norefresh->integer) { return; } startTime = ri.Milliseconds(); if (!tr.world && !(fd->rdflags & RDF_NOWORLDMODEL)) { Ren_Drop("R_RenderScene: NULL worldmodel"); } memcpy(tr.refdef.text, fd->text, sizeof(tr.refdef.text)); tr.refdef.x = fd->x; tr.refdef.y = fd->y; tr.refdef.width = fd->width; tr.refdef.height = fd->height; tr.refdef.fov_x = fd->fov_x; tr.refdef.fov_y = fd->fov_y; VectorCopy(fd->vieworg, tr.refdef.vieworg); VectorCopy(fd->viewaxis[0], tr.refdef.viewaxis[0]); VectorCopy(fd->viewaxis[1], tr.refdef.viewaxis[1]); VectorCopy(fd->viewaxis[2], tr.refdef.viewaxis[2]); tr.refdef.time = fd->time; tr.refdef.rdflags = fd->rdflags; if (fd->rdflags & RDF_SKYBOXPORTAL) { skyboxportal = 1; } // copy the areamask data over and note if it has changed, which // will force a reset of the visible leafs even if the view hasn't moved tr.refdef.areamaskModified = qfalse; if (!(tr.refdef.rdflags & RDF_NOWORLDMODEL)) { int areaDiff; int i; // compare the area bits areaDiff = 0; for (i = 0 ; i < MAX_MAP_AREA_BYTES / 4 ; i++) { areaDiff |= ((int *)tr.refdef.areamask)[i] ^ ((int *)fd->areamask)[i]; ((int *)tr.refdef.areamask)[i] = ((int *)fd->areamask)[i]; } if (areaDiff) { // a door just opened or something tr.refdef.areamaskModified = qtrue; } } // derived info tr.refdef.floatTime = (double)tr.refdef.time * 0.001; tr.refdef.numDrawSurfs = r_firstSceneDrawSurf; tr.refdef.drawSurfs = backEndData->drawSurfs; tr.refdef.num_entities = r_numentities - r_firstSceneEntity; tr.refdef.entities = &backEndData->entities[r_firstSceneEntity]; tr.refdef.num_dlights = r_numdlights - r_firstSceneDlight; tr.refdef.dlights = &backEndData->dlights[r_firstSceneDlight]; tr.refdef.dlightBits = 0; tr.refdef.num_coronas = r_numcoronas - r_firstSceneCorona; tr.refdef.coronas = &backEndData->coronas[r_firstSceneCorona]; tr.refdef.numPolys = r_numpolys - r_firstScenePoly; tr.refdef.polys = &backEndData->polys[r_firstScenePoly]; tr.refdef.numPolyBuffers = r_numpolybuffers - r_firstScenePolybuffer; tr.refdef.polybuffers = &backEndData->polybuffers[r_firstScenePolybuffer]; tr.refdef.numDecalProjectors = r_numDecalProjectors - r_firstSceneDecalProjector; tr.refdef.decalProjectors = &backEndData->decalProjectors[r_firstSceneDecalProjector]; tr.refdef.numDecals = 0; tr.refdef.decals = &backEndData->decals[r_firstSceneDecal]; // a single frame may have multiple scenes draw inside it -- // a 3D game view, 3D status bar renderings, 3D menus, etc. // They need to be distinguished by the light flare code, because // the visibility state for a given surface may be different in // each scene / view. tr.frameSceneNum++; tr.sceneCount++; // setup view parms for the initial view // set up viewport // The refdef takes 0-at-the-top y coordinates, so // convert to GL's 0-at-the-bottom space memset(&parms, 0, sizeof(parms)); parms.viewportX = tr.refdef.x; parms.viewportY = glConfig.vidHeight - (tr.refdef.y + tr.refdef.height); parms.viewportWidth = tr.refdef.width; parms.viewportHeight = tr.refdef.height; parms.isPortal = qfalse; parms.fovX = tr.refdef.fov_x; parms.fovY = tr.refdef.fov_y; VectorCopy(fd->vieworg, parms.orientation.origin); VectorCopy(fd->viewaxis[0], parms.orientation.axis[0]); VectorCopy(fd->viewaxis[1], parms.orientation.axis[1]); VectorCopy(fd->viewaxis[2], parms.orientation.axis[2]); VectorCopy(fd->vieworg, parms.pvsOrigin); R_RenderView(&parms); // the next scene rendered in this frame will tack on after this one r_firstSceneDrawSurf = tr.refdef.numDrawSurfs; r_firstSceneDecal += tr.refdef.numDecals; r_firstSceneEntity = r_numentities; r_firstSceneDlight = r_numdlights; r_firstScenePoly = r_numpolys; r_firstScenePolybuffer = r_numpolybuffers; tr.frontEndMsec += ri.Milliseconds() - startTime; }
/* @@@@@@@@@@@@@@@@@@@@@ RE_RenderScene Draw a 3D view into a part of the window, then return to 2D drawing. Rendering a scene may require multiple views to be rendered to handle mirrors, @@@@@@@@@@@@@@@@@@@@@ */ void RE_RenderScene(const refdef_t *fd) { viewParms_t parms; int startTime; if (!tr.registered) { return; } Ren_LogComment("====== RE_RenderScene =====\n"); if (r_norefresh->integer) { return; } startTime = ri.Milliseconds(); if (!tr.world && !(fd->rdflags & RDF_NOWORLDMODEL)) { Ren_Drop("R_RenderScene: NULL worldmodel"); } Com_Memcpy(tr.refdef.text, fd->text, sizeof(tr.refdef.text)); tr.refdef.x = fd->x; tr.refdef.y = fd->y; tr.refdef.width = fd->width; tr.refdef.height = fd->height; tr.refdef.fov_x = fd->fov_x; tr.refdef.fov_y = fd->fov_y; VectorCopy(fd->vieworg, tr.refdef.vieworg); VectorCopy(fd->viewaxis[0], tr.refdef.viewaxis[0]); VectorCopy(fd->viewaxis[1], tr.refdef.viewaxis[1]); VectorCopy(fd->viewaxis[2], tr.refdef.viewaxis[2]); tr.refdef.time = fd->time; tr.refdef.rdflags = fd->rdflags; /* if(fd->rdflags & RDF_SKYBOXPORTAL) { Ren_Print("skyboxportal = 1\n"); } */ // copy the areamask data over and note if it has changed, which // will force a reset of the visible leafs even if the view hasn't moved tr.refdef.areamaskModified = qfalse; if (!(tr.refdef.rdflags & RDF_NOWORLDMODEL) && !((tr.refdef.rdflags & RDF_SKYBOXPORTAL) && tr.world->numSkyNodes > 0)) { int areaDiff; int i; // compare the area bits areaDiff = 0; for (i = 0; i < MAX_MAP_AREA_BYTES / 4; i++) { areaDiff |= ((int *)tr.refdef.areamask)[i] ^ ((int *)fd->areamask)[i]; ((int *)tr.refdef.areamask)[i] = ((int *)fd->areamask)[i]; } if (areaDiff) { // a door just opened or something tr.refdef.areamaskModified = qtrue; } } R_AddWorldLightsToScene(); // derived info tr.refdef.floatTime = tr.refdef.time * 0.001f; tr.refdef.numDrawSurfs = r_firstSceneDrawSurf; tr.refdef.drawSurfs = backEndData->drawSurfs; tr.refdef.numInteractions = r_firstSceneInteraction; tr.refdef.interactions = backEndData->interactions; tr.refdef.numEntities = r_numEntities - r_firstSceneEntity; tr.refdef.entities = &backEndData->entities[r_firstSceneEntity]; tr.refdef.numLights = r_numLights - r_firstSceneLight; tr.refdef.lights = &backEndData->lights[r_firstSceneLight]; tr.refdef.num_coronas = r_numcoronas - r_firstSceneCorona; tr.refdef.coronas = &backEndData->coronas[r_firstSceneCorona]; tr.refdef.numPolys = r_numPolys - r_firstScenePoly; tr.refdef.polys = &backEndData->polys[r_firstScenePoly]; tr.refdef.numPolybuffers = r_numPolybuffers - r_firstScenePolybuffer; tr.refdef.polybuffers = &backEndData->polybuffers[r_firstScenePolybuffer]; tr.refdef.numDecalProjectors = r_numDecalProjectors - r_firstSceneDecalProjector; tr.refdef.decalProjectors = &backEndData->decalProjectors[r_firstSceneDecalProjector]; tr.refdef.numDecals = 0; tr.refdef.decals = &backEndData->decals[r_firstSceneDecal]; // a single frame may have multiple scenes draw inside it -- // a 3D game view, 3D status bar renderings, 3D menus, etc. // They need to be distinguished by the light flare code, because // the visibility state for a given surface may be different in // each scene / view. tr.frameSceneNum++; tr.sceneCount++; // a scene can have multiple views caused by mirrors or portals // the number of views is restricted so we can use hardware occlusion queries // and put them into the BSP nodes for each view tr.viewCount = -1; // setup view parms for the initial view // // set up viewport // The refdef takes 0-at-the-top y coordinates, so // convert to GL's 0-at-the-bottom space // Com_Memset(&parms, 0, sizeof(parms)); #if 1 if (tr.refdef.pixelTarget == NULL) { parms.viewportX = tr.refdef.x; parms.viewportY = glConfig.vidHeight - (tr.refdef.y + tr.refdef.height); } else { // Driver bug, if we try and do pixel target work along the top edge of a window // we can end up capturing part of the status bar. (see screenshot corruption..) // Soooo.. use the middle. parms.viewportX = glConfig.vidWidth / 2; parms.viewportY = glConfig.vidHeight / 2; } #else parms.viewportX = tr.refdef.x; parms.viewportY = glConfig.vidHeight - (tr.refdef.y + tr.refdef.height); #endif parms.viewportWidth = tr.refdef.width; parms.viewportHeight = tr.refdef.height; Vector4Set(parms.viewportVerts[0], parms.viewportX, parms.viewportY, 0, 1); Vector4Set(parms.viewportVerts[1], parms.viewportX + parms.viewportWidth, parms.viewportY, 0, 1); Vector4Set(parms.viewportVerts[2], parms.viewportX + parms.viewportWidth, parms.viewportY + parms.viewportHeight, 0, 1); Vector4Set(parms.viewportVerts[3], parms.viewportX, parms.viewportY + parms.viewportHeight, 0, 1); parms.isPortal = qfalse; parms.fovX = tr.refdef.fov_x; parms.fovY = tr.refdef.fov_y; parms.stereoFrame = tr.refdef.stereoFrame; VectorCopy(fd->vieworg, parms.orientation.origin); VectorCopy(fd->viewaxis[0], parms.orientation.axis[0]); VectorCopy(fd->viewaxis[1], parms.orientation.axis[1]); VectorCopy(fd->viewaxis[2], parms.orientation.axis[2]); VectorCopy(fd->vieworg, parms.pvsOrigin); R_RenderView(&parms); // the next scene rendered in this frame will tack on after this one r_firstSceneDrawSurf = tr.refdef.numDrawSurfs; r_firstSceneInteraction = tr.refdef.numInteractions; r_firstSceneDecal += tr.refdef.numDecals; r_firstSceneEntity = r_numEntities; r_firstSceneLight = r_numLights; r_firstScenePoly = r_numPolys; r_firstScenePolybuffer = r_numPolybuffers; tr.frontEndMsec += ri.Milliseconds() - startTime; }
/* ================= 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; }
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; }