/* From the 'Wizard2' engine by Spaddlewit Inc. ( http://www.spaddlewit.com ) An experimental work-in-progress. Donated to Sonic Team Junior and adapted to work with Sonic Robo Blast 2. The license of this code matches whatever the licensing is for Sonic Robo Blast 2. */ #include "../doomdef.h" #include "../doomtype.h" #include "../info.h" #include "../z_zone.h" #include "hw_model.h" #include "hw_md2load.h" #include "hw_md3load.h" #include "hw_md2.h" #include "u_list.h" #include static float PI = (3.1415926535897932384626433832795f); static float U_Deg2Rad(float deg) { return deg * ((float)PI / 180.0f); } vector_t vectorXaxis = { 1.0f, 0.0f, 0.0f }; vector_t vectorYaxis = { 0.0f, 1.0f, 0.0f }; vector_t vectorZaxis = { 0.0f, 0.0f, 1.0f }; void VectorRotate(vector_t *rotVec, const vector_t *axisVec, float angle) { float ux, uy, uz, vx, vy, vz, wx, wy, wz, sa, ca; angle = U_Deg2Rad(angle); // Rotate the point (x,y,z) around the vector (u,v,w) ux = axisVec->x * rotVec->x; uy = axisVec->x * rotVec->y; uz = axisVec->x * rotVec->z; vx = axisVec->y * rotVec->x; vy = axisVec->y * rotVec->y; vz = axisVec->y * rotVec->z; wx = axisVec->z * rotVec->x; wy = axisVec->z * rotVec->y; wz = axisVec->z * rotVec->z; sa = sinf(angle); ca = cosf(angle); rotVec->x = axisVec->x*(ux + vy + wz) + (rotVec->x*(axisVec->y*axisVec->y + axisVec->z*axisVec->z) - axisVec->x*(vy + wz))*ca + (-wy + vz)*sa; rotVec->y = axisVec->y*(ux + vy + wz) + (rotVec->y*(axisVec->x*axisVec->x + axisVec->z*axisVec->z) - axisVec->y*(ux + wz))*ca + (wx - uz)*sa; rotVec->z = axisVec->z*(ux + vy + wz) + (rotVec->z*(axisVec->x*axisVec->x + axisVec->y*axisVec->y) - axisVec->z*(ux + vy))*ca + (-vx + uy)*sa; } void UnloadModel(model_t *model) { // Wouldn't it be great if C just had destructors? int i; for (i = 0; i < model->numMeshes; i++) { mesh_t *mesh = &model->meshes[i]; if (mesh->frames) { int j; for (j = 0; j < mesh->numFrames; j++) { if (mesh->frames[j].normals) Z_Free(mesh->frames[j].normals); if (mesh->frames[j].tangents) Z_Free(mesh->frames[j].tangents); if (mesh->frames[j].vertices) Z_Free(mesh->frames[j].vertices); if (mesh->frames[j].colors) Z_Free(mesh->frames[j].colors); } Z_Free(mesh->frames); } else if (mesh->tinyframes) { int j; for (j = 0; j < mesh->numFrames; j++) { if (mesh->tinyframes[j].normals) Z_Free(mesh->tinyframes[j].normals); if (mesh->tinyframes[j].tangents) Z_Free(mesh->tinyframes[j].tangents); if (mesh->tinyframes[j].vertices) Z_Free(mesh->tinyframes[j].vertices); } if (mesh->indices) Z_Free(mesh->indices); Z_Free(mesh->tinyframes); } if (mesh->uvs) Z_Free(mesh->uvs); if (mesh->lightuvs) Z_Free(mesh->lightuvs); } if (model->meshes) Z_Free(model->meshes); if (model->tags) Z_Free(model->tags); if (model->materials) Z_Free(model->materials); DeleteVBOs(model); Z_Free(model); } tag_t *GetTagByName(model_t *model, char *name, int frame) { if (frame < model->maxNumFrames) { tag_t *iterator = &model->tags[frame * model->numTags]; int i; for (i = 0; i < model->numTags; i++) { if (!stricmp(iterator[i].name, name)) return &iterator[i]; } } return NULL; } // // LoadModel // // Load a model and // convert it to the // internal format. // model_t *LoadModel(const char *filename, int ztag) { model_t *model; // What type of file? const char *extension = NULL; int i; for (i = (int)strlen(filename)-1; i >= 0; i--) { if (filename[i] != '.') continue; extension = &filename[i]; break; } if (!extension) { CONS_Printf("Model %s is lacking a file extension, unable to determine type!\n", filename); return NULL; } if (!strcmp(extension, ".md3")) { if (!(model = MD3_LoadModel(filename, ztag, false))) return NULL; } else if (!strcmp(extension, ".md3s")) // MD3 that will be converted in memory to use full floats { if (!(model = MD3_LoadModel(filename, ztag, true))) return NULL; } else if (!strcmp(extension, ".md2")) { if (!(model = MD2_LoadModel(filename, ztag, false))) return NULL; } else if (!strcmp(extension, ".md2s")) { if (!(model = MD2_LoadModel(filename, ztag, true))) return NULL; } else { CONS_Printf("Unknown model format: %s\n", extension); return NULL; } model->mdlFilename = (char*)Z_Malloc(strlen(filename)+1, ztag, 0); strcpy(model->mdlFilename, filename); Optimize(model); GeneratePolygonNormals(model, ztag); LoadModelSprite2(model); // Default material properties for (i = 0 ; i < model->numMaterials; i++) { material_t *material = &model->materials[i]; material->ambient[0] = 0.7686f; material->ambient[1] = 0.7686f; material->ambient[2] = 0.7686f; material->ambient[3] = 1.0f; material->diffuse[0] = 0.5863f; material->diffuse[1] = 0.5863f; material->diffuse[2] = 0.5863f; material->diffuse[3] = 1.0f; material->specular[0] = 0.4902f; material->specular[1] = 0.4902f; material->specular[2] = 0.4902f; material->specular[3] = 1.0f; material->shininess = 25.0f; } return model; } void HWR_ReloadModels(void) { INT32 s; for (s = 0; s < MAXSKINS; s++) { if (md2_playermodels[s].model) LoadModelSprite2(md2_playermodels[s].model); } } void LoadModelSprite2(model_t *model) { INT32 i; modelspr2frames_t *spr2frames = NULL; INT32 numframes = model->meshes[0].numFrames; char *framename = model->framenames; if (!framename) return; for (i = 0; i < numframes; i++) { char prefix[6]; char name[5]; char framechar[4]; UINT8 frame = 0; UINT8 spr2idx; memset(&prefix, 0x00, 6); memset(&name, 0x00, 5); memset(&framechar, 0x00, 4); if (strlen(framename) >= 9) { char *modelframename = framename; memcpy(&prefix, modelframename, 5); modelframename += 5; memcpy(&name, modelframename, 4); modelframename += 4; memcpy(&framechar, modelframename, 3); frame = atoi(framechar); if ((!memcmp(prefix, "SPR2_", 5)) || (!memcmp(prefix, "SUPER", 5))) { spr2idx = 0; while (spr2idx < NUMPLAYERSPRITES) { if (!memcmp(spr2names[spr2idx], name, 4)) { if (!spr2frames) spr2frames = (modelspr2frames_t*)Z_Calloc(sizeof(modelspr2frames_t)*NUMPLAYERSPRITES, PU_STATIC, NULL); if (!memcmp(prefix, "SUPER", 5)) spr2frames[spr2idx].superframes[frame] = i; else spr2frames[spr2idx].frames[frame] = i; break; } spr2idx++; } } } framename += 16; } if (model->spr2frames) Z_Free(model->spr2frames); model->spr2frames = spr2frames; } // // GenerateVertexNormals // // Creates a new normal for a vertex using the average of all of the polygons it belongs to. // void GenerateVertexNormals(model_t *model) { int i; for (i = 0; i < model->numMeshes; i++) { int j; mesh_t *mesh = &model->meshes[i]; if (!mesh->frames) continue; for (j = 0; j < mesh->numFrames; j++) { mdlframe_t *frame = &mesh->frames[j]; int memTag = PU_STATIC; float *newNormals = (float*)Z_Malloc(sizeof(float)*3*mesh->numTriangles*3, memTag, 0); int k; float *vertPtr = frame->vertices; float *oldNormals; M_Memcpy(newNormals, frame->normals, sizeof(float)*3*mesh->numTriangles*3); /* if (!systemSucks) { memTag = Z_GetTag(frame->tangents); float *newTangents = (float*)Z_Malloc(sizeof(float)*3*mesh->numTriangles*3, memTag); M_Memcpy(newTangents, frame->tangents, sizeof(float)*3*mesh->numTriangles*3); }*/ for (k = 0; k < mesh->numVertices; k++) { float x, y, z; int vCount = 0; vector_t normal; int l; float *testPtr = frame->vertices; x = *vertPtr++; y = *vertPtr++; z = *vertPtr++; normal.x = normal.y = normal.z = 0; for (l = 0; l < mesh->numVertices; l++) { float testX, testY, testZ; testX = *testPtr++; testY = *testPtr++; testZ = *testPtr++; if (fabsf(x - testX) > FLT_EPSILON || fabsf(y - testY) > FLT_EPSILON || fabsf(z - testZ) > FLT_EPSILON) continue; // Found a vertex match! Add it... normal.x += frame->normals[3 * l + 0]; normal.y += frame->normals[3 * l + 1]; normal.z += frame->normals[3 * l + 2]; vCount++; } if (vCount > 1) { // Vector::Normalize(&normal); newNormals[3 * k + 0] = (float)normal.x; newNormals[3 * k + 1] = (float)normal.y; newNormals[3 * k + 2] = (float)normal.z; /* if (!systemSucks) { Vector::vector_t tangent; Vector::Tangent(&normal, &tangent); newTangents[3 * k + 0] = tangent.x; newTangents[3 * k + 1] = tangent.y; newTangents[3 * k + 2] = tangent.z; }*/ } } oldNormals = frame->normals; frame->normals = newNormals; Z_Free(oldNormals); /* if (!systemSucks) { float *oldTangents = frame->tangents; frame->tangents = newTangents; Z_Free(oldTangents); }*/ } } } typedef struct materiallist_s { struct materiallist_s *next; struct materiallist_s *prev; material_t *material; } materiallist_t; static boolean AddMaterialToList(materiallist_t **head, material_t *material) { materiallist_t *node, *newMatNode; for (node = *head; node; node = node->next) { if (node->material == material) return false; } // Didn't find it, so add to the list newMatNode = (materiallist_t*)Z_Malloc(sizeof(materiallist_t), PU_CACHE, 0); newMatNode->material = material; ListAdd(newMatNode, (listitem_t**)head); return true; } // // Optimize // // Groups triangles from meshes in the model // Only works for models with 1 frame // void Optimize(model_t *model) { int numMeshes = 0; int i; materiallist_t *matListHead = NULL; int memTag; mesh_t *newMeshes; materiallist_t *node; if (model->numMeshes <= 1) return; // No need for (i = 0; i < model->numMeshes; i++) { mesh_t *curMesh = &model->meshes[i]; if (curMesh->numFrames > 1) return; // Can't optimize models with > 1 frame if (!curMesh->frames) return; // Don't optimize tinyframe models (no need) // We are condensing to 1 mesh per material, so // the # of materials we use will be the new // # of meshes if (AddMaterialToList(&matListHead, curMesh->frames[0].material)) numMeshes++; } memTag = PU_STATIC; newMeshes = (mesh_t*)Z_Calloc(sizeof(mesh_t) * numMeshes, memTag, 0); i = 0; for (node = matListHead; node; node = node->next) { material_t *curMat = node->material; mesh_t *newMesh = &newMeshes[i]; mdlframe_t *curFrame; int uvCount; int vertCount; int colorCount; // Find all triangles with this material and count them int numTriangles = 0; int j; for (j = 0; j < model->numMeshes; j++) { mesh_t *curMesh = &model->meshes[j]; if (curMesh->frames[0].material == curMat) numTriangles += curMesh->numTriangles; } newMesh->numFrames = 1; newMesh->numTriangles = numTriangles; newMesh->numVertices = numTriangles * 3; newMesh->uvs = (float*)Z_Malloc(sizeof(float)*2*numTriangles*3, memTag, 0); // if (node->material->lightmap) // newMesh->lightuvs = (float*)Z_Malloc(sizeof(float)*2*numTriangles*3, memTag, 0); newMesh->frames = (mdlframe_t*)Z_Calloc(sizeof(mdlframe_t), memTag, 0); curFrame = &newMesh->frames[0]; curFrame->material = curMat; curFrame->normals = (float*)Z_Malloc(sizeof(float)*3*numTriangles*3, memTag, 0); // if (!systemSucks) // curFrame->tangents = (float*)Z_Malloc(sizeof(float)*3*numTriangles*3, memTag, 0); curFrame->vertices = (float*)Z_Malloc(sizeof(float)*3*numTriangles*3, memTag, 0); curFrame->colors = (char*)Z_Malloc(sizeof(char)*4*numTriangles*3, memTag, 0); // Now traverse the meshes of the model, adding in // vertices/normals/uvs that match the current material uvCount = 0; vertCount = 0; colorCount = 0; for (j = 0; j < model->numMeshes; j++) { mesh_t *curMesh = &model->meshes[j]; if (curMesh->frames[0].material == curMat) { float *dest; float *src; char *destByte; char *srcByte; M_Memcpy(&newMesh->uvs[uvCount], curMesh->uvs, sizeof(float)*2*curMesh->numTriangles*3); /* if (node->material->lightmap) { M_Memcpy(&newMesh->lightuvs[uvCount], curMesh->lightuvs, sizeof(float)*2*curMesh->numTriangles*3); }*/ uvCount += 2*curMesh->numTriangles*3; dest = (float*)newMesh->frames[0].vertices; src = (float*)curMesh->frames[0].vertices; M_Memcpy(&dest[vertCount], src, sizeof(float)*3*curMesh->numTriangles*3); dest = (float*)newMesh->frames[0].normals; src = (float*)curMesh->frames[0].normals; M_Memcpy(&dest[vertCount], src, sizeof(float)*3*curMesh->numTriangles*3); /* if (!systemSucks) { dest = (float*)newMesh->frames[0].tangents; src = (float*)curMesh->frames[0].tangents; M_Memcpy(&dest[vertCount], src, sizeof(float)*3*curMesh->numTriangles*3); }*/ vertCount += 3 * curMesh->numTriangles * 3; destByte = (char*)newMesh->frames[0].colors; srcByte = (char*)curMesh->frames[0].colors; if (srcByte) { M_Memcpy(&destByte[colorCount], srcByte, sizeof(char)*4*curMesh->numTriangles*3); } else { memset(&destByte[colorCount], 255, sizeof(char)*4*curMesh->numTriangles*3); } colorCount += 4 * curMesh->numTriangles * 3; } } i++; } CONS_Printf("Model::Optimize(): Model reduced from %d to %d meshes.\n", model->numMeshes, numMeshes); model->meshes = newMeshes; model->numMeshes = numMeshes; } void GeneratePolygonNormals(model_t *model, int ztag) { int i; for (i = 0; i < model->numMeshes; i++) { int j; mesh_t *mesh = &model->meshes[i]; if (!mesh->frames) continue; for (j = 0; j < mesh->numFrames; j++) { int k; mdlframe_t *frame = &mesh->frames[j]; const float *vertices = frame->vertices; vector_t *polyNormals; frame->polyNormals = (vector_t*)Z_Malloc(sizeof(vector_t) * mesh->numTriangles, ztag, 0); polyNormals = frame->polyNormals; for (k = 0; k < mesh->numTriangles; k++) { // Vector::Normal(vertices, polyNormals); vertices += 3 * 3; polyNormals++; } } } } // // Reload // // Reload VBOs // #if 0 static void Reload(void) { /* model_t *node; for (node = modelHead; node; node = node->next) { int i; for (i = 0; i < node->numMeshes; i++) { mesh_t *mesh = &node->meshes[i]; if (mesh->frames) { int j; for (j = 0; j < mesh->numFrames; j++) CreateVBO(mesh, &mesh->frames[j]); } else if (mesh->tinyframes) { int j; for (j = 0; j < mesh->numFrames; j++) CreateVBO(mesh, &mesh->tinyframes[j]); } } }*/ } #endif void DeleteVBOs(model_t *model) { (void)model; /* for (int i = 0; i < model->numMeshes; i++) { mesh_t *mesh = &model->meshes[i]; if (mesh->frames) { for (int j = 0; j < mesh->numFrames; j++) { mdlframe_t *frame = &mesh->frames[j]; if (!frame->vboID) continue; bglDeleteBuffers(1, &frame->vboID); frame->vboID = 0; } } else if (mesh->tinyframes) { for (int j = 0; j < mesh->numFrames; j++) { tinyframe_t *frame = &mesh->tinyframes[j]; if (!frame->vboID) continue; bglDeleteBuffers(1, &frame->vboID); frame->vboID = 0; } } }*/ }