// multilayer terrain shader or similar
shader.textureblendalpha = true;
if (mod_q3shader_force_terrain_alphaflag.integer)
- shader.layers[0].texflags |= TEXF_ALPHA;
+ shader.layers[0].dptexflags |= TEXF_ALPHA;
}
}
layer->blendfunc[0] = GL_SRC_ALPHA;
}
- layer->texflags = 0;
+ layer->dptexflags = 0;
if (layer->alphatest)
- layer->texflags |= TEXF_ALPHA;
+ layer->dptexflags |= TEXF_ALPHA;
switch(layer->blendfunc[0])
{
case GL_SRC_ALPHA:
case GL_ONE_MINUS_SRC_ALPHA:
- layer->texflags |= TEXF_ALPHA;
+ layer->dptexflags |= TEXF_ALPHA;
break;
}
switch(layer->blendfunc[1])
{
case GL_SRC_ALPHA:
case GL_ONE_MINUS_SRC_ALPHA:
- layer->texflags |= TEXF_ALPHA;
+ layer->dptexflags |= TEXF_ALPHA;
break;
}
if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
- layer->texflags |= TEXF_MIPMAP;
+ layer->dptexflags |= TEXF_MIPMAP;
if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
- layer->texflags |= TEXF_PICMIP | TEXF_COMPRESS;
+ layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
if (layer->clampmap)
- layer->texflags |= TEXF_CLAMP;
+ layer->dptexflags |= TEXF_CLAMP;
continue;
}
numparameters = 0;
return NULL;
}
-texture_shaderpass_t *Mod_CreateShaderPass(skinframe_t *skinframe)
+texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
{
- texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(loadmodel->mempool, sizeof(*shaderpass));
+ texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
shaderpass->framerate = 0.0f;
shaderpass->numframes = 1;
shaderpass->blendfunc[0] = GL_ONE;
return shaderpass;
}
-texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
+texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
{
int j;
- texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(loadmodel->mempool, sizeof(*shaderpass));
+ texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
shaderpass->alphatest = layer->alphatest != 0;
shaderpass->framerate = layer->framerate;
shaderpass->numframes = layer->numframes;
for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
shaderpass->tcmods[j] = layer->tcmods[j];
for (j = 0; j < layer->numframes; j++)
- {
- if (cls.state == ca_dedicated)
- {
- shaderpass->skinframes[j] = NULL;
- }
- else if (!(shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false)))
- {
- Con_Printf("^1%s:^7 could not load texture ^3\"%s\"^7 (frame %i) for layer %i of shader ^2\"%s\"\n", loadmodel->name, layer->texturename[j], j, layerindex, texturename);
- shaderpass->skinframes[j] = R_SkinFrame_LoadMissing();
- }
- }
+ shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
return shaderpass;
}
-qboolean Mod_LoadTextureFromQ3Shader(texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags)
+qboolean Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags, int defaultmaterialflags)
{
int texflagsmask, texflagsor;
qboolean success = true;
texture->basealpha = 1.0f;
shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
+ // allow disabling of picmip or compression by defaulttexflags
texflagsmask = ~0;
if(!(defaulttexflags & TEXF_PICMIP))
texflagsmask &= ~TEXF_PICMIP;
if (shader)
{
if (developer_loading.integer)
- Con_Printf("%s: loaded shader for %s\n", loadmodel->name, name);
-
- // allow disabling of picmip or compression by defaulttexflags
- texture->textureflags = (shader->textureflags & texflagsmask) | texflagsor;
+ Con_Printf("%s: loaded shader for %s\n", modelname, name);
if (shader->surfaceparms & Q3SURFACEPARM_SKY)
{
- texture->basematerialflags = MATERIALFLAG_SKY | MATERIALFLAG_NOSHADOW;
- if (shader->skyboxname[0])
+ texture->basematerialflags = MATERIALFLAG_SKY;
+ if (shader->skyboxname[0] && loadmodel)
{
// quake3 seems to append a _ to the skybox name, so this must do so as well
dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
materiallayer = rgbgenvertexlayer;
endofprelayers = rgbgenvertexlayer;
firstpostlayer = rgbgenvertexlayer + 1;
+ // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
+ if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
+ texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR;
}
else if (rgbgendiffuselayer >= 0)
{
// convert the main material layer
// FIXME: if alphagenspecularlayer is used, we should pass a specular texture name to R_SkinFrame_LoadExternal and have it load that texture instead of the assumed name for _gloss texture
if (materiallayer >= 0)
- texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(&shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].texflags & texflagsmask) | texflagsor, texture->name);
+ texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
// convert the terrain background blend layer (if any)
if (terrainbackgroundlayer >= 0)
- texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(&shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].texflags & texflagsmask) | texflagsor, texture->name);
+ texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
// convert the prepass layers (if any)
texture->startpreshaderpass = shaderpassindex;
for (i = 0; i < endofprelayers; i++)
- texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(&shader->layers[i], i, (shader->layers[i].texflags & texflagsmask) | texflagsor, texture->name);
+ texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
texture->endpreshaderpass = shaderpassindex;
texture->startpostshaderpass = shaderpassindex;
// convert the postpass layers (if any)
for (i = firstpostlayer; i < shader->numlayers; i++)
- texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(&shader->layers[i], i, (shader->layers[i].texflags & texflagsmask) | texflagsor, texture->name);
+ texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
texture->startpostshaderpass = shaderpassindex;
}
if (shader->dpmeshcollisions)
texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
if (shader->dpshaderkill && developer_extra.integer)
- Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", loadmodel->name, name);
+ Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
}
else if (!strcmp(texture->name, "noshader") || !texture->name[0])
{
if (developer_extra.integer)
- Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", loadmodel->name, name);
+ Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
}
else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
{
if (developer_extra.integer)
- Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", loadmodel->name, name);
+ Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
texture->supercontents = SUPERCONTENTS_SOLID;
}
else
{
if (developer_extra.integer)
- Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", loadmodel->name, texture->name);
+ Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
{
- texture->basematerialflags |= MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
+ texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
texture->supercontents = SUPERCONTENTS_SOLID;
}
else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
{
- texture->basematerialflags |= MATERIALFLAG_SKY | MATERIALFLAG_NOSHADOW;
+ texture->basematerialflags = MATERIALFLAG_SKY;
texture->supercontents = SUPERCONTENTS_SKY;
}
else
{
- texture->basematerialflags |= MATERIALFLAG_WALL;
+ texture->basematerialflags = defaultmaterialflags;
texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
}
if(cls.state == ca_dedicated)
}
else
{
- if (fallback)
+ skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
+ if (skinframe)
{
- texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false));
- if (texture->materialshaderpass->skinframes[0])
- {
- if (texture->materialshaderpass->skinframes[0]->hasalpha)
- texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
- if (texture->q2contents)
- texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(loadmodel, texture->q2contents);
- }
- else
- success = false;
+ texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
+ if (texture->materialshaderpass->skinframes[0]->hasalpha)
+ texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
+ if (texture->q2contents)
+ texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
}
else
success = false;
if (!success && warnmissing)
- Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", loadmodel->name, texture->name);
+ Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
}
}
// init the animation variables
texture->currentframe = texture;
+ texture->currentmaterialflags = texture->basematerialflags;
if (!texture->materialshaderpass)
- texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(R_SkinFrame_LoadMissing());
+ texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
if (!texture->materialshaderpass->skinframes[0])
texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
return success;
}
+void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
+{
+ if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
+ Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
+
+ strlcpy(texture->name, name, sizeof(texture->name));
+ texture->basealpha = 1.0f;
+ texture->basematerialflags = materialflags;
+ texture->supercontents = supercontents;
+
+ texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
+ texture->offsetscale = 1;
+ texture->offsetbias = 0;
+ texture->specularscalemod = 1;
+ texture->specularpowermod = 1;
+ texture->rtlightambient = 0;
+ texture->transparentsort = TRANSPARENTSORT_DISTANCE;
+ // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
+ // JUST GREP FOR "specularscalemod = 1".
+
+ if (developer_extra.integer)
+ Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
+ if (skinframe)
+ texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
+
+ // init the animation variables
+ texture->currentmaterialflags = texture->basematerialflags;
+ texture->currentframe = texture;
+ texture->currentskinframe = skinframe;
+ texture->backgroundcurrentskinframe = NULL;
+}
+
+void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
+{
+ int i, j;
+ for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
+ {
+ if (texture->shaderpasses[i])
+ {
+ if (purgeskins)
+ for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
+ if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
+ R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
+ Mem_Free(texture->shaderpasses[i]);
+ texture->shaderpasses[i] = NULL;
+ }
+ }
+ texture->materialshaderpass = NULL;
+ texture->currentskinframe = NULL;
+ texture->backgroundcurrentskinframe = NULL;
+}
+
skinfile_t *Mod_LoadSkinFiles(void)
{
int i, words, line, wordsoverflow;
continue;
if (model && model->TraceLine)
{
- model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_VISBLOCKERMASK);
+ model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
if (trace.fraction < 1)
continue;
}
if (!normal)
{
// for light grid we'd better check visibility of the offset point
- cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_VISBLOCKERMASK);
+ cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
if (trace.fraction < 1)
VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
}
}
Mod_GenerateLightmaps(cl.worldmodel);
}
+
+void Mod_Mesh_Create(dp_model_t *mod, const char *name)
+{
+ memset(mod, 0, sizeof(*mod));
+ strlcpy(mod->name, name, sizeof(mod->name));
+ mod->mempool = Mem_AllocPool(name, 0, NULL);
+ mod->texturepool = R_AllocTexturePool();
+ mod->Draw = R_Q1BSP_Draw;
+ mod->DrawDepth = R_Q1BSP_DrawDepth;
+ mod->DrawDebug = R_Q1BSP_DrawDebug;
+ mod->DrawPrepass = R_Q1BSP_DrawPrepass;
+ mod->GetLightInfo = R_Q1BSP_GetLightInfo;
+ mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
+ mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+ mod->DrawLight = R_Q1BSP_DrawLight;
+}
+
+void Mod_Mesh_Destroy(dp_model_t *mod)
+{
+ Mod_UnloadModel(mod);
+}
+
+// resets the mesh model to have no geometry to render, ready for a new frame -
+// the mesh will be prepared for rendering later using Mod_Mesh_Finalize
+void Mod_Mesh_Reset(dp_model_t *mod)
+{
+ mod->num_surfaces = 0;
+ mod->surfmesh.num_vertices = 0;
+ mod->surfmesh.num_triangles = 0;
+ memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
+ mod->DrawSky = NULL; // will be set if a texture needs it
+ mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
+}
+
+texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
+{
+ int i;
+ texture_t *t;
+ for (i = 0; i < mod->num_textures; i++)
+ if (!strcmp(mod->data_textures[i].name, name))
+ return mod->data_textures + i;
+ if (mod->max_textures <= mod->num_textures)
+ {
+ texture_t *oldtextures = mod->data_textures;
+ mod->max_textures = max(mod->max_textures * 2, 1024);
+ mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
+ // update the pointers
+ for (i = 0; i < mod->num_surfaces; i++)
+ mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
+ }
+ t = &mod->data_textures[mod->num_textures++];
+ Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, false, true, defaulttexflags, defaultmaterialflags);
+ switch (defaultdrawflags & DRAWFLAG_MASK)
+ {
+ case DRAWFLAG_ADDITIVE:
+ t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
+ t->currentmaterialflags = t->basematerialflags;
+ break;
+ case DRAWFLAG_MODULATE:
+ t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
+ t->currentmaterialflags = t->basematerialflags;
+ t->customblendfunc[0] = GL_DST_COLOR;
+ t->customblendfunc[1] = GL_ZERO;
+ break;
+ case DRAWFLAG_2XMODULATE:
+ t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
+ t->currentmaterialflags = t->basematerialflags;
+ t->customblendfunc[0] = GL_DST_COLOR;
+ t->customblendfunc[1] = GL_SRC_COLOR;
+ break;
+ case DRAWFLAG_SCREEN:
+ t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
+ t->currentmaterialflags = t->basematerialflags;
+ t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
+ t->customblendfunc[1] = GL_ONE;
+ break;
+ default:
+ break;
+ }
+ return t;
+}
+
+msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
+{
+ msurface_t *surf;
+ // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
+ if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
+ return mod->data_surfaces + mod->num_surfaces - 1;
+ // create new surface
+ if (mod->max_surfaces == mod->num_surfaces)
+ {
+ mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
+ mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
+ mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
+ }
+ surf = mod->data_surfaces + mod->num_surfaces;
+ mod->num_surfaces++;
+ memset(surf, 0, sizeof(*surf));
+ surf->texture = tex;
+ surf->num_firsttriangle = mod->surfmesh.num_triangles;
+ surf->num_firstvertex = mod->surfmesh.num_vertices;
+ if (tex->basematerialflags & (MATERIALFLAG_SKY))
+ mod->DrawSky = R_Q1BSP_DrawSky;
+ if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+ mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+ return surf;
+}
+
+int Mod_Mesh_IndexForVertex(dp_model_t *mod, msurface_t *surf, float x, float y, float z, float nx, float ny, float nz, float s, float t, float u, float v, float r, float g, float b, float a)
+{
+ int hashindex, h, vnum, mask;
+ surfmesh_t *mesh = &mod->surfmesh;
+ if (mesh->max_vertices == mesh->num_vertices)
+ {
+ mesh->max_vertices = max(mesh->num_vertices * 2, 256);
+ mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
+ mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
+ mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
+ mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
+ mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
+ mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
+ mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
+ // rebuild the hash table
+ mesh->num_vertexhashsize = 4 * mesh->max_vertices;
+ mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
+ mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
+ memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
+ mask = mod->surfmesh.num_vertexhashsize - 1;
+ // no need to hash the vertices for the entire model, the latest surface will suffice.
+ for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
+ {
+ // this uses prime numbers intentionally for computing the hash
+ hashindex = (unsigned int)(mesh->data_vertex3f[vnum * 3 + 0] * 2003 + mesh->data_vertex3f[vnum * 3 + 1] * 4001 + mesh->data_vertex3f[vnum * 3 + 2] * 7919 + mesh->data_normal3f[vnum * 3 + 0] * 4097 + mesh->data_normal3f[vnum * 3 + 1] * 257 + mesh->data_normal3f[vnum * 3 + 2] * 17) & mask;
+ for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
+ ; // just iterate until we find the terminator
+ mesh->data_vertexhash[h] = vnum;
+ }
+ }
+ mask = mod->surfmesh.num_vertexhashsize - 1;
+ // this uses prime numbers intentionally for computing the hash
+ hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
+ // when possible find an identical vertex within the same surface and return it
+ for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
+ {
+ if (vnum >= surf->num_firstvertex
+ && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
+ && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
+ && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
+ && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
+ && mesh->data_lightmapcolor4f[vnum * 4 + 0] == r && mesh->data_lightmapcolor4f[vnum * 4 + 1] == g && mesh->data_lightmapcolor4f[vnum * 4 + 2] == b && mesh->data_lightmapcolor4f[vnum * 4 + 3] == a)
+ return vnum;
+ }
+ // add the new vertex
+ vnum = mesh->num_vertices++;
+ if (surf->num_vertices > 0)
+ {
+ if (surf->mins[0] > x) surf->mins[0] = x;
+ if (surf->mins[1] > y) surf->mins[1] = y;
+ if (surf->mins[2] > z) surf->mins[2] = z;
+ if (surf->maxs[0] < x) surf->maxs[0] = x;
+ if (surf->maxs[1] < y) surf->maxs[1] = y;
+ if (surf->maxs[2] < z) surf->maxs[2] = z;
+ }
+ else
+ {
+ VectorSet(surf->mins, x, y, z);
+ VectorSet(surf->maxs, x, y, z);
+ }
+ surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
+ mesh->data_vertexhash[h] = vnum;
+ mesh->data_vertex3f[vnum * 3 + 0] = x;
+ mesh->data_vertex3f[vnum * 3 + 1] = y;
+ mesh->data_vertex3f[vnum * 3 + 2] = z;
+ mesh->data_normal3f[vnum * 3 + 0] = nx;
+ mesh->data_normal3f[vnum * 3 + 1] = ny;
+ mesh->data_normal3f[vnum * 3 + 2] = nz;
+ mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
+ mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
+ mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
+ mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
+ mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
+ mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
+ mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
+ mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
+ return vnum;
+}
+
+void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
+{
+ surfmesh_t *mesh = &mod->surfmesh;
+ if (mesh->max_triangles == mesh->num_triangles)
+ {
+ mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
+ mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
+ mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
+ }
+ mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
+ mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
+ mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
+ mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
+ mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
+ mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
+ mesh->num_triangles++;
+ surf->num_triangles++;
+}
+
+static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
+{
+ int i, j;
+ texture_t *tex;
+ msurface_t *surf, *surf2;
+
+ // build the sorted surfaces list properly to reduce material setup
+ // this is easy because we're just sorting on texture and don't care about the order of textures
+ mod->nummodelsurfaces = 0;
+ for (i = 0; i < mod->num_surfaces; i++)
+ mod->data_surfaces[i].included = false;
+ for (i = 0; i < mod->num_surfaces; i++)
+ {
+ surf = mod->data_surfaces + i;
+ if (surf->included)
+ continue;
+ tex = surf->texture;
+ // j = i is intentional
+ for (j = i; j < mod->num_surfaces; j++)
+ {
+ surf2 = mod->data_surfaces + j;
+ if (surf2->included)
+ continue;
+ if (surf2->texture == tex)
+ {
+ surf2->included = true;
+ mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
+ }
+ }
+ }
+}
+
+void Mod_Mesh_ComputeBounds(dp_model_t *mod)
+{
+ int i;
+ vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
+
+ if (mod->surfmesh.num_vertices > 0)
+ {
+ // calculate normalmins/normalmaxs
+ VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
+ VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
+ for (i = 1; i < mod->surfmesh.num_vertices; i++)
+ {
+ float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
+ float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
+ float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
+ // expand bounds to include this vertex
+ if (mod->normalmins[0] > x) mod->normalmins[0] = x;
+ if (mod->normalmins[1] > y) mod->normalmins[1] = y;
+ if (mod->normalmins[2] > z) mod->normalmins[2] = z;
+ if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
+ if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
+ if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
+ }
+ // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
+ // (fast but less accurate than doing it per vertex)
+ x2a = mod->normalmins[0] * mod->normalmins[0];
+ x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
+ y2a = mod->normalmins[1] * mod->normalmins[1];
+ y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
+ z2a = mod->normalmins[2] * mod->normalmins[2];
+ z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
+ x2 = max(x2a, x2b);
+ y2 = max(y2a, y2b);
+ z2 = max(z2a, z2b);
+ yawradius = sqrt(x2 + y2);
+ rotatedradius = sqrt(x2 + y2 + z2);
+ VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
+ VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
+ VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
+ VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
+ mod->radius = rotatedradius;
+ mod->radius2 = x2 + y2 + z2;
+ }
+ else
+ {
+ VectorClear(mod->normalmins);
+ VectorClear(mod->normalmaxs);
+ VectorClear(mod->yawmins);
+ VectorClear(mod->yawmaxs);
+ VectorClear(mod->rotatedmins);
+ VectorClear(mod->rotatedmaxs);
+ mod->radius = 0;
+ mod->radius2 = 0;
+ }
+}
+
+void Mod_Mesh_Finalize(dp_model_t *mod)
+{
+ Mod_Mesh_ComputeBounds(mod);
+ Mod_Mesh_MakeSortedSurfaces(mod);
+ Mod_BuildTextureVectorsFromNormals(0, mod->surfmesh.num_vertices, mod->surfmesh.num_triangles, mod->surfmesh.data_vertex3f, mod->surfmesh.data_texcoordtexture2f, mod->surfmesh.data_normal3f, mod->surfmesh.data_element3i, mod->surfmesh.data_svector3f, mod->surfmesh.data_tvector3f, true);
+}