{
// link stuff
CL_RelinkWorld();
+ // the scene mesh is added first for easier debugging (consistent spot in render entities list)
+ CL_MeshEntities_Scene_AddRenderEntity();
CL_RelinkStaticEntities();
CL_RelinkBeams();
CL_RelinkEffects();
// update view blend
V_CalcViewBlend();
-
- CL_MeshEntities_AddToScene();
}
/*
// when csqc is loaded, it will call this in CSQC_UpdateView
if (!cl.csqc_loaded)
+ {
+ // clear the CL_Mesh_Scene() used for some engine effects
+ CL_MeshEntities_Scene_Clear();
+ // add engine entities and effects
CSQC_RelinkAllEntities(ENTMASK_ENGINE | ENTMASK_ENGINEVIEWMODELS);
+ }
// decals, particles, and explosions will be updated during rneder
}
dp_model_t cl_meshentitymodels[NUM_MESHENTITIES];
const char *cl_meshentitynames[NUM_MESHENTITIES] =
{
- "MESH_DEBUG",
- "MESH_CSQC_POLYGONS",
- "MESH_PARTICLES",
+ "MESH_SCENE",
"MESH_UI",
};
ent->state_current.active = true;
ent->render.model = cl_meshentitymodels + i;
Mod_Mesh_Create(ent->render.model, cl_meshentitynames[i]);
- ent->render.alpha = 0.999999f; // not quite 1 so that MATERIALFLAG_ALPHA is always set.
+ ent->render.alpha = 1;
ent->render.flags = RENDER_SHADOW | RENDER_LIGHT;
ent->render.framegroupblend[0].lerp = 1;
ent->render.frameblend[0].lerp = 1;
R_RegisterModule("cl_meshentities", CL_MeshEntities_Restart, CL_MeshEntities_Restart, CL_MeshEntities_Restart, CL_MeshEntities_Restart, CL_MeshEntities_Restart);
}
-void CL_MeshEntities_AddToScene(void)
+void CL_MeshEntities_Scene_Clear(void)
{
- int i;
- entity_t *ent;
- for (i = 0; i < NUM_MESHENTITIES && r_refdef.scene.numentities < r_refdef.scene.maxentities; i++)
- {
- ent = cl_meshentities + i;
- if (ent->render.model->num_surfaces == 0)
- continue;
- Mod_Mesh_Finalize(ent->render.model);
- VectorCopy(ent->render.model->normalmins, ent->render.mins);
- VectorCopy(ent->render.model->normalmaxs, ent->render.maxs);
- r_refdef.scene.entities[r_refdef.scene.numentities++] = &ent->render;
- }
+ Mod_Mesh_Reset(CL_Mesh_Scene());
}
-void CL_MeshEntities_Reset(void)
+void CL_MeshEntities_Scene_AddRenderEntity(void)
{
- int i;
- entity_t *ent;
- for (i = 0; i < NUM_MESHENTITIES && r_refdef.scene.numentities < r_refdef.scene.maxentities; i++)
+ entity_t* ent = &cl_meshentities[MESH_SCENE];
+ r_refdef.scene.entities[r_refdef.scene.numentities++] = &ent->render;
+}
+
+void CL_MeshEntities_Scene_FinalizeRenderEntity(void)
+{
+ entity_t *ent = &cl_meshentities[MESH_SCENE];
+ if (ent->render.model->num_surfaces)
{
- ent = cl_meshentities + i;
- Mod_Mesh_Reset(ent->render.model);
+ Mod_Mesh_Finalize(ent->render.model);
+ VectorCopy(ent->render.model->normalmins, ent->render.mins);
+ VectorCopy(ent->render.model->normalmaxs, ent->render.maxs);
}
}
CL_VM_UpdateView(r_stereo_side ? 0.0 : max(0.0, cl.time - cl.oldtime));
else
{
+ // Prepare the scene mesh for rendering - this is lightning beams and other effects rendered as normal surfaces
+ CL_MeshEntities_Scene_FinalizeRenderEntity();
+
CL_UpdateEntityShading();
R_RenderView(0, NULL, NULL, r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
}
void CL_RotateMoves(const matrix4x4_t *m);
typedef enum meshname_e {
- MESH_DEBUG,
- MESH_CSQCPOLYGONS,
- MESH_PARTICLES,
+ MESH_SCENE, // CSQC R_PolygonBegin, potentially also engine particles and debug stuff
MESH_UI,
NUM_MESHENTITIES,
} meshname_t;
extern entity_t cl_meshentities[NUM_MESHENTITIES];
extern dp_model_t cl_meshentitymodels[NUM_MESHENTITIES];
extern const char *cl_meshentitynames[NUM_MESHENTITIES];
-#define CL_Mesh_Debug() (&cl_meshentitymodels[MESH_DEBUG])
-#define CL_Mesh_CSQC() (&cl_meshentitymodels[MESH_CSQCPOLYGONS])
-#define CL_Mesh_Particles() (&cl_meshentitymodels[MESH_PARTICLES])
+#define CL_Mesh_Scene() (&cl_meshentitymodels[MESH_SCENE])
#define CL_Mesh_UI() (&cl_meshentitymodels[MESH_UI])
-void CL_MeshEntities_AddToScene(void);
-void CL_MeshEntities_Reset(void);
+void CL_MeshEntities_Scene_Clear(void);
+void CL_MeshEntities_Scene_AddRenderEntity(void);
+void CL_MeshEntities_Scene_FinalizeRenderEntity(void);
void CL_UpdateEntityShading(void);
void CL_NewFrameReceived(int num);
cl.csqc_vidvars.drawenginesbar = false;
cl.csqc_vidvars.drawcrosshair = false;
CSQC_R_RecalcView();
+ // clear the CL_Mesh_Scene() used for CSQC polygons and engine effects, they will be added by CSQC_RelinkAllEntities and manually created by CSQC
+ CL_MeshEntities_Scene_Clear();
}
//#301 void(float mask) addentities (EXT_CSQC)
csqc_main_r_refdef_view = r_refdef.view;
}
+ // now after all of the predraw we know the geometry in the scene mesh and can finalize it for rendering
+ CL_MeshEntities_Scene_FinalizeRenderEntity();
+
// we need to update any RENDER_VIEWMODEL entities at this point because
// csqc supplies its own view matrix
CL_UpdateViewEntities();
- CL_MeshEntities_AddToScene();
CL_UpdateEntityShading();
// now draw stuff!
R_RenderView(0, NULL, NULL, r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
- Mod_Mesh_Reset(CL_Mesh_CSQC());
-
// callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView
t = Sys_DirtyTime() - t;if (t < 0 || t >= 1800) t = 0;
prog->functions[PRVM_clientfunction(CSQC_UpdateView)].totaltime -= t;
}
// we need to remember whether this is a 2D or 3D mesh we're adding to
- mod = draw2d ? CL_Mesh_UI() : CL_Mesh_CSQC();
+ mod = draw2d ? CL_Mesh_UI() : CL_Mesh_Scene();
prog->polygonbegin_model = mod;
- Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, texname, drawflags, TEXF_ALPHA, MATERIALFLAG_WALL | MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX), false);
+ strlcpy(prog->polygonbegin_texname, texname, sizeof(prog->polygonbegin_texname));
+ prog->polygonbegin_drawflags = drawflags;
+ prog->polygonbegin_numvertices = 0;
}
//void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
const prvm_vec_t *tc = PRVM_G_VECTOR(OFS_PARM1);
const prvm_vec_t *c = PRVM_G_VECTOR(OFS_PARM2);
const prvm_vec_t a = PRVM_G_FLOAT(OFS_PARM3);
+ float *o;
dp_model_t *mod = prog->polygonbegin_model;
- int e0, e1, e2;
- msurface_t *surf;
VM_SAFEPARMCOUNT(4, VM_CL_R_PolygonVertex);
- if (!mod || mod->num_surfaces == 0)
+ if (!mod)
{
VM_Warning(prog, "VM_CL_R_PolygonVertex: VM_CL_R_PolygonBegin wasn't called\n");
return;
}
- surf = &mod->data_surfaces[mod->num_surfaces - 1];
- e2 = Mod_Mesh_IndexForVertex(mod, surf, v[0], v[1], v[2], 0, 0, 0, tc[0], tc[1], 0, 0, c[0], c[1], c[2], a);
- if (surf->num_vertices >= 3)
+ if (prog->polygonbegin_maxvertices <= prog->polygonbegin_numvertices)
{
- // the first element is the start of the triangle fan
- e0 = surf->num_firstvertex;
- // the second element is the previous vertex
- e1 = e0 + 1;
- if (surf->num_triangles > 0)
- e1 = mod->surfmesh.data_element3i[(surf->num_firsttriangle + surf->num_triangles) * 3 - 1];
- Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
+ prog->polygonbegin_maxvertices = max(16, prog->polygonbegin_maxvertices * 2);
+ prog->polygonbegin_vertexdata = (float *)Mem_Realloc(prog->progs_mempool, prog->polygonbegin_vertexdata, prog->polygonbegin_maxvertices * sizeof(float[10]));
}
+ o = prog->polygonbegin_vertexdata + prog->polygonbegin_numvertices++ * 10;
+
+ o[0] = v[0];
+ o[1] = v[1];
+ o[2] = v[2];
+ o[3] = tc[0];
+ o[4] = tc[1];
+ o[5] = tc[2];
+ o[6] = c[0];
+ o[7] = c[1];
+ o[8] = c[2];
+ o[9] = a;
}
//void() R_EndPolygon
static void VM_CL_R_PolygonEnd (prvm_prog_t *prog)
{
+ int i;
+ qboolean hascolor;
+ qboolean hasalpha;
+ int e0 = 0, e1 = 0, e2 = 0;
+ float *o;
dp_model_t *mod = prog->polygonbegin_model;
msurface_t *surf;
VM_SAFEPARMCOUNT(0, VM_CL_R_PolygonEnd);
- if (!mod || mod->num_surfaces == 0)
+ if (!mod)
{
VM_Warning(prog, "VM_CL_R_PolygonEnd: VM_CL_R_PolygonBegin wasn't called\n");
return;
}
- surf = &mod->data_surfaces[mod->num_surfaces - 1];
+
+ // determine if vertex alpha is being used so we can provide that hint to GetTexture...
+ hascolor = false;
+ hasalpha = false;
+ for (i = 0; i < prog->polygonbegin_numvertices; i++)
+ {
+ o = prog->polygonbegin_vertexdata + 10 * i;
+ if (o[6] != 1.0f || o[7] != 1.0f || o[8] != 1.0f)
+ hascolor = true;
+ if (o[9] != 1.0f)
+ hasalpha = true;
+ }
+
+ // create the surface, looking up the best matching texture/shader
+ surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, prog->polygonbegin_texname, prog->polygonbegin_drawflags, TEXF_ALPHA, MATERIALFLAG_WALL | (hascolor ? MATERIALFLAG_VERTEXCOLOR : 0) | (hasalpha ? MATERIALFLAG_ALPHAGEN_VERTEX | MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW : 0)), false);
+ // create triangle fan
+ for (i = 0; i < prog->polygonbegin_numvertices; i++)
+ {
+ o = prog->polygonbegin_vertexdata + 10 * i;
+ e2 = Mod_Mesh_IndexForVertex(mod, surf, o[0], o[1], o[2], 0, 0, 0, o[3], o[4], 0, 0, o[6], o[7], o[8], o[9]);
+ if (i >= 2)
+ Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
+ else if (i == 0)
+ e0 = e2;
+ e1 = e2;
+ }
+ // build normals (since they are not provided)
Mod_BuildNormals(surf->num_firstvertex, surf->num_vertices, surf->num_triangles, mod->surfmesh.data_vertex3f, mod->surfmesh.data_element3i + 3 * surf->num_firsttriangle, mod->surfmesh.data_normal3f, true);
+
+ // reset state
prog->polygonbegin_model = NULL;
+ prog->polygonbegin_texname[0] = 0;
+ prog->polygonbegin_drawflags = 0;
+ prog->polygonbegin_numvertices = 0;
}
/*
return;
}
- // TODO: render the mesh using R_Q1BSP_Draw or similar, for full material support.
+ // this is roughly equivalent to R_Q1BSP_Draw, so the UI can use full material feature set
r_refdef.view.colorscale = 1;
r_textureframe++; // used only by R_GetCurrentTexture
GL_DepthMask(false);
R_TimeReport("client");
CL_UpdateScreen();
- CL_MeshEntities_Reset();
R_TimeReport("render");
if (host_speeds.integer)
{
if (developer_extra.integer)
Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
+ texture->basematerialflags = defaultmaterialflags;
texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
}
else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
texture_t *t;
int drawflag = defaultdrawflags & DRAWFLAG_MASK;
for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
- if (!strcmp(t->name, name) && t->drawflag == drawflag)
+ if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
return t;
if (mod->max_textures <= mod->num_textures)
{
}
t = &mod->data_textures[mod->num_textures++];
Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, false, true, defaulttexflags, defaultmaterialflags);
- t->drawflag = drawflag;
+ t->mesh_drawflag = drawflag;
+ t->mesh_defaulttexflags = defaulttexflags;
+ t->mesh_defaultmaterialflags = defaultmaterialflags;
switch (defaultdrawflags & DRAWFLAG_MASK)
{
case DRAWFLAG_ADDITIVE:
// diffuse and ambient
float rtlightambient;
- // used by Mod_Mesh_GetTexture for drawflag overrides, to disambiguate the same texture with different drawflags
- int drawflag;
+ // used by Mod_Mesh_GetTexture for drawflag and materialflag overrides, to disambiguate the same texture with different hints
+ int mesh_drawflag;
+ int mesh_defaulttexflags;
+ int mesh_defaultmaterialflags;
}
texture_t;
// buffer for storing all tempstrings created during one invocation of ExecuteProgram
sizebuf_t tempstringsbuf;
- // in csqc the polygonbegin,polygonvertex,polygonend sequencing is
- // stateful, so this tracks the last polygonbegin's choice of
+ // polygonbegin, polygonvertex, polygonend state
+ // the polygon is buffered here until polygonend commits it to the relevant
+ // CL_Mesh entity, because important decisions depend on the vertex data
+ // provided (e.g. whether the polygon is transparent), we can't really do much
+ // with it until we have all of the data...
+
+ // this tracks the last polygonbegin's choice of
// CL_Mesh_CSQC or CL_Mesh_UI for this polygon
dp_model_t *polygonbegin_model;
// indicates if polygonbegin should be interpreted as 2d
// where the behavior is always 3D unless DRAWFLAG_2D is passed, but
// DRAWFLAG_2D conflicts with our DRAWFLAG_SCREEN.
qboolean polygonbegin_guess2d;
+ // the texture name and drawflags provided to polygonbegin
+ char polygonbegin_texname[MAX_QPATH];
+ int polygonbegin_drawflags;
+ // the vertex data
+ int polygonbegin_numvertices;
+ int polygonbegin_maxvertices;
+ float *polygonbegin_vertexdata;
// copies of some vars that were former read from sv
int num_edicts;
static void CL_Beams_SetupExternalTexture(void)
{
- if (Mod_LoadTextureFromQ3Shader(r_main_mempool, "r_lightning.c", &cl_beams_externaltexture, "textures/particles/lightning", false, false, TEXF_ALPHA | TEXF_FORCELINEAR, MATERIALFLAG_WALL | MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOCULLFACE))
+ if (Mod_LoadTextureFromQ3Shader(r_main_mempool, "r_lightning.c", &cl_beams_externaltexture, "textures/particles/lightning", false, false, TEXF_ALPHA | TEXF_FORCELINEAR, MATERIALFLAG_WALL | MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOCULLFACE | MATERIALFLAG_NOSHADOW | MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX))
Cvar_SetValueQuick(&r_lightningbeam_qmbtexture, false);
}
}
skinframe = R_SkinFrame_LoadInternalBGRA("lightningbeam", TEXF_FORCELINEAR, data, texwidth, texheight, 0, 0, 0, false);
- Mod_LoadCustomMaterial(r_main_mempool, &cl_beams_builtintexture, "cl_beams_builtintexture", 0, MATERIALFLAG_WALL | MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOCULLFACE | MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX, skinframe);
+ Mod_LoadCustomMaterial(r_main_mempool, &cl_beams_builtintexture, "cl_beams_builtintexture", 0, MATERIALFLAG_WALL | MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOCULLFACE | MATERIALFLAG_NOSHADOW | MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX, skinframe);
Mem_Free(data);
}
// (and realize that the whole polygon assembly orients itself to face
// the viewer)
- mod = &cl_meshentitymodels[MESH_PARTICLES];
+ mod = CL_Mesh_Scene();
surf = Mod_Mesh_AddSurface(mod, r_lightningbeam_qmbtexture.integer ? &cl_beams_externaltexture : &cl_beams_builtintexture, false);
// polygon 1
VectorM(r_lightningbeam_thickness.value, right, offset);