#include "quakedef.h" #include "glquake.h" #include "shader.h" #include "hash.h" //FIXME typedef struct { float scale[3]; // multiply qbyte verts by this float translate[3]; // then add this char name[16]; // frame name from grabbing dtrivertx_t verts[1]; // variable sized } dmd2aliasframe_t; // entity_state_t->renderfx flags #define Q2RF_MINLIGHT 1 // allways have some light (viewmodel) #define Q2RF_VIEWERMODEL 2 // don't draw through eyes, only mirrors #define Q2RF_WEAPONMODEL 4 // only draw through eyes #define Q2RF_FULLBRIGHT 8 // allways draw full intensity #define Q2RF_DEPTHHACK 16 // for view weapon Z crunching #define Q2RF_TRANSLUCENT 32 #define Q2RF_FRAMELERP 64 #define Q2RF_BEAM 128 #define Q2RF_CUSTOMSKIN 256 // skin is an index in image_precache #define Q2RF_GLOW 512 // pulse lighting for bonus items #define Q2RF_SHELL_RED 1024 #define Q2RF_SHELL_GREEN 2048 #define Q2RF_SHELL_BLUE 4096 //ROGUE #define Q2RF_IR_VISIBLE 0x00008000 // 32768 #define Q2RF_SHELL_DOUBLE 0x00010000 // 65536 #define Q2RF_SHELL_HALF_DAM 0x00020000 #define Q2RF_USE_DISGUISE 0x00040000 //ROGUE extern cvar_t gl_part_flame, gl_part_torch, r_fullbrightSkins, r_fb_models; extern cvar_t r_noaliasshadows; void R_TorchEffect (vec3_t pos, int type); void GL_DrawMesh(mesh_t *mesh, shader_t *shader, int texturenum, int lmtexturenum); void GLMod_FloodFillSkin( qbyte *skin, int skinwidth, int skinheight ); extern char loadname[32]; // for hunk tags int numTempColours; byte_vec4_t *tempColours; int numTempVertexCoords; vec4_t *tempVertexCoords; int numTempNormals; vec3_t *tempNormals; extern cvar_t gl_ati_truform; typedef struct { int ofs_indexes; int numindexes; int ofs_trineighbours; int numskins; int ofsskins; int numverts; int ofs_st_array; int groups; int groupofs; int nextsurf; } galiasinfo_t; //frame is an index into this typedef struct { int numposes; float rate; int poseofs; } galiasgroup_t; typedef struct { int ofsverts; int ofsnormals; vec3_t scale; vec3_t scale_origin; } galiaspose_t; //we can't be bothered with animating skins. //We'll load up to four of them but after that you're on your own typedef struct { int skinwidth; int skinheight; int ofstexels; //this is 8bit for frame 0 only. only valid in q1 models without replacement textures, used for colourising player skins. float skinspeed; int texnums; int ofstexnums; } galiasskin_t; typedef struct { int base; int bump; int fullbright; } galiastexnum_t; typedef struct { char name[MAX_QPATH]; galiastexnum_t texnum; int colour; int skinnum; bucket_t bucket; } galiascolourmapped_t; static hashtable_t skincolourmapped; static vec3_t shadevector; static vec3_t shadelight, ambientlight; static void R_LerpFrames(mesh_t *mesh, galiaspose_t *p1, galiaspose_t *p2, float lerp, qbyte alpha, float expand) { extern cvar_t r_nolerp, r_nolightdir; float blerp = 1-lerp; int i; float l; int temp; vec3_t *p1v, *p2v; vec3_t *p1n, *p2n; (char *)p1v = (char *)p1 + p1->ofsverts; (char *)p2v = (char *)p2 + p2->ofsverts; (char *)p1n = (char *)p1 + p1->ofsnormals; (char *)p2n = (char *)p2 + p2->ofsnormals; if (p1v == p2v || r_nolerp.value) { (char *)mesh->normals_array = (char *)p1 + p1->ofsnormals; if (r_nolightdir.value) { for (i = 0; i < mesh->numvertexes; i++) { mesh->xyz_array[i][0] = p1v[i][0]; mesh->xyz_array[i][1] = p1v[i][1]; mesh->xyz_array[i][2] = p1v[i][2]; mesh->colors_array[i][0] = ambientlight[0]+shadelight[0]; mesh->colors_array[i][1] = ambientlight[1]+shadelight[1]; mesh->colors_array[i][2] = ambientlight[2]+shadelight[2]; mesh->colors_array[i][3] = alpha; } } else { for (i = 0; i < mesh->numvertexes; i++) { mesh->xyz_array[i][0] = p1v[i][0]; mesh->xyz_array[i][1] = p1v[i][1]; mesh->xyz_array[i][2] = p1v[i][2]; l = DotProduct(mesh->normals_array[i], shadevector); temp = l*ambientlight[0]+shadelight[0]; if (temp < 0) temp = 0; else if (temp > 255) temp = 255; mesh->colors_array[i][0] = temp; temp = l*ambientlight[1]+shadelight[1]; if (temp < 0) temp = 0; else if (temp > 255) temp = 255; mesh->colors_array[i][1] = temp; temp = l*ambientlight[2]+shadelight[2]; if (temp < 0) temp = 0; else if (temp > 255) temp = 255; mesh->colors_array[i][2] = temp; mesh->colors_array[i][3] = alpha; } } } else { if (r_nolightdir.value) { for (i = 0; i < mesh->numvertexes; i++) { mesh->normals_array[i][0] = p1n[i][0]*lerp + p2n[i][0]*blerp; mesh->normals_array[i][1] = p1n[i][1]*lerp + p2n[i][1]*blerp; mesh->normals_array[i][2] = p1n[i][2]*lerp + p2n[i][2]*blerp; mesh->xyz_array[i][0] = p1v[i][0]*lerp + p2v[i][0]*blerp; mesh->xyz_array[i][1] = p1v[i][1]*lerp + p2v[i][1]*blerp; mesh->xyz_array[i][2] = p1v[i][2]*lerp + p2v[i][2]*blerp; mesh->colors_array[i][0] = ambientlight[0]; mesh->colors_array[i][1] = ambientlight[1]; mesh->colors_array[i][2] = ambientlight[2]; mesh->colors_array[i][3] = alpha; } } else { for (i = 0; i < mesh->numvertexes; i++) { mesh->normals_array[i][0] = p1n[i][0]*lerp + p2n[i][0]*blerp; mesh->normals_array[i][1] = p1n[i][1]*lerp + p2n[i][1]*blerp; mesh->normals_array[i][2] = p1n[i][2]*lerp + p2n[i][2]*blerp; mesh->xyz_array[i][0] = p1v[i][0]*lerp + p2v[i][0]*blerp; mesh->xyz_array[i][1] = p1v[i][1]*lerp + p2v[i][1]*blerp; mesh->xyz_array[i][2] = p1v[i][2]*lerp + p2v[i][2]*blerp; l = DotProduct(mesh->normals_array[i], shadevector); temp = l*ambientlight[0]+shadelight[0]; if (temp < 0) temp = 0; else if (temp > 255) temp = 255; mesh->colors_array[i][0] = temp; temp = l*ambientlight[1]+shadelight[1]; if (temp < 0) temp = 0; else if (temp > 255) temp = 255; mesh->colors_array[i][1] = temp; temp = l*ambientlight[2]+shadelight[2]; if (temp < 0) temp = 0; else if (temp > 255) temp = 255; mesh->colors_array[i][2] = temp; mesh->colors_array[i][3] = alpha; } } } if (expand) { for (i = 0; i < mesh->numvertexes; i++) { mesh->xyz_array[i][0] += mesh->normals_array[i][0]*expand; mesh->xyz_array[i][1] += mesh->normals_array[i][1]*expand; mesh->xyz_array[i][2] += mesh->normals_array[i][2]*expand; } } } static void R_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf, int frame1, int frame2, float lerp, float alpha) { galiasgroup_t *g1, *g2; if (!inf->groups) { Con_DPrintf("Model with no frames\n"); return; } if (frame1 < 0) { Con_DPrintf("Negative frame\n"); frame1 = 0; } if (frame2 < 0) { Con_DPrintf("Negative frame\n"); frame2 = frame1; } if (frame1 >= inf->groups) { Con_DPrintf("Too high frame\n"); frame1 = 0; } if (frame2 >= inf->groups) { Con_DPrintf("Too high frame\n"); frame2 = frame1; } if (lerp <= 0) frame2 = frame1; if (lerp >= 1) frame1 = frame2; if (numTempColours < inf->numverts) { if (tempColours) BZ_Free(tempColours); tempColours = BZ_Malloc(sizeof(*tempColours)*inf->numverts); numTempColours = inf->numverts; } if (numTempNormals < inf->numverts) { if (tempNormals) BZ_Free(tempNormals); tempNormals = BZ_Malloc(sizeof(*tempNormals)*inf->numverts); numTempNormals = inf->numverts; } if (numTempVertexCoords < inf->numverts) { if (tempVertexCoords) BZ_Free(tempVertexCoords); tempVertexCoords = BZ_Malloc(sizeof(*tempVertexCoords)*inf->numverts); numTempVertexCoords = inf->numverts; } (char *)mesh->indexes = (char *)inf + inf->ofs_indexes; mesh->numindexes = inf->numindexes; (char *)mesh->st_array = (char *)inf + inf->ofs_st_array; mesh->lmst_array = NULL; mesh->colors_array = tempColours; mesh->normals_array = tempNormals; mesh->xyz_array = tempVertexCoords; mesh->numvertexes = inf->numverts; (char *)mesh->trneighbors = (char *)inf + inf->ofs_trineighbours; (char *)g1 = (char *)inf + inf->groupofs + sizeof(galiasgroup_t)*frame1; (char *)g2 = (char *)inf + inf->groupofs + sizeof(galiasgroup_t)*frame2; if (g1 == g2) //lerping within group is only done if not changing group { lerp = cl.time*g1->rate; frame1=lerp; frame2=frame1+1; lerp-=frame1; frame1=frame1%g1->numposes; frame2=frame2%g1->numposes; } else //don't bother with a four way lerp { frame1=0; frame2=0; } R_LerpFrames(mesh, (galiaspose_t *)((char *)g1 + g1->poseofs + sizeof(galiaspose_t)*frame1), (galiaspose_t *)((char *)g2 + g2->poseofs + sizeof(galiaspose_t)*frame2), 1-lerp, (qbyte)(alpha*255), currententity->fatness);//20*sin(cl.time)); } void GL_GAliasFlushSkinCache(void) { int i; bucket_t *b; for (i = 0; i < skincolourmapped.numbuckets; i++) { while((b = skincolourmapped.bucket[i])) { skincolourmapped.bucket[i] = b->next; BZ_Free(b->data); } } if (skincolourmapped.bucket) BZ_Free(skincolourmapped.bucket); skincolourmapped.bucket = NULL; skincolourmapped.numbuckets = 0; } static galiastexnum_t *GL_ChooseSkin(galiasinfo_t *inf, char *modelname, entity_t *e) { galiasskin_t *skins; galiastexnum_t *texnums; int frame; int tc, bc; if (e->scoreboard) { if (!e->scoreboard->skin && !gl_nocolors.value) Skin_Find(e->scoreboard); tc = e->scoreboard->topcolor; bc = e->scoreboard->bottomcolor; } else { tc = 1; bc = 1; } if (!gl_nocolors.value && (tc != 1 || bc != 1 || (e->scoreboard && e->scoreboard->skin))) { int inwidth, inheight; int tinwidth, tinheight; char *skinname; qbyte *original; int cc; galiascolourmapped_t *cm; cc = (tc<<4)|bc; if (!strstr(modelname, "player")) skinname = modelname; else { if (e->scoreboard && e->scoreboard->skin && !gl_nocolors.value) skinname = e->scoreboard->skin->name; else skinname = modelname; } if (!skincolourmapped.numbuckets) Hash_InitTable(&skincolourmapped, 256, BZ_Malloc(Hash_BytesForBuckets(256))); for (cm = Hash_Get(&skincolourmapped, skinname); cm; cm = Hash_GetNext(&skincolourmapped, skinname, cm)) { if (cm->colour == cc && cm->skinnum == e->skinnum) { return &cm->texnum; } } (char *)skins = (char *)inf + inf->ofsskins; if (!skins->texnums) return NULL; if (e->skinnum >= 0 && e->skinnum < inf->numskins) skins += e->skinnum; (char *)texnums = (char *)skins + skins->ofstexnums; //colourmap isn't present yet. cm = BZ_Malloc(sizeof(*cm)); Q_strncpyz(cm->name, skinname, sizeof(cm->name)); Hash_Add2(&skincolourmapped, cm->name, cm, &cm->bucket); cm->colour = cc; cm->skinnum = e->skinnum; cm->texnum.fullbright = 0; cm->texnum.base = 0; cm->texnum.bump = texnums[cm->skinnum].bump; //can't colour bumpmapping if (skins->ofstexels) { original = (qbyte *)skins + skins->ofstexels; inwidth = skins->skinwidth; inheight = skins->skinheight; } else if (skinname!=modelname && e->scoreboard && e->scoreboard->skin) { original = Skin_Cache8(e->scoreboard->skin); inwidth = e->scoreboard->skin->width; inheight = e->scoreboard->skin->height; } else { original = NULL; inwidth = 0; inheight = 0; } tinwidth = skins->skinwidth; tinheight = skins->skinheight; if (original) { int i, j; qbyte translate[256]; unsigned translate32[256]; static unsigned pixels[512*512]; unsigned *out; unsigned frac, fracstep; unsigned scaled_width, scaled_height; qbyte *inrow; texnums = &cm->texnum; texnums->base = 0; texnums->fullbright = 0; scaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512; scaled_height = gl_max_size.value < 512 ? gl_max_size.value : 512; for (i=0 ; i<256 ; i++) translate[i] = i; tc<<=4; bc<<=4; for (i=0 ; i<16 ; i++) { if (tc < 128) // the artists made some backwards ranges. sigh. translate[TOP_RANGE+i] = tc+i; else translate[TOP_RANGE+i] = tc+15-i; if (bc < 128) translate[BOTTOM_RANGE+i] = bc+i; else translate[BOTTOM_RANGE+i] = bc+15-i; } for (i=0 ; i<256 ; i++) translate32[i] = d_8to24rgbtable[translate[i]]; out = pixels; fracstep = tinwidth*0x10000/scaled_width; for (i=0 ; i> 1; for (j=0 ; j>16]]; frac += fracstep; out[j+1] = translate32[inrow[frac>>16]]; frac += fracstep; out[j+2] = translate32[inrow[frac>>16]]; frac += fracstep; out[j+3] = translate32[inrow[frac>>16]]; frac += fracstep; } } texnums->base = texture_extension_number++; GL_Bind(texnums->base); glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //now do the fullbrights. out = pixels; fracstep = tinwidth*0x10000/scaled_width; for (i=0 ; i> 1; for (j=0 ; j>16] < 255-vid.fullbright) ((char *) (&out[j]))[3] = 0; //alpha 0 frac += fracstep; } } texnums->fullbright = texture_extension_number++; GL_Bind(texnums->fullbright); glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { (char *)skins = (char *)inf + inf->ofsskins; if (e->skinnum >= 0 && e->skinnum < inf->numskins) skins += e->skinnum; if (!skins->texnums) return NULL; frame = cl.time*skins->skinspeed; frame = frame%skins->texnums; (char *)texnums = (char *)skins + skins->ofstexnums + frame*sizeof(galiastexnum_t); memcpy(&cm->texnum, texnums, sizeof(cm->texnum)); } return &cm->texnum; } (char *)skins = (char *)inf + inf->ofsskins; if (e->skinnum >= 0 && e->skinnum < inf->numskins) skins += e->skinnum; if (!skins->texnums) return NULL; frame = cl.time*skins->skinspeed; frame = frame%skins->texnums; (char *)texnums = (char *)skins + skins->ofstexnums + frame*sizeof(galiastexnum_t); return texnums; } static int numFacing; static qbyte *triangleFacing; static void R_CalcFacing(mesh_t *mesh, vec3_t lightpos) { float *v1, *v2, *v3; vec3_t d1, d2, norm; int i; int *indexes = mesh->indexes; int numtris = mesh->numindexes/3; if (numFacing < numtris) { if (triangleFacing) BZ_Free(triangleFacing); triangleFacing = BZ_Malloc(sizeof(*triangleFacing)*numtris); numFacing = numtris; } for (i = 0; i < numtris; i++, indexes+=3) { v1 = (float *)(mesh->xyz_array + indexes[0]); v2 = (float *)(mesh->xyz_array + indexes[1]); v3 = (float *)(mesh->xyz_array + indexes[2]); VectorSubtract(v1, v2, d1); VectorSubtract(v3, v2, d2); CrossProduct(d1, d2, norm); triangleFacing[i] = (( lightpos[0] - v1[0] ) * norm[0] + ( lightpos[1] - v1[1] ) * norm[1] + ( lightpos[2] - v1[2] ) * norm[2]) > 0; } } #define PROJECTION_DISTANCE 30000 static int numProjectedShadowVerts; static vec3_t *ProjectedShadowVerts; static void R_ProjectShadowVolume(mesh_t *mesh, vec3_t lightpos) { int numverts = mesh->numvertexes; int i; vec4_t *input = mesh->xyz_array; vec3_t *projected; if (numProjectedShadowVerts < numverts) { if (ProjectedShadowVerts) BZ_Free(ProjectedShadowVerts); ProjectedShadowVerts = BZ_Malloc(sizeof(*ProjectedShadowVerts)*numverts); numProjectedShadowVerts = numverts; } projected = ProjectedShadowVerts; for (i = 0; i < numverts; i++) { projected[i][0] = input[i][0] + (input[i][0]-lightpos[0])*PROJECTION_DISTANCE; projected[i][1] = input[i][1] + (input[i][1]-lightpos[1])*PROJECTION_DISTANCE; projected[i][2] = input[i][2] + (input[i][2]-lightpos[2])*PROJECTION_DISTANCE; } } static void R_DrawShadowVolume(mesh_t *mesh) { int t; vec3_t *proj = ProjectedShadowVerts; vec4_t *verts = mesh->xyz_array; int *indexes = mesh->indexes; int *neighbours = mesh->trneighbors; int numtris = mesh->numindexes/3; glBegin(GL_TRIANGLES); for (t = 0; t < numtris; t++) { if (triangleFacing[t]) { //draw front glVertex3fv(verts[indexes[t*3+0]]); glVertex3fv(verts[indexes[t*3+1]]); glVertex3fv(verts[indexes[t*3+2]]); //draw back glVertex3fv(proj[indexes[t*3+1]]); glVertex3fv(proj[indexes[t*3+0]]); glVertex3fv(proj[indexes[t*3+2]]); //draw side caps if (neighbours[t*3+0] < 0 || !triangleFacing[neighbours[t*3+0]]) { glVertex3fv(verts[indexes[t*3+1]]); glVertex3fv(verts[indexes[t*3+0]]); glVertex3fv(proj [indexes[t*3+0]]); glVertex3fv(verts[indexes[t*3+1]]); glVertex3fv(proj [indexes[t*3+0]]); glVertex3fv(proj [indexes[t*3+1]]); } if (neighbours[t*3+1] < 0 || !triangleFacing[neighbours[t*3+1]]) { glVertex3fv(verts[indexes[t*3+2]]); glVertex3fv(verts[indexes[t*3+1]]); glVertex3fv(proj [indexes[t*3+1]]); glVertex3fv(verts[indexes[t*3+2]]); glVertex3fv(proj [indexes[t*3+1]]); glVertex3fv(proj [indexes[t*3+2]]); } if (neighbours[t*3+2] < 0 || !triangleFacing[neighbours[t*3+2]]) { glVertex3fv(verts[indexes[t*3+0]]); glVertex3fv(verts[indexes[t*3+2]]); glVertex3fv(proj [indexes[t*3+2]]); glVertex3fv(verts[indexes[t*3+0]]); glVertex3fv(proj [indexes[t*3+2]]); glVertex3fv(proj [indexes[t*3+0]]); } } } glEnd(); } void R_DrawGAliasModel (entity_t *e) { model_t *clmodel = e->model; vec3_t mins, maxs; vec3_t dist; vec_t add; int i; galiasinfo_t *inf; mesh_t mesh; galiastexnum_t *skin; float entScale; vec3_t lightdir; float tmatrix[3][4]; if (e->flags & Q2RF_VIEWERMODEL && e->keynum == cl.playernum[r_refdef.currentplayernum]+1) return; VectorAdd (e->origin, clmodel->mins, mins); VectorAdd (e->origin, clmodel->maxs, maxs); if (!(e->flags & Q2RF_WEAPONMODEL)) if (R_CullBox (mins, maxs)) return; if (!(r_refdef.flags & 1)) //RDF_NOWORLDMODEL { cl.worldmodel->funcs.LightPointValues(e->origin, shadelight, ambientlight, lightdir); } else { ambientlight[0] = ambientlight[1] = ambientlight[2] = shadelight[0] = shadelight[1] = shadelight[2] = 255; lightdir[0] = 0; lightdir[1] = 1; lightdir[2] = 1; } for (i=0 ; i= cl.time) { VectorSubtract (e->origin, cl_dlights[i].origin, dist); add = cl_dlights[i].radius - Length(dist); if (add > 0) { add*=5; ambientlight[0] += add * cl_dlights[i].color[0]; ambientlight[1] += add * cl_dlights[i].color[1]; ambientlight[2] += add * cl_dlights[i].color[2]; //ZOID models should be affected by dlights as well shadelight[0] += add * cl_dlights[i].color[0]; shadelight[1] += add * cl_dlights[i].color[1]; shadelight[2] += add * cl_dlights[i].color[2]; } } } for (i = 0; i < 3; i++) //clamp light so it doesn't get vulgar. { if (ambientlight[i] > 128) ambientlight[i] = 128; if (ambientlight[i] + shadelight[i] > 192) shadelight[i] = 192 - ambientlight[i]; } if (e->flags & Q2RF_WEAPONMODEL) { for (i = 0; i < 3; i++) { if (ambientlight[i] < 24) ambientlight[i] = shadelight[i] = 24; } } //MORE HUGE HACKS! WHEN WILL THEY CEASE! // clamp lighting so it doesn't overbright as much // ZOID: never allow players to go totally black if (!strcmp(clmodel->name, "progs/player.mdl")) { float fb = r_fullbrightSkins.value; if (fb > cls.allow_fbskins) fb = cls.allow_fbskins; if (fb < 0) fb = 0; if (fb) { for (i = 0; i < 3; i++) { ambientlight[i] = max(ambientlight[i], 8 + fb * 120); shadelight[i] = max(shadelight[i], 8 + fb * 120); } } for (i = 0; i < 3; i++) { if (ambientlight[i] < 8) ambientlight[i] = shadelight[i] = 8; } } for (i = 0; i < 3; i++) { if (ambientlight[i] > 128) ambientlight[i] = 128; shadelight[i] /= 200.0/255; ambientlight[i] /= 200.0/255; } if ((e->drawflags & MLS_MASKIN) == MLS_ABSLIGHT) { shadelight[0] = shadelight[1] = shadelight[2] = e->abslight; ambientlight[0] = ambientlight[1] = ambientlight[2] = 0; } //#define SHOWLIGHTDIR { //lightdir is absolute, shadevector is relative vec3_t entaxis[3]; e->angles[0]*=-1; AngleVectors(e->angles, entaxis[0], entaxis[1], entaxis[2]); e->angles[0]*=-1; entaxis[1][0]*=-1; entaxis[1][1]*=-1; entaxis[1][2]*=-1; shadevector[0] = DotProduct(lightdir, entaxis[0]); shadevector[1] = DotProduct(lightdir, entaxis[1]); shadevector[2] = DotProduct(lightdir, entaxis[2]); VectorNormalize(shadevector); VectorCopy(shadevector, mesh.lightaxis[2]); VectorVectors(mesh.lightaxis[2], mesh.lightaxis[1], mesh.lightaxis[0]); mesh.lightaxis[0][0]*=-1; mesh.lightaxis[0][1]*=-1; mesh.lightaxis[0][2]*=-1; } /* an = e->angles[1]/180*M_PI; shadevector[0] = cos(-an); shadevector[1] = sin(-an); shadevector[2] = 1; VectorNormalize (shadevector); */ GL_DisableMultitexture(); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); if (gl_smoothmodels.value) glShadeModel (GL_SMOOTH); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); glDisable (GL_ALPHA_TEST); if (e->flags & Q2RF_DEPTHHACK) glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); // glColor3f( 1,1,1); if ((e->model->flags & EF_SPECIAL_TRANS)) //hexen2 flags. { glEnable (GL_BLEND); glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); // glColor3f( 1,1,1); glDisable( GL_CULL_FACE ); } else if (e->drawflags & DRF_TRANSLUCENT) { glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); e->alpha = r_wateralpha.value; // glColor4f( 1,1,1,r_wateralpha.value); } else if ((e->model->flags & EF_TRANSPARENT)) { glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glColor3f( 1,1,1); } else if ((e->model->flags & EF_HOLEY)) { glEnable (GL_ALPHA_TEST); // glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glColor3f( 1,1,1); } else if (e->alpha < 1) { glEnable(GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } // glEnable (GL_ALPHA_TEST); glPushMatrix(); R_RotateForEntity(e); if (e->scale != 1 && e->scale != 0) //hexen 2 stuff { vec3_t scale; vec3_t scale_origin; float xyfact, zfact; scale[0] = (clmodel->maxs[0]-clmodel->mins[0])/255; scale[1] = (clmodel->maxs[1]-clmodel->mins[1])/255; scale[2] = (clmodel->maxs[2]-clmodel->mins[2])/255; scale_origin[0] = clmodel->mins[0]; scale_origin[1] = clmodel->mins[1]; scale_origin[2] = clmodel->mins[2]; /* glScalef( 1/scale[0], 1/scale[1], 1/scale[2]); glTranslatef ( -scale_origin[0], -scale_origin[1], -scale_origin[2]); */ if(e->scale != 0 && e->scale != 1) { entScale = (float)e->scale; switch(e->drawflags&SCALE_TYPE_MASKIN) { default: case SCALE_TYPE_UNIFORM: tmatrix[0][0] = scale[0]*entScale; tmatrix[1][1] = scale[1]*entScale; tmatrix[2][2] = scale[2]*entScale; xyfact = zfact = (entScale-1.0)*127.95; break; case SCALE_TYPE_XYONLY: tmatrix[0][0] = scale[0]*entScale; tmatrix[1][1] = scale[1]*entScale; tmatrix[2][2] = scale[2]; xyfact = (entScale-1.0)*127.95; zfact = 1.0; break; case SCALE_TYPE_ZONLY: tmatrix[0][0] = scale[0]; tmatrix[1][1] = scale[1]; tmatrix[2][2] = scale[2]*entScale; xyfact = 1.0; zfact = (entScale-1.0)*127.95; break; } switch(currententity->drawflags&SCALE_ORIGIN_MASKIN) { default: case SCALE_ORIGIN_CENTER: tmatrix[0][3] = scale_origin[0]-scale[0]*xyfact; tmatrix[1][3] = scale_origin[1]-scale[1]*xyfact; tmatrix[2][3] = scale_origin[2]-scale[2]*zfact; break; case SCALE_ORIGIN_BOTTOM: tmatrix[0][3] = scale_origin[0]-scale[0]*xyfact; tmatrix[1][3] = scale_origin[1]-scale[1]*xyfact; tmatrix[2][3] = scale_origin[2]; break; case SCALE_ORIGIN_TOP: tmatrix[0][3] = scale_origin[0]-scale[0]*xyfact; tmatrix[1][3] = scale_origin[1]-scale[1]*xyfact; tmatrix[2][3] = scale_origin[2]-scale[2]*zfact*2.0; break; } } else { tmatrix[0][0] = scale[0]; tmatrix[1][1] = scale[1]; tmatrix[2][2] = scale[2]; tmatrix[0][3] = scale_origin[0]; tmatrix[1][3] = scale_origin[1]; tmatrix[2][3] = scale_origin[2]; } /* if(clmodel->flags&EF_ROTATE) { // Floating motion tmatrix[2][3] += sin(currententity->origin[0] +currententity->origin[1]+(cl.time*3))*5.5; }*/ glTranslatef (tmatrix[0][3],tmatrix[1][3],tmatrix[2][3]); glScalef (tmatrix[0][0],tmatrix[1][1],tmatrix[2][2]); glScalef( 1/scale[0], 1/scale[1], 1/scale[2]); glTranslatef ( -scale_origin[0], -scale_origin[1], -scale_origin[2]); } inf = GLMod_Extradata (clmodel); if (qglPNTrianglesfATI && gl_ati_truform.value) glEnable(GL_PN_TRIANGLES_ATI); while(inf) { R_GAliasBuildMesh(&mesh, inf, e->frame, e->oldframe, e->lerptime, e->alpha); skin = GL_ChooseSkin(inf, clmodel->name, e); c_alias_polys += mesh.numindexes/3; if (!skin) GL_DrawMesh(&mesh, NULL, 1, 0); else { if (skin->bump) GL_DrawMeshBump(&mesh, skin->base, 0, skin->bump, 0); else GL_DrawMesh(&mesh, NULL, skin->base, 0); if (skin->fullbright && r_fb_models.value && cls.allow_luma) { mesh.colors_array = NULL; glEnable(GL_BLEND); glColor4f(1, 1, 1, e->alpha*r_fb_models.value); c_alias_polys += mesh.numindexes/3; GL_DrawMesh(&mesh, NULL, 0, skin->fullbright); } } if (inf->nextsurf) (char *)inf = (char *)inf + inf->nextsurf; else inf = NULL; } if (qglPNTrianglesfATI && gl_ati_truform.value) glDisable(GL_PN_TRIANGLES_ATI); #ifdef SHOWLIGHTDIR //testing glDisable(GL_TEXTURE_2D); glBegin(GL_LINES); glColor3f(1,0,0); glVertex3f( 0, 0, 0); glVertex3f( 100*mesh.lightaxis[0][0], 100*mesh.lightaxis[0][1], 100*mesh.lightaxis[0][2]); glColor3f(0,1,0); glVertex3f( 0, 0, 0); glVertex3f( 100*mesh.lightaxis[1][0], 100*mesh.lightaxis[1][1], 100*mesh.lightaxis[1][2]); glColor3f(0,0,1); glVertex3f( 0, 0, 0); glVertex3f( 100*mesh.lightaxis[2][0], 100*mesh.lightaxis[2][1], 100*mesh.lightaxis[2][2]); glEnd(); glEnable(GL_TEXTURE_2D); #endif glPopMatrix(); glDisable(GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glEnable(GL_TEXTURE_2D); glShadeModel (GL_FLAT); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); if (e->flags & Q2RF_DEPTHHACK) glDepthRange (gldepthmin, gldepthmax); if ((currententity->model->flags & EF_SPECIAL_TRANS) && gl_cull.value) glEnable( GL_CULL_FACE ); if ((currententity->model->flags & EF_HOLEY)) glDisable( GL_ALPHA_TEST ); #ifdef SHOWLIGHTDIR //testing glDisable(GL_TEXTURE_2D); glColor3f(1,1,1); glBegin(GL_LINES); glVertex3f( currententity->origin[0], currententity->origin[1], currententity->origin[2]); glVertex3f( currententity->origin[0]+100*lightdir[0], currententity->origin[1]+100*lightdir[1], currententity->origin[2]+100*lightdir[2]); glEnd(); glEnable(GL_TEXTURE_2D); #endif } void R_DrawGAliasModelLighting (entity_t *e) { model_t *clmodel = e->model; vec3_t mins, maxs; vec3_t dist; vec_t add, an; vec3_t lightdir; int i; galiasinfo_t *inf; mesh_t mesh; if (e->flags & Q2RF_VIEWERMODEL) return; //Total insanity with r_shadows 2... // if (!strcmp (clmodel->name, "progs/flame2.mdl")) // CL_NewDlight (e, e->origin[0]-1, e->origin[1]+1, e->origin[2]+24, 200 + (rand()&31), host_frametime*2, 3); // if (!strcmp (clmodel->name, "progs/armor.mdl")) // CL_NewDlight (e->keynum, e->origin[0]-1, e->origin[1]+1, e->origin[2]+25, 200 + (rand()&31), host_frametime*2, 3); VectorAdd (e->origin, clmodel->mins, mins); VectorAdd (e->origin, clmodel->maxs, maxs); if (!(e->flags & Q2RF_WEAPONMODEL)) if (R_CullBox (mins, maxs)) return; if (!(r_refdef.flags & 1)) //RDF_NOWORLDMODEL { cl.worldmodel->funcs.LightPointValues(e->origin, shadelight, ambientlight, lightdir); } else { ambientlight[0] = ambientlight[1] = ambientlight[2] = shadelight[0] = shadelight[1] = shadelight[2] = 255; lightdir[0] = 0; lightdir[1] = 1; lightdir[2] = 1; } if (e->flags & 4) { if (ambientlight[0] < 24) ambientlight[0] = shadelight[0] = 24; if (ambientlight[1] < 24) ambientlight[1] = shadelight[1] = 24; if (ambientlight[2] < 24) ambientlight[2] = shadelight[2] = 24; } for (i=0 ; i= cl.time) { VectorSubtract (e->origin, cl_dlights[i].origin, dist); add = cl_dlights[i].radius - Length(dist); if (add > 0) { add*=5; ambientlight[0] += add * cl_dlights[i].color[0]; ambientlight[1] += add * cl_dlights[i].color[1]; ambientlight[2] += add * cl_dlights[i].color[2]; //ZOID models should be affected by dlights as well shadelight[0] += add * cl_dlights[i].color[0]; shadelight[1] += add * cl_dlights[i].color[1]; shadelight[2] += add * cl_dlights[i].color[2]; } } } //MORE HUGE HACKS! WHEN WILL THEY CEASE! // clamp lighting so it doesn't overbright as much // ZOID: never allow players to go totally black if (!strcmp(clmodel->name, "progs/player.mdl")) { for (i = 0; i < 3; i++) if (ambientlight[i] < 8) ambientlight[i] = shadelight[i] = 8; } for (i = 0; i < 3; i++) { if (ambientlight[i] > 128) ambientlight[i] = 128; shadelight[i] /= 200.0/255; ambientlight[i] /= 200.0/255; } an = e->angles[1]/180*M_PI; shadevector[0] = cos(-an); shadevector[1] = sin(-an); shadevector[2] = 1; VectorNormalize (shadevector); GL_DisableMultitexture(); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); if (gl_smoothmodels.value) glShadeModel (GL_SMOOTH); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); if (e->flags & Q2RF_DEPTHHACK) glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); glPushMatrix(); R_RotateForEntity(e); inf = GLMod_Extradata (clmodel); if (gl_ati_truform.value) glEnable(GL_PN_TRIANGLES_ATI); while(inf) { R_GAliasBuildMesh(&mesh, inf, e->frame, e->oldframe, e->lerptime, e->alpha); mesh.colors_array = NULL; GL_DrawMesh(&mesh, NULL, 0, 0); if (inf->nextsurf) (char *)inf = (char *)inf + inf->nextsurf; else inf = NULL; } glPopMatrix(); if (gl_ati_truform.value) glDisable(GL_PN_TRIANGLES_ATI); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glShadeModel (GL_FLAT); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); if (e->flags & Q2RF_DEPTHHACK) glDepthRange (gldepthmin, gldepthmax); } //returns result in the form of the result vector void RotateLightVector(vec3_t angles, vec3_t origin, vec3_t lightpoint, vec3_t result) { vec3_t f, r, u, offs; angles[0]*=-1; AngleVectors(angles, f, r, u); angles[0]*=-1; offs[0] = lightpoint[0] - origin[0]; offs[1] = lightpoint[1] - origin[1]; offs[2] = lightpoint[2] - origin[2]; result[0] = DotProduct (offs, f); result[1] = -DotProduct (offs, r); result[2] = DotProduct (offs, u); } //FIXME: Be less agressive. //This function will have to be called twice (for geforce cards), with the same data, so do the building once and rendering twice. void R_DrawGAliasShadowVolume(entity_t *e, vec3_t lightpos, float radius) { model_t *clmodel = e->model; galiasinfo_t *inf; mesh_t mesh; vec3_t lightorg; if (!strcmp (clmodel->name, "progs/flame2.mdl")) return; if (!strncmp (clmodel->name, "progs/bolt", 10)) return; if (r_noaliasshadows.value) return; RotateLightVector(e->angles, e->origin, lightpos, lightorg); if (Length(lightorg) > radius + clmodel->radius) return; glPushMatrix(); R_RotateForEntity(e); inf = GLMod_Extradata (clmodel); while(inf) { if (inf->ofs_trineighbours) { R_GAliasBuildMesh(&mesh, inf, e->frame, e->oldframe, e->lerptime, e->alpha); R_CalcFacing(&mesh, lightorg); R_ProjectShadowVolume(&mesh, lightorg); R_DrawShadowVolume(&mesh); } if (inf->nextsurf) (char *)inf = (char *)inf + inf->nextsurf; else inf = NULL; } glPopMatrix(); } static int R_FindTriangleWithEdge ( int *indexes, int numtris, int start, int end, int ignore) { int i; int match, count; count = 0; match = -1; for (i = 0; i < numtris; i++, indexes += 3) { if ( (indexes[0] == start && indexes[1] == end) || (indexes[1] == start && indexes[2] == end) || (indexes[2] == start && indexes[0] == end) ) { if (i != ignore) match = i; count++; } else if ( (indexes[1] == start && indexes[0] == end) || (indexes[2] == start && indexes[1] == end) || (indexes[0] == start && indexes[2] == end) ) { count++; } } // detect edges shared by three triangles and make them seams if (count > 2) match = -1; return match; } static void R_BuildTriangleNeighbours ( int *neighbours, int *indexes, int numtris ) { int i, *n; int *index; for (i = 0, index = indexes, n = neighbours; i < numtris; i++, index += 3, n += 3) { n[0] = R_FindTriangleWithEdge (indexes, numtris, index[1], index[0], i); n[1] = R_FindTriangleWithEdge (indexes, numtris, index[2], index[1], i); n[2] = R_FindTriangleWithEdge (indexes, numtris, index[0], index[2], i); } } void GL_GenerateNormals(float *orgs, float *normals, int *indicies, int numtris, int numverts) { vec3_t d1, d2; vec3_t norm; int t, i, v1, v2, v3; int tricounts[MD2MAX_VERTS]; vec3_t combined[MD2MAX_VERTS]; int triremap[MD2MAX_VERTS]; if (numverts > MD2MAX_VERTS) return; //not an issue, you just loose the normals. memset(triremap, 0, numverts*sizeof(triremap[0])); v2=0; for (i = 0; i < numverts; i++) //weld points { for (v1 = 0; v1 < v2; v1++) { if (orgs[i*3+0] == combined[v1][0] && orgs[i*3+1] == combined[v1][1] && orgs[i*3+2] == combined[v1][2]) { triremap[i] = v1; break; } } if (v1 == v2) { combined[v1][0] = orgs[i*3+0]; combined[v1][1] = orgs[i*3+1]; combined[v1][2] = orgs[i*3+2]; v2++; triremap[i] = v1; } } memset(tricounts, 0, v2*sizeof(tricounts[0])); memset(combined, 0, v2*sizeof(*combined)); for (t = 0; t < numtris; t++) { v1 = triremap[indicies[t*3]]; v2 = triremap[indicies[t*3+1]]; v3 = triremap[indicies[t*3+2]]; VectorSubtract((orgs+v2*3), (orgs+v1*3), d1); VectorSubtract((orgs+v3*3), (orgs+v1*3), d2); CrossProduct(d1, d2, norm); VectorNormalize(norm); VectorAdd(norm, combined[v1], combined[v1]); VectorAdd(norm, combined[v2], combined[v2]); VectorAdd(norm, combined[v3], combined[v3]); tricounts[v1]++; tricounts[v2]++; tricounts[v3]++; } for (i = 0; i < numverts; i++) { if (tricounts[triremap[i]]) { VectorScale(combined[triremap[i]], 1.0f/tricounts[triremap[i]], normals+i*3); } } } //Q1 model loading #if 1 static galiasinfo_t *galias; static model_t *loadmodel; static dmdl_t *pq1inmodel; #define NUMVERTEXNORMALS 162 extern float r_avertexnormals[NUMVERTEXNORMALS][3]; static void *Q1_LoadFrameGroup (daliasframetype_t *pframetype, int *seamremaps) { galiaspose_t *pose; galiasgroup_t *frame; dtrivertx_t *pinframe; int i, j, k; daliasgroup_t *ingroup; daliasinterval_t *intervals; vec3_t *normals; vec3_t *verts; (char *)frame = (char *)galias + galias->groupofs; for (i = 0; i < pq1inmodel->numframes; i++) { switch(LittleLong(pframetype->type)) { case ALIAS_SINGLE: (char *)pinframe = (char *)(pframetype+1)+sizeof(daliasframe_t); pose = (galiaspose_t *)Hunk_Alloc(sizeof(galiaspose_t) + sizeof(vec3_t)*2*galias->numverts); frame->poseofs = (char *)pose - (char *)frame; frame->numposes = 1; galias->groups++; verts = (vec3_t *)(pose+1); normals = &verts[galias->numverts]; pose->ofsverts = (char *)verts - (char *)pose; pose->ofsnormals = (char *)normals - (char *)pose; for (j = 0; j < pq1inmodel->numverts; j++) { verts[j][0] = pinframe[j].v[0]*pq1inmodel->scale[0]+pq1inmodel->scale_origin[0]; verts[j][1] = pinframe[j].v[1]*pq1inmodel->scale[1]+pq1inmodel->scale_origin[1]; verts[j][2] = pinframe[j].v[2]*pq1inmodel->scale[2]+pq1inmodel->scale_origin[2]; VectorCopy(r_avertexnormals[pinframe[j].lightnormalindex], normals[j]); if (seamremaps[j] != j) { VectorCopy(verts[j], verts[seamremaps[j]]); VectorCopy(normals[j], normals[seamremaps[j]]); } } // GL_GenerateNormals((float*)verts, (float*)normals, (int *)((char *)galias + galias->ofs_indexes), galias->numindexes/3, galias->numverts); (char *)pframetype = (char *)&pinframe[pq1inmodel->numverts]; break; case ALIAS_GROUP: (char *)ingroup = (char *)(pframetype+1); pose = (galiaspose_t *)Hunk_Alloc(ingroup->numframes*(sizeof(galiaspose_t) + sizeof(vec3_t)*2*galias->numverts)); frame->poseofs = (char *)pose - (char *)frame; frame->numposes = LittleLong(ingroup->numframes); galias->groups++; verts = (vec3_t *)(pose+frame->numposes); normals = &verts[galias->numverts]; intervals = (daliasinterval_t *)(ingroup+1); frame->rate = 1/LittleFloat(intervals->interval); pinframe = (dtrivertx_t *)(intervals+frame->numposes); for (k = 0; k < frame->numposes; k++) { pose->ofsverts = (char *)verts - (char *)pose; pose->ofsnormals = (char *)normals - (char *)pose; pinframe = (dtrivertx_t *)((char *)pinframe + sizeof(daliasframe_t)); for (j = 0; j < pq1inmodel->numverts; j++) { verts[j][0] = pinframe[j].v[0]*pq1inmodel->scale[0]+pq1inmodel->scale_origin[0]; verts[j][1] = pinframe[j].v[1]*pq1inmodel->scale[1]+pq1inmodel->scale_origin[1]; verts[j][2] = pinframe[j].v[2]*pq1inmodel->scale[2]+pq1inmodel->scale_origin[2]; VectorCopy(r_avertexnormals[pinframe[j].lightnormalindex], normals[j]); if (seamremaps[j] != j) { VectorCopy(verts[j], verts[seamremaps[j]]); VectorCopy(normals[j], normals[seamremaps[j]]); } } verts = &normals[galias->numverts]; normals = &verts[galias->numverts]; pose++; pinframe += pq1inmodel->numverts; } // GL_GenerateNormals((float*)verts, (float*)normals, (int *)((char *)galias + galias->ofs_indexes), galias->numindexes/3, galias->numverts); (char *)pframetype = (char *)pinframe; break; default: Sys_Error("Bad frame type\n"); } frame++; } return pframetype; } static void *Q1_LoadSkins (daliasskintype_t *pskintype, qboolean alpha) { extern int gl_bumpmappingpossible; galiastexnum_t *texnums; char skinname[MAX_QPATH]; int i; int s, t; int *count; float *intervals; qbyte *data, *saved; galiasskin_t *outskin = (galiasskin_t *)((char *)galias + galias->ofsskins); int texture; int fbtexture; s = pq1inmodel->skinwidth*pq1inmodel->skinheight; for (i = 0; i < pq1inmodel->numskins; i++) { switch(LittleLong(pskintype->type)) { case ALIAS_SKIN_SINGLE: outskin->skinwidth = pq1inmodel->skinwidth; outskin->skinheight = pq1inmodel->skinheight; sprintf(skinname, "%s_%i", loadname, i); texture = Mod_LoadReplacementTexture(skinname, true, false); if (!texture) { sprintf(skinname, "textures/models/%s_%i", loadname, i); texture = Mod_LoadReplacementTexture(skinname, true, false); if (texture && r_fb_models.value) { sprintf(skinname, "textures/models/%s_%i_luma", loadname, i); fbtexture = Mod_LoadReplacementTexture(skinname, true, true); } else fbtexture = 0; } else if (texture && r_fb_models.value) { sprintf(skinname, "%s_%i_luma", loadname, i); fbtexture = Mod_LoadReplacementTexture(skinname, true, true); } else fbtexture = 0; if (!texture) { texnums = Hunk_Alloc(sizeof(*texnums)+s); saved = (qbyte*)(texnums+1); outskin->ofstexels = (qbyte *)(saved) - (qbyte *)outskin; memcpy(saved, pskintype+1, s); GLMod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight); sprintf(skinname, "%s_%i", loadname, i); texture = GL_LoadTexture(skinname, outskin->skinwidth, outskin->skinheight, saved, true, alpha); if (r_fb_models.value) { sprintf(skinname, "%s_%i_luma", loadname, i); fbtexture = GL_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, true, true); } } else { texnums = Hunk_Alloc(sizeof(*texnums)); outskin->ofstexels = 0; } outskin->texnums=1; outskin->ofstexnums = (char *)texnums - (char *)outskin; texnums->base = texture; texnums->fullbright = fbtexture; (char *)pskintype = (char *)(pskintype+1)+s; break; default: outskin->skinwidth = pq1inmodel->skinwidth; outskin->skinheight = pq1inmodel->skinheight; count = (int *)(pskintype+1); intervals = (float *)(count+1); outskin->texnums = LittleLong(*count); data = (qbyte *)(intervals + outskin->texnums); texnums = Hunk_Alloc(sizeof(*texnums)*outskin->texnums); outskin->ofstexnums = (char *)texnums - (char *)outskin; outskin->ofstexels = 0; for (t = 0; t < outskin->texnums; t++,data+=s, texnums++) { sprintf(skinname, "%s_%i%c", loadname, i, t+'a'); texture = Mod_LoadReplacementTexture(skinname, true, false); if (texture) { texnums->base = texture; if (r_fb_models.value) { sprintf(skinname, "%s_%i%c_luma", loadname, i, t+'a'); texnums->fullbright = Mod_LoadReplacementTexture(skinname, true, true); } } else { if (t == 0) { saved = Hunk_Alloc(s); outskin->ofstexels = (qbyte *)(saved) - (qbyte *)outskin; } else saved = BZ_Malloc(s); memcpy(saved, pskintype+1, s); GLMod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight); sprintf(skinname, "%s_%i%c", loadname, i, t+'a'); texnums->base = GL_LoadTexture(skinname, outskin->skinwidth, outskin->skinheight, saved, true, alpha); if (gl_bumpmappingpossible) { char name[MAX_QPATH]; COM_StripExtension(skinname, name); //go for the normalmap strcat(name, "_norm"); texnums->bump = Mod_LoadHiResTexture(name, true, true); if (!texnums->bump) { strcpy(name, loadmodel->name); COM_StripExtension(COM_SkipPath(skinname), COM_SkipPath(name)); strcat(name, "_norm"); texnums->bump = Mod_LoadHiResTexture(name, true, true); if (!texnums->bump) { COM_StripExtension(skinname, name); //bother, go for heightmap and convert strcat(name, "_bump"); texnums->bump = Mod_LoadBumpmapTexture(name); if (!texnums->bump) { strcpy(name, loadmodel->name); strcpy(COM_SkipPath(name), COM_SkipPath(skinname)); //eviile eh? COM_StripExtension(name, name); strcat(name, "_bump"); texnums->bump = Mod_LoadBumpmapTexture(name); } } } } if (r_fb_models.value) { sprintf(skinname, "%s_%i%c_luma", loadname, i, t+'a'); texnums->fullbright = GL_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, true, true); } if (t != 0) //only keep the first. BZ_Free(saved); } } (char *)pskintype = (char *)data; break; } outskin++; } galias->numskins=pq1inmodel->numskins; return pskintype; } void GL_LoadQ1Model (model_t *mod, void *buffer) { vec2_t *st_array; int hunkstart, hunkend, hunktotal; int version; int i, j, onseams; dstvert_t *pinstverts; dtriangle_t *pintriangles; int *seamremap; int *indexes; int size; loadmodel=mod; //we've got to have this bit if (!strcmp(loadmodel->name, "progs/player.mdl") || !strcmp(loadmodel->name, "progs/eyes.mdl")) { unsigned short crc; qbyte *p; int len; char st[40]; CRC_Init(&crc); for (len = com_filesize, p = buffer; len; len--, p++) CRC_ProcessByte(&crc, *p); sprintf(st, "%d", (int) crc); Info_SetValueForKey (cls.userinfo, !strcmp(loadmodel->name, "progs/player.mdl") ? pmodel_name : emodel_name, st, MAX_INFO_STRING); if (cls.state >= ca_connected) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); sprintf(st, "setinfo %s %d", !strcmp(loadmodel->name, "progs/player.mdl") ? pmodel_name : emodel_name, (int)crc); SZ_Print (&cls.netchan.message, st); } } hunkstart = Hunk_LowMark (); pq1inmodel = (dmdl_t *)buffer; version = LittleLong (pq1inmodel->version); if (version != ALIAS_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION); if (pq1inmodel->numframes < 1 || pq1inmodel->numskins < 1 || pq1inmodel->numtris < 1 || pq1inmodel->numverts < 3 || pq1inmodel->skinheight < 1 || pq1inmodel->skinwidth < 1) Sys_Error("Model %s has an invalid quantity\n", mod->name); mod->flags = LittleLong (pq1inmodel->flags); size = sizeof(galiasinfo_t) + pq1inmodel->numframes*sizeof(galiasgroup_t) + pq1inmodel->numskins*sizeof(galiasskin_t); galias = Hunk_Alloc(size); galias->groupofs = sizeof(*galias); galias->ofsskins = sizeof(*galias)+pq1inmodel->numframes*sizeof(galiasgroup_t); galias->nextsurf = 0; //skins if( mod->flags & EF_HOLEY ) pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 3); else if( mod->flags & EF_TRANSPARENT ) pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 2); else if( mod->flags & EF_SPECIAL_TRANS ) pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 4); else pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 0); // pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1)); //count number of verts that are onseam. for (onseams=0,i = 0; i < pq1inmodel->numverts; i++) { if (pinstverts[i].onseam) onseams++; } seamremap = BZ_Malloc(sizeof(int)*pq1inmodel->numverts); galias->numverts = pq1inmodel->numverts+onseams; //st st_array = Hunk_Alloc(sizeof(*st_array)*(pq1inmodel->numverts+onseams)); galias->ofs_st_array = (char *)st_array - (char *)galias; for (j=pq1inmodel->numverts,i = 0; i < pq1inmodel->numverts; i++) { st_array[i][0] = LittleLong(pinstverts[i].s)/(float)pq1inmodel->skinwidth; st_array[i][1] = LittleLong(pinstverts[i].t)/(float)pq1inmodel->skinheight; if (pinstverts[i].onseam) { st_array[j][0] = st_array[i][0]+0.5; st_array[j][1] = st_array[i][1]; seamremap[i] = j; j++; } else seamremap[i] = i; } //trianglelists; pintriangles = (dtriangle_t *)&pinstverts[pq1inmodel->numverts]; galias->numindexes = pq1inmodel->numtris*3; indexes = Hunk_Alloc(galias->numindexes*sizeof(*indexes)); galias->ofs_indexes = (char *)indexes - (char *)galias; for (i=0 ; inumtris ; i++) { if (!pintriangles[i].facesfront) { indexes[i*3+0] = seamremap[LittleLong(pintriangles[i].vertindex[0])]; indexes[i*3+1] = seamremap[LittleLong(pintriangles[i].vertindex[1])]; indexes[i*3+2] = seamremap[LittleLong(pintriangles[i].vertindex[2])]; } else { indexes[i*3+0] = LittleLong(pintriangles[i].vertindex[0]); indexes[i*3+1] = LittleLong(pintriangles[i].vertindex[1]); indexes[i*3+2] = LittleLong(pintriangles[i].vertindex[2]); } } //frames Q1_LoadFrameGroup((daliasframetype_t *)&pintriangles[pq1inmodel->numtris], seamremap); BZ_Free(seamremap); if (r_shadows.value) { int *neighbours; neighbours = Hunk_Alloc(sizeof(int)*3*pq1inmodel->numtris); galias->ofs_trineighbours = (qbyte *)neighbours - (qbyte *)galias; R_BuildTriangleNeighbours(neighbours, indexes, pq1inmodel->numtris); } VectorCopy (pq1inmodel->scale_origin, mod->mins); VectorMA (mod->mins, 255, pq1inmodel->scale, mod->maxs); // // move the complete, relocatable alias model to the cache // hunkend = Hunk_LowMark (); hunktotal = hunkend - hunkstart; Cache_Alloc (&mod->cache, hunktotal, loadname); mod->type = mod_alias; if (!mod->cache.data) { Hunk_FreeToLowMark (hunkstart); return; } memcpy (mod->cache.data, galias, hunktotal); Hunk_FreeToLowMark (hunkstart); } #endif int Mod_ReadFlagsFromMD1(char *name, int md3version) { dmdl_t *pinmodel; char fname[MAX_QPATH]; COM_StripExtension(name, fname); COM_DefaultExtension(fname, ".mdl"); if (!strcmp(name, fname)) //md3 renamed as mdl { COM_StripExtension(name, fname); //seeing as the md3 is named over the mdl, COM_DefaultExtension(fname, ".md1");//read from a file with md1 (one, not an ell) return 0; } pinmodel = (dmdl_t *)COM_LoadTempFile(fname); if (!pinmodel) //not found return 0; if (pinmodel->ident != IDPOLYHEADER) return 0; if (pinmodel->version != ALIAS_VERSION) return 0; return pinmodel->flags; } #ifdef MD2MODELS //////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Q2 model loading static galiasinfo_t *galias; static model_t *loadmodel; static md2_t *pq2inmodel; #define Q2NUMVERTEXNORMALS 162 extern vec3_t bytedirs[Q2NUMVERTEXNORMALS]; static void Q2_LoadSkins(char *skins) { int i; galiastexnum_t *texnums; galiasskin_t *outskin = (galiasskin_t *)((char *)galias + galias->ofsskins); for (i = 0; i < pq2inmodel->num_skins; i++) { texnums = Hunk_Alloc(sizeof(*texnums)); outskin->ofstexnums = (char *)texnums - (char *)outskin; outskin->texnums=1; texnums->base = Mod_LoadReplacementTexture(skins, true, false); outskin->skinwidth = 0; outskin->skinheight = 0; outskin->skinspeed = 0; skins += MD2MAX_SKINNAME; } } #define MD2_MAX_TRIANGLES 4096 void GL_LoadQ2Model (model_t *mod, void *buffer) { int hunkstart, hunkend, hunktotal; int version; int i, j; dmd2stvert_t *pinstverts; dmd2triangle_t *pintri; int *indexes; int numindexes; vec3_t min; vec3_t max; galiaspose_t *pose; galiasgroup_t *poutframe; dmd2aliasframe_t *pinframe; int framesize; vec3_t *verts; vec3_t *normals; vec2_t *st_array; int indremap[MD2_MAX_TRIANGLES*3]; unsigned short ptempindex[MD2_MAX_TRIANGLES*3], ptempstindex[MD2_MAX_TRIANGLES*3]; int numverts; int size; loadmodel=mod; hunkstart = Hunk_LowMark (); pq2inmodel = (md2_t *)buffer; version = LittleLong (pq2inmodel->version); if (version != MD2ALIAS_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, MD2ALIAS_VERSION); if (pq2inmodel->num_frames < 1 || pq2inmodel->num_skins < 0 || pq2inmodel->num_tris < 1 || pq2inmodel->num_xyz < 3 || pq2inmodel->num_st < 3 || pq2inmodel->skinheight < 1 || pq2inmodel->skinwidth < 1) Sys_Error("Model %s has an invalid quantity\n", mod->name); mod->flags = 0; size = sizeof(galiasinfo_t) + pq2inmodel->num_frames*sizeof(galiasgroup_t) + pq2inmodel->num_skins*sizeof(galiasskin_t); galias = Hunk_Alloc(size); galias->groupofs = sizeof(*galias); galias->ofsskins = sizeof(*galias)+pq2inmodel->num_frames*sizeof(galiasgroup_t); galias->nextsurf = 0; //skins Q2_LoadSkins(((char *)pq2inmodel+pq2inmodel->ofs_skins)); //trianglelists; pintri = (dmd2triangle_t *)((char *)pq2inmodel + pq2inmodel->ofs_tris); for (i=0 ; inum_tris ; i++, pintri++) { for (j=0 ; j<3 ; j++) { ptempindex[i*3+j] = ( unsigned short )LittleShort ( pintri->xyz_index[j] ); ptempstindex[i*3+j] = ( unsigned short )LittleShort ( pintri->st_index[j] ); } } numindexes = galias->numindexes = pq2inmodel->num_tris*3; indexes = Hunk_Alloc(galias->numindexes*sizeof(*indexes)); galias->ofs_indexes = (char *)indexes - (char *)galias; memset ( indremap, -1, sizeof(indremap) ); numverts=0; for ( i = 0; i < numindexes; i++ ) { if ( indremap[i] != -1 ) { continue; } for ( j = 0; j < numindexes; j++ ) { if ( j == i ) { continue; } if ( (ptempindex[i] == ptempindex[j]) && (ptempstindex[i] == ptempstindex[j]) ) { indremap[j] = i; } } } // count unique vertexes for ( i = 0; i < numindexes; i++ ) { if ( indremap[i] != -1 ) { continue; } indexes[i] = numverts++; indremap[i] = i; } Con_DPrintf ( "%s: remapped %i verts to %i\n", mod->name, pq2inmodel->num_xyz, numverts ); galias->numverts = numverts; // remap remaining indexes for ( i = 0; i < numindexes; i++ ) { if ( indremap[i] != i ) { indexes[i] = indexes[indremap[i]]; } } // s and t vertices pinstverts = ( dmd2stvert_t * ) ( ( qbyte * )pq2inmodel + LittleLong (pq2inmodel->ofs_st) ); st_array = Hunk_Alloc(sizeof(*st_array)*(numverts)); galias->ofs_st_array = (char *)st_array - (char *)galias; for (j=0 ; jskinwidth); st_array[indexes[j]][1] = (float)(((double)LittleShort (pinstverts[ptempstindex[indremap[j]]].t) + 0.5f) /pq2inmodel->skinheight); } //frames ClearBounds ( mod->mins, mod->maxs ); poutframe = (galiasgroup_t*)((char *)galias + galias->groupofs); framesize = LittleLong (pq2inmodel->framesize); for (i=0 ; inum_frames ; i++) { pose = (galiaspose_t *)Hunk_Alloc(sizeof(galiaspose_t) + sizeof(vec3_t)*2*numverts); poutframe->poseofs = (char *)pose - (char *)poutframe; poutframe->numposes = 1; galias->groups++; verts = (vec3_t *)(pose+1); normals = &verts[galias->numverts]; pose->ofsverts = (char *)verts - (char *)pose; pose->ofsnormals = (char *)normals - (char *)pose; pinframe = ( dmd2aliasframe_t * )( ( qbyte * )pq2inmodel + LittleLong (pq2inmodel->ofs_frames) + i * framesize ); for (j=0 ; j<3 ; j++) { pose->scale[j] = LittleFloat (pinframe->scale[j]); pose->scale_origin[j] = LittleFloat (pinframe->translate[j]); } for (j=0 ; jscale_origin[0]+pose->scale[0]*pinframe->verts[ptempindex[indremap[j]]].v[0]; verts[indexes[j]][1] = pose->scale_origin[1]+pose->scale[1]*pinframe->verts[ptempindex[indremap[j]]].v[1]; verts[indexes[j]][2] = pose->scale_origin[2]+pose->scale[2]*pinframe->verts[ptempindex[indremap[j]]].v[2]; VectorCopy(bytedirs[pinframe->verts[ptempindex[indremap[j]]].lightnormalindex], normals[indexes[j]]); } // Mod_AliasCalculateVertexNormals ( numindexes, poutindex, numverts, poutvertex, qfalse ); VectorCopy ( pose->scale_origin, min ); VectorMA ( pose->scale_origin, 255, pose->scale, max ); // poutframe->radius = RadiusFromBounds ( min, max ); // mod->radius = max ( mod->radius, poutframe->radius ); AddPointToBounds ( min, mod->mins, mod->maxs ); AddPointToBounds ( max, mod->mins, mod->maxs ); // GL_GenerateNormals((float*)verts, (float*)normals, indexes, numindexes/3, numverts); poutframe++; } if (r_shadows.value) { int *neighbours; neighbours = Hunk_Alloc(sizeof(int)*3*pq2inmodel->num_tris); galias->ofs_trineighbours = (qbyte *)neighbours - (qbyte *)galias; R_BuildTriangleNeighbours(neighbours, indexes, pq2inmodel->num_tris); } /* VectorCopy (pq2inmodel->scale_origin, mod->mins); VectorMA (mod->mins, 255, pq2inmodel->scale, mod->maxs); */ // // move the complete, relocatable alias model to the cache // hunkend = Hunk_LowMark (); hunktotal = hunkend - hunkstart; Cache_Alloc (&mod->cache, hunktotal, loadname); mod->type = mod_alias; if (!mod->cache.data) { Hunk_FreeToLowMark (hunkstart); return; } memcpy (mod->cache.data, galias, hunktotal); Hunk_FreeToLowMark (hunkstart); } #endif #ifdef MD3MODELS //structures from Tenebrae typedef struct { int ident; int version; char name[MAX_QPATH]; int flags; //Does anyone know what these are? int numFrames; int numTags; int numSurfaces; int numSkins; int ofsFrames; int ofsTags; int ofsSurfaces; int ofsEnd; } md3Header_t; //then has header->numFrames of these at header->ofs_Frames typedef struct md3Frame_s { vec3_t bounds[2]; vec3_t localOrigin; float radius; char name[16]; } md3Frame_t; //there are header->numSurfaces of these at header->ofsSurfaces, following from ofsEnd typedef struct { int ident; // char name[MAX_QPATH]; // polyset name int flags; int numFrames; // all surfaces in a model should have the same int numShaders; // all surfaces in a model should have the same int numVerts; int numTriangles; int ofsTriangles; int ofsShaders; // offset from start of md3Surface_t int ofsSt; // texture coords are common for all frames int ofsXyzNormals; // numVerts * numFrames int ofsEnd; // next surface follows } md3Surface_t; //at surf+surf->ofsXyzNormals typedef struct { short xyz[3]; qbyte latlong[2]; } md3XyzNormal_t; //surf->numTriangles at surf+surf->ofsTriangles typedef struct { int indexes[3]; } md3Triangle_t; //surf->numVerts at surf+surf->ofsSt typedef struct { float s; float t; } md3St_t; typedef struct { char name[MAX_QPATH]; int shaderIndex; } md3Shader_t; //End of Tenebrae 'assistance' typedef struct { char name[MAX_QPATH]; vec3_t org; float ang[3][3]; } md3tag_t; void GL_LoadQ3Model(model_t *mod, void *buffer) { extern qboolean gl_bumpmappingpossible; int hunkstart, hunkend, hunktotal; // int version; int s, i, j, d; int *indexes; vec3_t min; vec3_t max; galiaspose_t *pose; galiasinfo_t *parent; galiasgroup_t *group; galiasskin_t *skin; galiastexnum_t *texnum; vec3_t *verts; vec3_t *normals; vec2_t *st_array; float lat, lng; md3St_t *inst; md3Triangle_t *intris; md3XyzNormal_t *invert; md3Shader_t *inshader; int size; md3Header_t *header; md3Surface_t *surf; loadmodel=mod; hunkstart = Hunk_LowMark (); header = buffer; // if (header->version != sdfs) // Sys_Error("GL_LoadQ3Model: Bad version\n"); parent = NULL; min[0] = min[1] = min[2] = 0; max[0] = max[1] = max[2] = 0; surf = (md3Surface_t *)((qbyte *)header + header->ofsSurfaces); for (s = 0; s < header->numSurfaces; s++) { size = sizeof(galiasinfo_t) + sizeof(galiasgroup_t)*header->numFrames; galias = Hunk_Alloc(size); galias->groupofs = sizeof(*galias); //frame groups galias->groups = header->numFrames; galias->numverts = surf->numVerts; galias->numindexes = surf->numTriangles*3; galias->numskins = 1; if (parent) parent->nextsurf = (qbyte *)surf - (qbyte *)parent; st_array = Hunk_Alloc(sizeof(vec2_t)*galias->numindexes); galias->ofs_st_array = (qbyte*)st_array - (qbyte*)galias; inst = (md3St_t*)((qbyte*)surf + surf->ofsSt); for (i = 0; i < galias->numverts; i++) { st_array[i][0] = inst[i].s; st_array[i][1] = inst[i].t; } indexes = Hunk_Alloc(sizeof(*indexes)*galias->numindexes); galias->ofs_indexes = (qbyte*)indexes - (qbyte*)galias; intris = (md3Triangle_t *)((qbyte*)surf + surf->ofsTriangles); for (i = 0; i < surf->numTriangles; i++) { indexes[i*3+0] = intris[i].indexes[0]; indexes[i*3+1] = intris[i].indexes[1]; indexes[i*3+2] = intris[i].indexes[2]; } group = (galiasgroup_t *)(galias+1); invert = (md3XyzNormal_t *)((qbyte*)surf + surf->ofsXyzNormals); for (i = 0; i < surf->numFrames; i++) { pose = (galiaspose_t *)Hunk_Alloc(sizeof(galiaspose_t) + sizeof(vec3_t)*2*surf->numVerts); normals = (vec3_t*)(pose+1); verts = normals + surf->numVerts; pose->ofsnormals = (qbyte*)normals - (qbyte*)pose; pose->ofsverts = (qbyte*)verts - (qbyte*)pose; for (j = 0; j < surf->numVerts; j++) { lat = (float)invert[j].latlong[0] * (2 * M_PI)*(1.0 / 255.0); lng = (float)invert[j].latlong[1] * (2 * M_PI)*(1.0 / 255.0); normals[j][0] = cos ( lng ) * sin ( lat ); normals[j][1] = sin ( lng ) * sin ( lat ); normals[j][2] = cos ( lat ); for (d = 0; d < 3; d++) { verts[j][d] = invert[j].xyz[d]/64.0f; if (verts[j][d]max[d]) max[d] = verts[j][d]; } } pose->scale[0] = 1; pose->scale[1] = 1; pose->scale[2] = 1; pose->scale_origin[0] = 0; pose->scale_origin[1] = 0; pose->scale_origin[2] = 0; group->numposes = 1; group->rate = 1; group->poseofs = (qbyte*)pose - (qbyte*)group; group++; invert += surf->numVerts; } if (surf->numShaders) { char name[1024]; skin = Hunk_Alloc(surf->numShaders*(sizeof(galiasskin_t)+sizeof(galiastexnum_t))); galias->ofsskins = (qbyte *)skin - (qbyte *)galias; texnum = (galiastexnum_t *)(skin + surf->numShaders); inshader = (md3Shader_t *)((qbyte *)surf + surf->ofsShaders); for (i = 0; i < surf->numShaders; i++) { skin->texnums = 1; skin->ofstexnums = (qbyte *)texnum - (qbyte *)skin; skin->ofstexels = 0; skin->skinwidth = 0; skin->skinheight = 0; skin->skinspeed = 0; texnum->base = Mod_LoadHiResTexture(inshader->name, true, true); if (!texnum->base) { strcpy(name, loadmodel->name); strcpy(COM_SkipPath(name), COM_SkipPath(inshader->name)); //eviile eh? texnum->base = Mod_LoadHiResTexture(name, true, true); } texnum->bump = 0; if (gl_bumpmappingpossible) { COM_StripExtension(inshader->name, name); //go for the normalmap strcat(name, "_norm"); texnum->bump = Mod_LoadHiResTexture(name, true, true); if (!texnum->bump) { strcpy(name, loadmodel->name); COM_StripExtension(COM_SkipPath(inshader->name), COM_SkipPath(name)); strcat(name, "_norm"); texnum->bump = Mod_LoadHiResTexture(name, true, true); if (!texnum->bump) { COM_StripExtension(inshader->name, name); //bother, go for heightmap and convert strcat(name, "_bump"); texnum->bump = Mod_LoadBumpmapTexture(name); if (!texnum->bump) { strcpy(name, loadmodel->name); strcpy(COM_SkipPath(name), COM_SkipPath(inshader->name)); //eviile eh? COM_StripExtension(name, name); strcat(name, "_bump"); texnum->bump = Mod_LoadBumpmapTexture(name); } } } } if (r_fb_models.value) { COM_StripExtension(inshader->name, name); //go for the normalmap strcat(name, "_luma"); texnum->fullbright = Mod_LoadHiResTexture(name, true, true); if (!texnum->base) { strcpy(name, loadmodel->name); strcpy(COM_SkipPath(name), COM_SkipPath(inshader->name)); //eviile eh? COM_StripExtension(name, name); strcat(name, "_luma"); texnum->fullbright = Mod_LoadBumpmapTexture(name); } } skin++; texnum++; } } VectorCopy(min, loadmodel->mins); VectorCopy(max, loadmodel->maxs); if (r_shadows.value) { int *neighbours; neighbours = Hunk_Alloc(sizeof(int)*3*surf->numTriangles); galias->ofs_trineighbours = (qbyte *)neighbours - (qbyte *)galias; R_BuildTriangleNeighbours(neighbours, indexes, surf->numTriangles); } surf = (md3Surface_t *)((qbyte *)surf + surf->ofsEnd); } // // move the complete, relocatable alias model to the cache // mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0); hunkend = Hunk_LowMark (); hunktotal = hunkend - hunkstart; Cache_Alloc (&mod->cache, hunktotal, loadname); mod->type = mod_alias; if (!mod->cache.data) { Hunk_FreeToLowMark (hunkstart); return; } memcpy (mod->cache.data, galias, hunktotal); Hunk_FreeToLowMark (hunkstart); } #endif