+ R_ResetViewRendering2D();
+}
+
+static const int bboxelements[36] =
+{
+ 5, 1, 3, 5, 3, 7,
+ 6, 2, 0, 6, 0, 4,
+ 7, 3, 2, 7, 2, 6,
+ 4, 0, 1, 4, 1, 5,
+ 4, 5, 7, 4, 7, 6,
+ 1, 0, 2, 1, 2, 3,
+};
+
+void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, float ca)
+{
+ int i;
+ float *v, *c, f1, f2, vertex3f[8*3], color4f[8*4];
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ GL_DepthMask(false);
+ GL_DepthRange(0, 1);
+ GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
+ R_Mesh_Matrix(&identitymatrix);
+ R_Mesh_ResetTextureState();
+
+ vertex3f[ 0] = mins[0];vertex3f[ 1] = mins[1];vertex3f[ 2] = mins[2]; //
+ vertex3f[ 3] = maxs[0];vertex3f[ 4] = mins[1];vertex3f[ 5] = mins[2];
+ vertex3f[ 6] = mins[0];vertex3f[ 7] = maxs[1];vertex3f[ 8] = mins[2];
+ vertex3f[ 9] = maxs[0];vertex3f[10] = maxs[1];vertex3f[11] = mins[2];
+ vertex3f[12] = mins[0];vertex3f[13] = mins[1];vertex3f[14] = maxs[2];
+ vertex3f[15] = maxs[0];vertex3f[16] = mins[1];vertex3f[17] = maxs[2];
+ vertex3f[18] = mins[0];vertex3f[19] = maxs[1];vertex3f[20] = maxs[2];
+ vertex3f[21] = maxs[0];vertex3f[22] = maxs[1];vertex3f[23] = maxs[2];
+ R_FillColors(color4f, 8, cr, cg, cb, ca);
+ if (r_refdef.fogenabled)
+ {
+ for (i = 0, v = vertex3f, c = color4f;i < 8;i++, v += 3, c += 4)
+ {
+ f1 = FogPoint_World(v);
+ f2 = 1 - f1;
+ c[0] = c[0] * f1 + r_refdef.fogcolor[0] * f2;
+ c[1] = c[1] * f1 + r_refdef.fogcolor[1] * f2;
+ c[2] = c[2] * f1 + r_refdef.fogcolor[2] * f2;
+ }
+ }
+ R_Mesh_VertexPointer(vertex3f, 0, 0);
+ R_Mesh_ColorPointer(color4f, 0, 0);
+ R_Mesh_ResetTextureState();
+ R_Mesh_Draw(0, 8, 12, bboxelements, 0, 0);
+}
+
+static void R_DrawEntityBBoxes_Callback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
+{
+ int i;
+ float color[4];
+ prvm_edict_t *edict;
+ // this function draws bounding boxes of server entities
+ if (!sv.active)
+ return;
+ SV_VM_Begin();
+ for (i = 0;i < numsurfaces;i++)
+ {
+ edict = PRVM_EDICT_NUM(surfacelist[i]);
+ switch ((int)edict->fields.server->solid)
+ {
+ case SOLID_NOT: Vector4Set(color, 1, 1, 1, 0.05);break;
+ case SOLID_TRIGGER: Vector4Set(color, 1, 0, 1, 0.10);break;
+ case SOLID_BBOX: Vector4Set(color, 0, 1, 0, 0.10);break;
+ case SOLID_SLIDEBOX: Vector4Set(color, 1, 0, 0, 0.10);break;
+ case SOLID_BSP: Vector4Set(color, 0, 0, 1, 0.05);break;
+ default: Vector4Set(color, 0, 0, 0, 0.50);break;
+ }
+ color[3] *= r_showbboxes.value;
+ color[3] = bound(0, color[3], 1);
+ GL_DepthTest(!r_showdisabledepthtest.integer);
+ GL_CullFace(r_view.cullface_front);
+ R_DrawBBoxMesh(edict->priv.server->areamins, edict->priv.server->areamaxs, color[0], color[1], color[2], color[3]);
+ }
+ SV_VM_End();
+}
+
+static void R_DrawEntityBBoxes(void)
+{
+ int i;
+ prvm_edict_t *edict;
+ vec3_t center;
+ // this function draws bounding boxes of server entities
+ if (!sv.active)
+ return;
+ SV_VM_Begin();
+ for (i = 0;i < prog->num_edicts;i++)
+ {
+ edict = PRVM_EDICT_NUM(i);
+ if (edict->priv.server->free)
+ continue;
+ VectorLerp(edict->priv.server->areamins, 0.5f, edict->priv.server->areamaxs, center);
+ R_MeshQueue_AddTransparent(center, R_DrawEntityBBoxes_Callback, (entity_render_t *)NULL, i, (rtlight_t *)NULL);
+ }
+ SV_VM_End();
+}
+
+int nomodelelements[24] =
+{
+ 5, 2, 0,
+ 5, 1, 2,
+ 5, 0, 3,
+ 5, 3, 1,
+ 0, 2, 4,
+ 2, 1, 4,
+ 3, 0, 4,
+ 1, 3, 4
+};
+
+float nomodelvertex3f[6*3] =
+{
+ -16, 0, 0,
+ 16, 0, 0,
+ 0, -16, 0,
+ 0, 16, 0,
+ 0, 0, -16,
+ 0, 0, 16
+};
+
+float nomodelcolor4f[6*4] =
+{
+ 0.0f, 0.0f, 0.5f, 1.0f,
+ 0.0f, 0.0f, 0.5f, 1.0f,
+ 0.0f, 0.5f, 0.0f, 1.0f,
+ 0.0f, 0.5f, 0.0f, 1.0f,
+ 0.5f, 0.0f, 0.0f, 1.0f,
+ 0.5f, 0.0f, 0.0f, 1.0f
+};
+
+void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
+{
+ int i;
+ float f1, f2, *c;
+ float color4f[6*4];
+ // this is only called once per entity so numsurfaces is always 1, and
+ // surfacelist is always {0}, so this code does not handle batches
+ R_Mesh_Matrix(&ent->matrix);
+
+ if (ent->flags & EF_ADDITIVE)
+ {
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+ GL_DepthMask(false);
+ }
+ else if (ent->alpha < 1)
+ {
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ GL_DepthMask(false);
+ }
+ else
+ {
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ GL_DepthMask(true);
+ }
+ GL_DepthRange(0, (ent->flags & RENDER_VIEWMODEL) ? 0.0625 : 1);
+ GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
+ GL_DepthTest(!(ent->effects & EF_NODEPTHTEST));
+ GL_CullFace((ent->effects & EF_DOUBLESIDED) ? GL_NONE : r_view.cullface_back);
+ R_Mesh_VertexPointer(nomodelvertex3f, 0, 0);
+ if (r_refdef.fogenabled)
+ {
+ vec3_t org;
+ memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
+ R_Mesh_ColorPointer(color4f, 0, 0);
+ Matrix4x4_OriginFromMatrix(&ent->matrix, org);
+ f1 = FogPoint_World(org);
+ f2 = 1 - f1;
+ for (i = 0, c = color4f;i < 6;i++, c += 4)
+ {
+ c[0] = (c[0] * f1 + r_refdef.fogcolor[0] * f2);
+ c[1] = (c[1] * f1 + r_refdef.fogcolor[1] * f2);
+ c[2] = (c[2] * f1 + r_refdef.fogcolor[2] * f2);
+ c[3] *= ent->alpha;
+ }
+ }
+ else if (ent->alpha != 1)
+ {
+ memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
+ R_Mesh_ColorPointer(color4f, 0, 0);
+ for (i = 0, c = color4f;i < 6;i++, c += 4)
+ c[3] *= ent->alpha;
+ }
+ else
+ R_Mesh_ColorPointer(nomodelcolor4f, 0, 0);
+ R_Mesh_ResetTextureState();
+ R_Mesh_Draw(0, 6, 8, nomodelelements, 0, 0);
+}
+
+void R_DrawNoModel(entity_render_t *ent)
+{
+ vec3_t org;
+ Matrix4x4_OriginFromMatrix(&ent->matrix, org);
+ //if ((ent->effects & EF_ADDITIVE) || (ent->alpha < 1))
+ R_MeshQueue_AddTransparent(ent->effects & EF_NODEPTHTEST ? r_view.origin : org, R_DrawNoModel_TransparentCallback, ent, 0, rsurface.rtlight);
+ //else
+ // R_DrawNoModelCallback(ent, 0);
+}
+
+void R_CalcBeam_Vertex3f (float *vert, const vec3_t org1, const vec3_t org2, float width)
+{
+ vec3_t right1, right2, diff, normal;
+
+ VectorSubtract (org2, org1, normal);
+
+ // calculate 'right' vector for start
+ VectorSubtract (r_view.origin, org1, diff);
+ CrossProduct (normal, diff, right1);
+ VectorNormalize (right1);
+
+ // calculate 'right' vector for end
+ VectorSubtract (r_view.origin, org2, diff);
+ CrossProduct (normal, diff, right2);
+ VectorNormalize (right2);
+
+ vert[ 0] = org1[0] + width * right1[0];
+ vert[ 1] = org1[1] + width * right1[1];
+ vert[ 2] = org1[2] + width * right1[2];
+ vert[ 3] = org1[0] - width * right1[0];
+ vert[ 4] = org1[1] - width * right1[1];
+ vert[ 5] = org1[2] - width * right1[2];
+ vert[ 6] = org2[0] - width * right2[0];
+ vert[ 7] = org2[1] - width * right2[1];
+ vert[ 8] = org2[2] - width * right2[2];
+ vert[ 9] = org2[0] + width * right2[0];
+ vert[10] = org2[1] + width * right2[1];
+ vert[11] = org2[2] + width * right2[2];
+}
+
+float spritetexcoord2f[4*2] = {0, 1, 0, 0, 1, 0, 1, 1};
+
+void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_t *fogtexture, qboolean depthdisable, qboolean depthshort, const vec3_t origin, const vec3_t left, const vec3_t up, float scalex1, float scalex2, float scaley1, float scaley2, float cr, float cg, float cb, float ca)
+{
+ float fog = 1.0f;
+ float vertex3f[12];
+
+ if (r_refdef.fogenabled)
+ fog = FogPoint_World(origin);
+
+ R_Mesh_Matrix(&identitymatrix);
+ GL_BlendFunc(blendfunc1, blendfunc2);
+
+ if(v_flipped_state)
+ {
+ scalex1 = -scalex1;
+ scalex2 = -scalex2;
+ GL_CullFace(r_view.cullface_front);
+ }
+ else
+ GL_CullFace(r_view.cullface_back);
+
+ GL_DepthMask(false);
+ GL_DepthRange(0, depthshort ? 0.0625 : 1);
+ GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
+ GL_DepthTest(!depthdisable);
+
+ vertex3f[ 0] = origin[0] + left[0] * scalex2 + up[0] * scaley1;
+ vertex3f[ 1] = origin[1] + left[1] * scalex2 + up[1] * scaley1;
+ vertex3f[ 2] = origin[2] + left[2] * scalex2 + up[2] * scaley1;
+ vertex3f[ 3] = origin[0] + left[0] * scalex2 + up[0] * scaley2;
+ vertex3f[ 4] = origin[1] + left[1] * scalex2 + up[1] * scaley2;
+ vertex3f[ 5] = origin[2] + left[2] * scalex2 + up[2] * scaley2;
+ vertex3f[ 6] = origin[0] + left[0] * scalex1 + up[0] * scaley2;
+ vertex3f[ 7] = origin[1] + left[1] * scalex1 + up[1] * scaley2;
+ vertex3f[ 8] = origin[2] + left[2] * scalex1 + up[2] * scaley2;
+ vertex3f[ 9] = origin[0] + left[0] * scalex1 + up[0] * scaley1;
+ vertex3f[10] = origin[1] + left[1] * scalex1 + up[1] * scaley1;
+ vertex3f[11] = origin[2] + left[2] * scalex1 + up[2] * scaley1;
+
+ R_Mesh_VertexPointer(vertex3f, 0, 0);
+ R_Mesh_ColorPointer(NULL, 0, 0);
+ R_Mesh_ResetTextureState();
+ R_Mesh_TexBind(0, R_GetTexture(texture));
+ R_Mesh_TexCoordPointer(0, 2, spritetexcoord2f, 0, 0);
+ // FIXME: fixed function path can't properly handle r_view.colorscale > 1
+ GL_Color(cr * fog * r_view.colorscale, cg * fog * r_view.colorscale, cb * fog * r_view.colorscale, ca);
+ R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+
+ if (blendfunc2 == GL_ONE_MINUS_SRC_ALPHA)
+ {
+ R_Mesh_TexBind(0, R_GetTexture(fogtexture));
+ GL_BlendFunc(blendfunc1, GL_ONE);
+ fog = 1 - fog;
+ GL_Color(r_refdef.fogcolor[0] * fog * r_view.colorscale, r_refdef.fogcolor[1] * fog * r_view.colorscale, r_refdef.fogcolor[2] * fog * r_view.colorscale, ca);
+ R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
+ }
+}
+
+int R_Mesh_AddVertex(rmesh_t *mesh, float x, float y, float z)
+{
+ int i;
+ float *vertex3f;
+ float v[3];
+ VectorSet(v, x, y, z);
+ for (i = 0, vertex3f = mesh->vertex3f;i < mesh->numvertices;i++, vertex3f += 3)
+ if (VectorDistance2(v, vertex3f) < mesh->epsilon2)
+ break;
+ if (i == mesh->numvertices)
+ {
+ if (mesh->numvertices < mesh->maxvertices)
+ {
+ VectorCopy(v, vertex3f);
+ mesh->numvertices++;
+ }
+ return mesh->numvertices;
+ }
+ else
+ return i;
+}
+
+void R_Mesh_AddPolygon3f(rmesh_t *mesh, int numvertices, float *vertex3f)
+{
+ int i;
+ int *e, element[3];
+ element[0] = R_Mesh_AddVertex(mesh, vertex3f[0], vertex3f[1], vertex3f[2]);vertex3f += 3;
+ element[1] = R_Mesh_AddVertex(mesh, vertex3f[0], vertex3f[1], vertex3f[2]);vertex3f += 3;
+ e = mesh->element3i + mesh->numtriangles * 3;
+ for (i = 0;i < numvertices - 2;i++, vertex3f += 3)
+ {
+ element[2] = R_Mesh_AddVertex(mesh, vertex3f[0], vertex3f[1], vertex3f[2]);
+ if (mesh->numtriangles < mesh->maxtriangles)
+ {
+ *e++ = element[0];
+ *e++ = element[1];
+ *e++ = element[2];
+ mesh->numtriangles++;
+ }
+ element[1] = element[2];
+ }
+}
+
+void R_Mesh_AddPolygon3d(rmesh_t *mesh, int numvertices, double *vertex3d)
+{
+ int i;
+ int *e, element[3];
+ element[0] = R_Mesh_AddVertex(mesh, vertex3d[0], vertex3d[1], vertex3d[2]);vertex3d += 3;
+ element[1] = R_Mesh_AddVertex(mesh, vertex3d[0], vertex3d[1], vertex3d[2]);vertex3d += 3;
+ e = mesh->element3i + mesh->numtriangles * 3;
+ for (i = 0;i < numvertices - 2;i++, vertex3d += 3)
+ {
+ element[2] = R_Mesh_AddVertex(mesh, vertex3d[0], vertex3d[1], vertex3d[2]);
+ if (mesh->numtriangles < mesh->maxtriangles)
+ {
+ *e++ = element[0];
+ *e++ = element[1];
+ *e++ = element[2];
+ mesh->numtriangles++;
+ }
+ element[1] = element[2];
+ }
+}
+
+#define R_MESH_PLANE_DIST_EPSILON (1.0 / 32.0)
+void R_Mesh_AddBrushMeshFromPlanes(rmesh_t *mesh, int numplanes, mplane_t *planes)
+{
+ int planenum, planenum2;
+ int w;
+ int tempnumpoints;
+ mplane_t *plane, *plane2;
+ double maxdist;
+ double temppoints[2][256*3];
+ // figure out how large a bounding box we need to properly compute this brush
+ maxdist = 0;
+ for (w = 0;w < numplanes;w++)
+ maxdist = max(maxdist, planes[w].dist);
+ // now make it large enough to enclose the entire brush, and round it off to a reasonable multiple of 1024
+ maxdist = floor(maxdist * (4.0 / 1024.0) + 1) * 1024.0;
+ for (planenum = 0, plane = planes;planenum < numplanes;planenum++, plane++)
+ {
+ w = 0;
+ tempnumpoints = 4;
+ PolygonD_QuadForPlane(temppoints[w], plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, maxdist);
+ for (planenum2 = 0, plane2 = planes;planenum2 < numplanes && tempnumpoints >= 3;planenum2++, plane2++)
+ {
+ if (planenum2 == planenum)
+ continue;
+ PolygonD_Divide(tempnumpoints, temppoints[w], plane2->normal[0], plane2->normal[1], plane2->normal[2], plane2->dist, R_MESH_PLANE_DIST_EPSILON, 0, NULL, NULL, 256, temppoints[!w], &tempnumpoints, NULL);
+ w = !w;
+ }
+ if (tempnumpoints < 3)
+ continue;
+ // generate elements forming a triangle fan for this polygon
+ R_Mesh_AddPolygon3d(mesh, tempnumpoints, temppoints[w]);
+ }
+}
+
+static void R_Texture_AddLayer(texture_t *t, qboolean depthmask, int blendfunc1, int blendfunc2, texturelayertype_t type, rtexture_t *texture, const matrix4x4_t *matrix, float r, float g, float b, float a)
+{
+ texturelayer_t *layer;
+ layer = t->currentlayers + t->currentnumlayers++;
+ layer->type = type;
+ layer->depthmask = depthmask;
+ layer->blendfunc1 = blendfunc1;
+ layer->blendfunc2 = blendfunc2;
+ layer->texture = texture;
+ layer->texmatrix = *matrix;
+ layer->color[0] = r * r_view.colorscale;
+ layer->color[1] = g * r_view.colorscale;
+ layer->color[2] = b * r_view.colorscale;
+ layer->color[3] = a;
+}
+
+static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms)
+{
+ double index, f;
+ index = parms[2] + r_refdef.time * parms[3];
+ index -= floor(index);
+ switch (func)
+ {
+ default:
+ case Q3WAVEFUNC_NONE:
+ case Q3WAVEFUNC_NOISE:
+ case Q3WAVEFUNC_COUNT:
+ f = 0;
+ break;
+ case Q3WAVEFUNC_SIN: f = sin(index * M_PI * 2);break;
+ case Q3WAVEFUNC_SQUARE: f = index < 0.5 ? 1 : -1;break;
+ case Q3WAVEFUNC_SAWTOOTH: f = index;break;
+ case Q3WAVEFUNC_INVERSESAWTOOTH: f = 1 - index;break;
+ case Q3WAVEFUNC_TRIANGLE:
+ index *= 4;
+ f = index - floor(index);
+ if (index < 1)
+ f = f;
+ else if (index < 2)
+ f = 1 - f;
+ else if (index < 3)
+ f = -f;
+ else
+ f = -(1 - f);
+ break;
+ }
+ return (float)(parms[0] + parms[1] * f);
+}
+
+void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
+{
+ int i;
+ model_t *model = ent->model;
+ float f;
+ float tcmat[12];
+ q3shaderinfo_layer_tcmod_t *tcmod;
+
+ // switch to an alternate material if this is a q1bsp animated material
+ {
+ texture_t *texture = t;
+ int s = ent->skinnum;
+ if ((unsigned int)s >= (unsigned int)model->numskins)
+ s = 0;
+ if (model->skinscenes)
+ {
+ if (model->skinscenes[s].framecount > 1)
+ s = model->skinscenes[s].firstframe + (unsigned int) (r_refdef.time * model->skinscenes[s].framerate) % model->skinscenes[s].framecount;
+ else
+ s = model->skinscenes[s].firstframe;
+ }
+ if (s > 0)
+ t = t + s * model->num_surfaces;
+ if (t->animated)
+ {
+ // use an alternate animation if the entity's frame is not 0,
+ // and only if the texture has an alternate animation
+ if (ent->frame2 != 0 && t->anim_total[1])
+ t = t->anim_frames[1][(t->anim_total[1] >= 2) ? ((int)(r_refdef.time * 5.0f) % t->anim_total[1]) : 0];
+ else
+ t = t->anim_frames[0][(t->anim_total[0] >= 2) ? ((int)(r_refdef.time * 5.0f) % t->anim_total[0]) : 0];
+ }
+ texture->currentframe = t;
+ }
+
+ // update currentskinframe to be a qw skin or animation frame
+ if ((i = ent->entitynumber - 1) >= 0 && i < cl.maxclients)
+ {
+ if (strcmp(r_qwskincache[i], cl.scores[i].qw_skin))
+ {
+ strlcpy(r_qwskincache[i], cl.scores[i].qw_skin, sizeof(r_qwskincache[i]));
+ Con_DPrintf("loading skins/%s\n", r_qwskincache[i]);
+ r_qwskincache_skinframe[i] = R_SkinFrame_LoadExternal(va("skins/%s", r_qwskincache[i]), TEXF_PRECACHE | (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_PICMIP | TEXF_COMPRESS, developer.integer > 0);
+ }
+ t->currentskinframe = r_qwskincache_skinframe[i];
+ if (t->currentskinframe == NULL)
+ t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - ent->frame2time)) % t->numskinframes];
+ }
+ else if (t->numskinframes >= 2)
+ t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - ent->frame2time)) % t->numskinframes];
+ if (t->backgroundnumskinframes >= 2)
+ t->backgroundcurrentskinframe = t->backgroundskinframes[(int)(t->backgroundskinframerate * (cl.time - ent->frame2time)) % t->backgroundnumskinframes];
+
+ t->currentmaterialflags = t->basematerialflags;
+ t->currentalpha = ent->alpha;
+ if (t->basematerialflags & MATERIALFLAG_WATERALPHA && (model->brush.supportwateralpha || r_novis.integer))
+ {
+ t->currentalpha *= r_wateralpha.value;
+ /*
+ * FIXME what is this supposed to do?
+ // if rendering refraction/reflection, disable transparency
+ if (r_waterstate.enabled && (t->currentalpha < 1 || (t->currentmaterialflags & MATERIALFLAG_ALPHA)))
+ t->currentmaterialflags |= MATERIALFLAG_WATERSHADER;
+ */
+ }
+ if(!r_waterstate.enabled)
+ t->currentmaterialflags &= ~(MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION);
+ if (!(ent->flags & RENDER_LIGHT))
+ t->currentmaterialflags |= MATERIALFLAG_FULLBRIGHT;
+ else if (rsurface.modeltexcoordlightmap2f == NULL)
+ {
+ // pick a model lighting mode
+ if (VectorLength2(ent->modellight_diffuse) >= (1.0f / 256.0f))
+ t->currentmaterialflags |= MATERIALFLAG_MODELLIGHT | MATERIALFLAG_MODELLIGHT_DIRECTIONAL;
+ else
+ t->currentmaterialflags |= MATERIALFLAG_MODELLIGHT;
+ }
+ if (ent->effects & EF_ADDITIVE)
+ t->currentmaterialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
+ else if (t->currentalpha < 1)
+ t->currentmaterialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
+ if (ent->effects & EF_DOUBLESIDED)
+ t->currentmaterialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
+ if (ent->effects & EF_NODEPTHTEST)
+ t->currentmaterialflags |= MATERIALFLAG_SHORTDEPTHRANGE;
+ if (ent->flags & RENDER_VIEWMODEL)
+ t->currentmaterialflags |= MATERIALFLAG_SHORTDEPTHRANGE;
+ if (t->backgroundnumskinframes && !(t->currentmaterialflags & MATERIALFLAGMASK_DEPTHSORTED))
+ t->currentmaterialflags |= MATERIALFLAG_VERTEXTEXTUREBLEND;
+
+ for (i = 0, tcmod = t->tcmods;i < Q3MAXTCMODS && (tcmod->tcmod || i < 1);i++, tcmod++)
+ {
+ matrix4x4_t matrix;
+ switch(tcmod->tcmod)
+ {
+ case Q3TCMOD_COUNT:
+ case Q3TCMOD_NONE:
+ if (t->currentmaterialflags & MATERIALFLAG_WATER && r_waterscroll.value != 0)
+ matrix = r_waterscrollmatrix;
+ else
+ matrix = identitymatrix;
+ break;
+ case Q3TCMOD_ENTITYTRANSLATE:
+ // this is used in Q3 to allow the gamecode to control texcoord
+ // scrolling on the entity, which is not supported in darkplaces yet.
+ Matrix4x4_CreateTranslate(&matrix, 0, 0, 0);
+ break;
+ case Q3TCMOD_ROTATE:
+ Matrix4x4_CreateTranslate(&matrix, 0.5, 0.5, 0);
+ Matrix4x4_ConcatRotate(&matrix, tcmod->parms[0] * r_refdef.time, 0, 0, 1);
+ Matrix4x4_ConcatTranslate(&matrix, -0.5, -0.5, 0);
+ break;
+ case Q3TCMOD_SCALE:
+ Matrix4x4_CreateScale3(&matrix, tcmod->parms[0], tcmod->parms[1], 1);
+ break;
+ case Q3TCMOD_SCROLL:
+ Matrix4x4_CreateTranslate(&matrix, tcmod->parms[0] * r_refdef.time, tcmod->parms[1] * r_refdef.time, 0);
+ break;
+ case Q3TCMOD_STRETCH:
+ f = 1.0f / R_EvaluateQ3WaveFunc(tcmod->wavefunc, tcmod->waveparms);
+ Matrix4x4_CreateFromQuakeEntity(&matrix, 0.5f * (1 - f), 0.5 * (1 - f), 0, 0, 0, 0, f);
+ break;
+ case Q3TCMOD_TRANSFORM:
+ VectorSet(tcmat + 0, tcmod->parms[0], tcmod->parms[1], 0);
+ VectorSet(tcmat + 3, tcmod->parms[2], tcmod->parms[3], 0);
+ VectorSet(tcmat + 6, 0 , 0 , 1);
+ VectorSet(tcmat + 9, tcmod->parms[4], tcmod->parms[5], 0);
+ Matrix4x4_FromArray12FloatGL(&matrix, tcmat);
+ break;
+ case Q3TCMOD_TURBULENT:
+ // this is handled in the RSurf_PrepareVertices function
+ matrix = identitymatrix;
+ break;
+ }
+ // either replace or concatenate the transformation
+ if (i < 1)
+ t->currenttexmatrix = matrix;
+ else
+ {
+ matrix4x4_t temp = t->currenttexmatrix;
+ Matrix4x4_Concat(&t->currenttexmatrix, &matrix, &temp);
+ }
+ }
+
+ t->colormapping = VectorLength2(ent->colormap_pantscolor) + VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f);
+ t->basetexture = (!t->colormapping && t->currentskinframe->merged) ? t->currentskinframe->merged : t->currentskinframe->base;
+ t->glosstexture = r_texture_black;
+ t->backgroundbasetexture = t->backgroundnumskinframes ? ((!t->colormapping && t->backgroundcurrentskinframe->merged) ? t->backgroundcurrentskinframe->merged : t->backgroundcurrentskinframe->base) : r_texture_white;
+ t->backgroundglosstexture = r_texture_black;
+ t->specularpower = r_shadow_glossexponent.value;
+ // TODO: store reference values for these in the texture?
+ t->specularscale = 0;
+ if (r_shadow_gloss.integer > 0)
+ {
+ if (t->currentskinframe->gloss || (t->backgroundcurrentskinframe && t->backgroundcurrentskinframe->gloss))
+ {
+ if (r_shadow_glossintensity.value > 0)
+ {
+ t->glosstexture = t->currentskinframe->gloss ? t->currentskinframe->gloss : r_texture_white;
+ t->backgroundglosstexture = (t->backgroundcurrentskinframe && t->backgroundcurrentskinframe->gloss) ? t->backgroundcurrentskinframe->gloss : r_texture_white;
+ t->specularscale = r_shadow_glossintensity.value;
+ }
+ }
+ else if (r_shadow_gloss.integer >= 2 && r_shadow_gloss2intensity.value > 0)
+ {
+ t->glosstexture = r_texture_white;
+ t->backgroundglosstexture = r_texture_white;
+ t->specularscale = r_shadow_gloss2intensity.value;
+ }
+ }
+
+ // lightmaps mode looks bad with dlights using actual texturing, so turn
+ // off the colormap and glossmap, but leave the normalmap on as it still
+ // accurately represents the shading involved
+ if (gl_lightmaps.integer && !(t->currentmaterialflags & MATERIALFLAG_BLENDED))
+ {
+ t->basetexture = r_texture_white;
+ t->specularscale = 0;
+ }
+
+ t->currentpolygonfactor = r_refdef.polygonfactor + t->basepolygonfactor;
+ t->currentpolygonoffset = r_refdef.polygonoffset + t->basepolygonoffset;
+ // submodels are biased to avoid z-fighting with world surfaces that they
+ // may be exactly overlapping (avoids z-fighting artifacts on certain
+ // doors and things in Quake maps)
+ if (ent->model->brush.submodel)
+ {
+ t->currentpolygonfactor += r_polygonoffset_submodel_factor.value;
+ t->currentpolygonoffset += r_polygonoffset_submodel_offset.value;
+ }
+
+ VectorClear(t->dlightcolor);
+ t->currentnumlayers = 0;
+ if (!(t->currentmaterialflags & MATERIALFLAG_NODRAW))
+ {
+ if (!(t->currentmaterialflags & MATERIALFLAG_SKY))
+ {
+ int blendfunc1, blendfunc2, depthmask;
+ if (t->currentmaterialflags & MATERIALFLAG_ADD)
+ {
+ blendfunc1 = GL_SRC_ALPHA;
+ blendfunc2 = GL_ONE;
+ }
+ else if (t->currentmaterialflags & MATERIALFLAG_ALPHA)
+ {
+ blendfunc1 = GL_SRC_ALPHA;
+ blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
+ }
+ else if (t->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND)
+ {
+ blendfunc1 = t->customblendfunc[0];
+ blendfunc2 = t->customblendfunc[1];
+ }
+ else
+ {
+ blendfunc1 = GL_ONE;
+ blendfunc2 = GL_ZERO;
+ }
+ depthmask = !(t->currentmaterialflags & MATERIALFLAG_BLENDED);
+ if (t->currentmaterialflags & (MATERIALFLAG_WATER | MATERIALFLAG_WALL))
+ {
+ rtexture_t *currentbasetexture;
+ int layerflags = 0;
+ if (r_refdef.fogenabled && (t->currentmaterialflags & MATERIALFLAG_BLENDED))
+ layerflags |= TEXTURELAYERFLAG_FOGDARKEN;
+ currentbasetexture = (VectorLength2(ent->colormap_pantscolor) + VectorLength2(ent->colormap_shirtcolor) < (1.0f / 1048576.0f) && t->currentskinframe->merged) ? t->currentskinframe->merged : t->currentskinframe->base;
+ if (t->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
+ {
+ // fullbright is not affected by r_refdef.lightmapintensity
+ R_Texture_AddLayer(t, depthmask, blendfunc1, blendfunc2, TEXTURELAYERTYPE_TEXTURE, currentbasetexture, &t->currenttexmatrix, ent->colormod[0], ent->colormod[1], ent->colormod[2], t->currentalpha);
+ if (VectorLength2(ent->colormap_pantscolor) >= (1.0f / 1048576.0f) && t->currentskinframe->pants)
+ R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->pants, &t->currenttexmatrix, ent->colormap_pantscolor[0] * ent->colormod[0], ent->colormap_pantscolor[1] * ent->colormod[1], ent->colormap_pantscolor[2] * ent->colormod[2], t->currentalpha);
+ if (VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f) && t->currentskinframe->shirt)
+ R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->shirt, &t->currenttexmatrix, ent->colormap_shirtcolor[0] * ent->colormod[0], ent->colormap_shirtcolor[1] * ent->colormod[1], ent->colormap_shirtcolor[2] * ent->colormod[2], t->currentalpha);
+ }
+ else
+ {
+ float colorscale;
+ // set the color tint used for lights affecting this surface
+ VectorSet(t->dlightcolor, ent->colormod[0] * t->currentalpha, ent->colormod[1] * t->currentalpha, ent->colormod[2] * t->currentalpha);
+ colorscale = 2;
+ // q3bsp has no lightmap updates, so the lightstylevalue that
+ // would normally be baked into the lightmap must be
+ // applied to the color
+ // FIXME: r_glsl 1 rendering doesn't support overbright lightstyles with this (the default light style is not overbright)
+ if (ent->model->type == mod_brushq3)
+ colorscale *= r_refdef.lightstylevalue[0] * (1.0f / 256.0f);
+ colorscale *= r_refdef.lightmapintensity;
+ R_Texture_AddLayer(t, depthmask, blendfunc1, blendfunc2, TEXTURELAYERTYPE_LITTEXTURE, currentbasetexture, &t->currenttexmatrix, ent->colormod[0] * colorscale, ent->colormod[1] * colorscale, ent->colormod[2] * colorscale, t->currentalpha);
+ if (r_ambient.value >= (1.0f/64.0f))
+ R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, currentbasetexture, &t->currenttexmatrix, ent->colormod[0] * r_ambient.value * (1.0f / 64.0f), ent->colormod[1] * r_ambient.value * (1.0f / 64.0f), ent->colormod[2] * r_ambient.value * (1.0f / 64.0f), t->currentalpha);
+ if (VectorLength2(ent->colormap_pantscolor) >= (1.0f / 1048576.0f) && t->currentskinframe->pants)
+ {
+ R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_LITTEXTURE, t->currentskinframe->pants, &t->currenttexmatrix, ent->colormap_pantscolor[0] * ent->colormod[0] * colorscale, ent->colormap_pantscolor[1] * ent->colormod[1] * colorscale, ent->colormap_pantscolor[2] * ent->colormod[2] * colorscale, t->currentalpha);
+ if (r_ambient.value >= (1.0f/64.0f))
+ R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->pants, &t->currenttexmatrix, ent->colormap_pantscolor[0] * ent->colormod[0] * r_ambient.value * (1.0f / 64.0f), ent->colormap_pantscolor[1] * ent->colormod[1] * r_ambient.value * (1.0f / 64.0f), ent->colormap_pantscolor[2] * ent->colormod[2] * r_ambient.value * (1.0f / 64.0f), t->currentalpha);
+ }
+ if (VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f) && t->currentskinframe->shirt)
+ {
+ R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_LITTEXTURE, t->currentskinframe->shirt, &t->currenttexmatrix, ent->colormap_shirtcolor[0] * ent->colormod[0] * colorscale, ent->colormap_shirtcolor[1] * ent->colormod[1] * colorscale, ent->colormap_shirtcolor[2] * ent->colormod[2] * colorscale, t->currentalpha);
+ if (r_ambient.value >= (1.0f/64.0f))
+ R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->shirt, &t->currenttexmatrix, ent->colormap_shirtcolor[0] * ent->colormod[0] * r_ambient.value * (1.0f / 64.0f), ent->colormap_shirtcolor[1] * ent->colormod[1] * r_ambient.value * (1.0f / 64.0f), ent->colormap_shirtcolor[2] * ent->colormod[2] * r_ambient.value * (1.0f / 64.0f), t->currentalpha);
+ }
+ }
+ if (t->currentskinframe->glow != NULL)
+ R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->currentskinframe->glow, &t->currenttexmatrix, r_hdr_glowintensity.value, r_hdr_glowintensity.value, r_hdr_glowintensity.value, t->currentalpha);
+ if (r_refdef.fogenabled && !(t->currentmaterialflags & MATERIALFLAG_ADD))
+ {
+ // if this is opaque use alpha blend which will darken the earlier
+ // passes cheaply.
+ //
+ // if this is an alpha blended material, all the earlier passes
+ // were darkened by fog already, so we only need to add the fog
+ // color ontop through the fog mask texture
+ //
+ // if this is an additive blended material, all the earlier passes
+ // were darkened by fog already, and we should not add fog color
+ // (because the background was not darkened, there is no fog color
+ // that was lost behind it).
+ R_Texture_AddLayer(t, false, GL_SRC_ALPHA, (t->currentmaterialflags & MATERIALFLAG_BLENDED) ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA, TEXTURELAYERTYPE_FOG, t->currentskinframe->fog, &identitymatrix, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2], t->currentalpha);
+ }
+ }
+ }
+ }
+}
+
+void R_UpdateAllTextureInfo(entity_render_t *ent)
+{
+ int i;
+ if (ent->model)
+ for (i = 0;i < ent->model->num_texturesperskin;i++)
+ R_UpdateTextureInfo(ent, ent->model->data_textures + i);
+}
+
+rsurfacestate_t rsurface;
+
+void R_Mesh_ResizeArrays(int newvertices)
+{
+ float *base;
+ if (rsurface.array_size >= newvertices)
+ return;
+ if (rsurface.array_modelvertex3f)
+ Mem_Free(rsurface.array_modelvertex3f);
+ rsurface.array_size = (newvertices + 1023) & ~1023;
+ base = (float *)Mem_Alloc(r_main_mempool, rsurface.array_size * sizeof(float[33]));
+ rsurface.array_modelvertex3f = base + rsurface.array_size * 0;
+ rsurface.array_modelsvector3f = base + rsurface.array_size * 3;
+ rsurface.array_modeltvector3f = base + rsurface.array_size * 6;
+ rsurface.array_modelnormal3f = base + rsurface.array_size * 9;
+ rsurface.array_deformedvertex3f = base + rsurface.array_size * 12;
+ rsurface.array_deformedsvector3f = base + rsurface.array_size * 15;
+ rsurface.array_deformedtvector3f = base + rsurface.array_size * 18;
+ rsurface.array_deformednormal3f = base + rsurface.array_size * 21;
+ rsurface.array_texcoord3f = base + rsurface.array_size * 24;
+ rsurface.array_color4f = base + rsurface.array_size * 27;
+ rsurface.array_generatedtexcoordtexture2f = base + rsurface.array_size * 31;
+}
+
+void RSurf_CleanUp(void)
+{
+ CHECKGLERROR
+ if (rsurface.mode == RSURFMODE_GLSL)
+ {
+ qglUseProgramObjectARB(0);CHECKGLERROR
+ }
+ GL_AlphaTest(false);
+ rsurface.mode = RSURFMODE_NONE;
+ rsurface.uselightmaptexture = false;
+ rsurface.texture = NULL;
+}
+
+void RSurf_ActiveWorldEntity(void)
+{
+ model_t *model = r_refdef.worldmodel;
+ RSurf_CleanUp();
+ if (rsurface.array_size < model->surfmesh.num_vertices)
+ R_Mesh_ResizeArrays(model->surfmesh.num_vertices);
+ rsurface.matrix = identitymatrix;
+ rsurface.inversematrix = identitymatrix;
+ R_Mesh_Matrix(&identitymatrix);
+ VectorCopy(r_view.origin, rsurface.modelorg);
+ VectorSet(rsurface.modellight_ambient, 0, 0, 0);
+ VectorSet(rsurface.modellight_diffuse, 0, 0, 0);
+ VectorSet(rsurface.modellight_lightdir, 0, 0, 1);
+ VectorSet(rsurface.colormap_pantscolor, 0, 0, 0);
+ VectorSet(rsurface.colormap_shirtcolor, 0, 0, 0);
+ rsurface.frameblend[0].frame = 0;
+ rsurface.frameblend[0].lerp = 1;
+ rsurface.frameblend[1].frame = 0;
+ rsurface.frameblend[1].lerp = 0;
+ rsurface.frameblend[2].frame = 0;
+ rsurface.frameblend[2].lerp = 0;
+ rsurface.frameblend[3].frame = 0;
+ rsurface.frameblend[3].lerp = 0;
+ rsurface.modelvertex3f = model->surfmesh.data_vertex3f;
+ rsurface.modelvertex3f_bufferobject = model->surfmesh.vbo;
+ rsurface.modelvertex3f_bufferoffset = model->surfmesh.vbooffset_vertex3f;
+ rsurface.modelsvector3f = model->surfmesh.data_svector3f;
+ rsurface.modelsvector3f_bufferobject = model->surfmesh.vbo;
+ rsurface.modelsvector3f_bufferoffset = model->surfmesh.vbooffset_svector3f;
+ rsurface.modeltvector3f = model->surfmesh.data_tvector3f;
+ rsurface.modeltvector3f_bufferobject = model->surfmesh.vbo;
+ rsurface.modeltvector3f_bufferoffset = model->surfmesh.vbooffset_tvector3f;
+ rsurface.modelnormal3f = model->surfmesh.data_normal3f;
+ rsurface.modelnormal3f_bufferobject = model->surfmesh.vbo;
+ rsurface.modelnormal3f_bufferoffset = model->surfmesh.vbooffset_normal3f;
+ rsurface.modellightmapcolor4f = model->surfmesh.data_lightmapcolor4f;
+ rsurface.modellightmapcolor4f_bufferobject = model->surfmesh.vbo;
+ rsurface.modellightmapcolor4f_bufferoffset = model->surfmesh.vbooffset_lightmapcolor4f;
+ rsurface.modeltexcoordtexture2f = model->surfmesh.data_texcoordtexture2f;
+ rsurface.modeltexcoordtexture2f_bufferobject = model->surfmesh.vbo;
+ rsurface.modeltexcoordtexture2f_bufferoffset = model->surfmesh.vbooffset_texcoordtexture2f;
+ rsurface.modeltexcoordlightmap2f = model->surfmesh.data_texcoordlightmap2f;
+ rsurface.modeltexcoordlightmap2f_bufferobject = model->surfmesh.vbo;
+ rsurface.modeltexcoordlightmap2f_bufferoffset = model->surfmesh.vbooffset_texcoordlightmap2f;
+ rsurface.modelelement3i = model->surfmesh.data_element3i;
+ rsurface.modelelement3i_bufferobject = model->surfmesh.ebo;
+ rsurface.modellightmapoffsets = model->surfmesh.data_lightmapoffsets;
+ rsurface.modelnum_vertices = model->surfmesh.num_vertices;
+ rsurface.modelnum_triangles = model->surfmesh.num_triangles;
+ rsurface.modelsurfaces = model->data_surfaces;
+ rsurface.generatedvertex = false;
+ rsurface.vertex3f = rsurface.modelvertex3f;
+ rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
+ rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
+ rsurface.svector3f = rsurface.modelsvector3f;
+ rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
+ rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
+ rsurface.tvector3f = rsurface.modeltvector3f;
+ rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
+ rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
+ rsurface.normal3f = rsurface.modelnormal3f;
+ rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
+ rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
+ rsurface.texcoordtexture2f = rsurface.modeltexcoordtexture2f;
+}
+
+void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, qboolean wanttangents)
+{
+ model_t *model = ent->model;
+ RSurf_CleanUp();
+ if (rsurface.array_size < model->surfmesh.num_vertices)
+ R_Mesh_ResizeArrays(model->surfmesh.num_vertices);
+ rsurface.matrix = ent->matrix;
+ rsurface.inversematrix = ent->inversematrix;
+ R_Mesh_Matrix(&rsurface.matrix);
+ Matrix4x4_Transform(&rsurface.inversematrix, r_view.origin, rsurface.modelorg);
+ VectorCopy(ent->modellight_ambient, rsurface.modellight_ambient);
+ VectorCopy(ent->modellight_diffuse, rsurface.modellight_diffuse);
+ VectorCopy(ent->modellight_lightdir, rsurface.modellight_lightdir);
+ VectorCopy(ent->colormap_pantscolor, rsurface.colormap_pantscolor);
+ VectorCopy(ent->colormap_shirtcolor, rsurface.colormap_shirtcolor);
+ rsurface.frameblend[0] = ent->frameblend[0];
+ rsurface.frameblend[1] = ent->frameblend[1];
+ rsurface.frameblend[2] = ent->frameblend[2];
+ rsurface.frameblend[3] = ent->frameblend[3];
+ if (model->surfmesh.isanimated && (rsurface.frameblend[0].lerp != 1 || rsurface.frameblend[0].frame != 0))
+ {
+ if (wanttangents)
+ {
+ rsurface.modelvertex3f = rsurface.array_modelvertex3f;
+ rsurface.modelsvector3f = rsurface.array_modelsvector3f;
+ rsurface.modeltvector3f = rsurface.array_modeltvector3f;
+ rsurface.modelnormal3f = rsurface.array_modelnormal3f;
+ Mod_Alias_GetMesh_Vertices(model, rsurface.frameblend, rsurface.array_modelvertex3f, rsurface.array_modelnormal3f, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f);
+ }
+ else if (wantnormals)
+ {
+ rsurface.modelvertex3f = rsurface.array_modelvertex3f;
+ rsurface.modelsvector3f = NULL;
+ rsurface.modeltvector3f = NULL;
+ rsurface.modelnormal3f = rsurface.array_modelnormal3f;
+ Mod_Alias_GetMesh_Vertices(model, rsurface.frameblend, rsurface.array_modelvertex3f, rsurface.array_modelnormal3f, NULL, NULL);
+ }
+ else
+ {
+ rsurface.modelvertex3f = rsurface.array_modelvertex3f;
+ rsurface.modelsvector3f = NULL;
+ rsurface.modeltvector3f = NULL;
+ rsurface.modelnormal3f = NULL;
+ Mod_Alias_GetMesh_Vertices(model, rsurface.frameblend, rsurface.array_modelvertex3f, NULL, NULL, NULL);
+ }
+ rsurface.modelvertex3f_bufferobject = 0;
+ rsurface.modelvertex3f_bufferoffset = 0;
+ rsurface.modelsvector3f_bufferobject = 0;
+ rsurface.modelsvector3f_bufferoffset = 0;
+ rsurface.modeltvector3f_bufferobject = 0;
+ rsurface.modeltvector3f_bufferoffset = 0;
+ rsurface.modelnormal3f_bufferobject = 0;
+ rsurface.modelnormal3f_bufferoffset = 0;
+ rsurface.generatedvertex = true;
+ }
+ else
+ {
+ rsurface.modelvertex3f = model->surfmesh.data_vertex3f;
+ rsurface.modelvertex3f_bufferobject = model->surfmesh.vbo;
+ rsurface.modelvertex3f_bufferoffset = model->surfmesh.vbooffset_vertex3f;
+ rsurface.modelsvector3f = model->surfmesh.data_svector3f;
+ rsurface.modelsvector3f_bufferobject = model->surfmesh.vbo;
+ rsurface.modelsvector3f_bufferoffset = model->surfmesh.vbooffset_svector3f;
+ rsurface.modeltvector3f = model->surfmesh.data_tvector3f;
+ rsurface.modeltvector3f_bufferobject = model->surfmesh.vbo;
+ rsurface.modeltvector3f_bufferoffset = model->surfmesh.vbooffset_tvector3f;
+ rsurface.modelnormal3f = model->surfmesh.data_normal3f;
+ rsurface.modelnormal3f_bufferobject = model->surfmesh.vbo;
+ rsurface.modelnormal3f_bufferoffset = model->surfmesh.vbooffset_normal3f;
+ rsurface.generatedvertex = false;
+ }
+ rsurface.modellightmapcolor4f = model->surfmesh.data_lightmapcolor4f;
+ rsurface.modellightmapcolor4f_bufferobject = model->surfmesh.vbo;
+ rsurface.modellightmapcolor4f_bufferoffset = model->surfmesh.vbooffset_lightmapcolor4f;
+ rsurface.modeltexcoordtexture2f = model->surfmesh.data_texcoordtexture2f;
+ rsurface.modeltexcoordtexture2f_bufferobject = model->surfmesh.vbo;
+ rsurface.modeltexcoordtexture2f_bufferoffset = model->surfmesh.vbooffset_texcoordtexture2f;
+ rsurface.modeltexcoordlightmap2f = model->surfmesh.data_texcoordlightmap2f;
+ rsurface.modeltexcoordlightmap2f_bufferobject = model->surfmesh.vbo;
+ rsurface.modeltexcoordlightmap2f_bufferoffset = model->surfmesh.vbooffset_texcoordlightmap2f;
+ rsurface.modelelement3i = model->surfmesh.data_element3i;
+ rsurface.modelelement3i_bufferobject = model->surfmesh.ebo;
+ rsurface.modellightmapoffsets = model->surfmesh.data_lightmapoffsets;
+ rsurface.modelnum_vertices = model->surfmesh.num_vertices;
+ rsurface.modelnum_triangles = model->surfmesh.num_triangles;
+ rsurface.modelsurfaces = model->data_surfaces;
+ rsurface.vertex3f = rsurface.modelvertex3f;
+ rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
+ rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
+ rsurface.svector3f = rsurface.modelsvector3f;
+ rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
+ rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
+ rsurface.tvector3f = rsurface.modeltvector3f;
+ rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
+ rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
+ rsurface.normal3f = rsurface.modelnormal3f;
+ rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
+ rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
+ rsurface.texcoordtexture2f = rsurface.modeltexcoordtexture2f;
+}
+
+static const int quadedges[6][2] = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}};
+void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generatetangents, int texturenumsurfaces, msurface_t **texturesurfacelist)
+{
+ int deformindex;
+ int texturesurfaceindex;
+ int i, j;
+ float amplitude;
+ float animpos;
+ float scale;
+ const float *v1, *in_tc;
+ float *out_tc;
+ float center[3], forward[3], right[3], up[3], v[3], newforward[3], newright[3], newup[3];
+ float waveparms[4];
+ q3shaderinfo_deform_t *deform;
+ // if vertices are dynamic (animated models), generate them into the temporary rsurface.array_model* arrays and point rsurface.model* at them instead of the static data from the model itself
+ if (rsurface.generatedvertex)
+ {
+ if (rsurface.texture->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
+ generatenormals = true;
+ for (i = 0;i < Q3MAXDEFORMS;i++)
+ {
+ if (rsurface.texture->deforms[i].deform == Q3DEFORM_AUTOSPRITE)
+ {
+ generatetangents = true;
+ generatenormals = true;
+ }
+ if (rsurface.texture->deforms[i].deform != Q3DEFORM_NONE)
+ generatenormals = true;
+ }
+ if (generatenormals && !rsurface.modelnormal3f)
+ {
+ rsurface.normal3f = rsurface.modelnormal3f = rsurface.array_modelnormal3f;
+ rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject = 0;
+ rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset = 0;
+ Mod_BuildNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modelelement3i, rsurface.array_modelnormal3f, r_smoothnormals_areaweighting.integer);
+ }
+ if (generatetangents && !rsurface.modelsvector3f)
+ {
+ rsurface.svector3f = rsurface.modelsvector3f = rsurface.array_modelsvector3f;
+ rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject = 0;
+ rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset = 0;
+ rsurface.tvector3f = rsurface.modeltvector3f = rsurface.array_modeltvector3f;
+ rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject = 0;
+ rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset = 0;
+ Mod_BuildTextureVectorsFromNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modeltexcoordtexture2f, rsurface.modelnormal3f, rsurface.modelelement3i, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f, r_smoothnormals_areaweighting.integer);
+ }
+ }
+ rsurface.vertex3f = rsurface.modelvertex3f;
+ rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
+ rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
+ rsurface.svector3f = rsurface.modelsvector3f;
+ rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
+ rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
+ rsurface.tvector3f = rsurface.modeltvector3f;
+ rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
+ rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
+ rsurface.normal3f = rsurface.modelnormal3f;
+ rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
+ rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
+ // if vertices are deformed (sprite flares and things in maps, possibly
+ // water waves, bulges and other deformations), generate them into
+ // rsurface.deform* arrays from whatever the rsurface.* arrays point to
+ // (may be static model data or generated data for an animated model, or
+ // the previous deform pass)
+ for (deformindex = 0, deform = rsurface.texture->deforms;deformindex < Q3MAXDEFORMS && deform->deform;deformindex++, deform++)
+ {
+ switch (deform->deform)
+ {
+ default:
+ case Q3DEFORM_PROJECTIONSHADOW:
+ case Q3DEFORM_TEXT0:
+ case Q3DEFORM_TEXT1:
+ case Q3DEFORM_TEXT2:
+ case Q3DEFORM_TEXT3:
+ case Q3DEFORM_TEXT4:
+ case Q3DEFORM_TEXT5:
+ case Q3DEFORM_TEXT6:
+ case Q3DEFORM_TEXT7:
+ case Q3DEFORM_NONE:
+ break;
+ case Q3DEFORM_AUTOSPRITE:
+ Matrix4x4_Transform3x3(&rsurface.inversematrix, r_view.forward, newforward);
+ Matrix4x4_Transform3x3(&rsurface.inversematrix, r_view.right, newright);
+ Matrix4x4_Transform3x3(&rsurface.inversematrix, r_view.up, newup);
+ VectorNormalize(newforward);
+ VectorNormalize(newright);
+ VectorNormalize(newup);
+ // make deformed versions of only the model vertices used by the specified surfaces
+ for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+ {
+ const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+ // a single autosprite surface can contain multiple sprites...
+ for (j = 0;j < surface->num_vertices - 3;j += 4)
+ {
+ VectorClear(center);
+ for (i = 0;i < 4;i++)
+ VectorAdd(center, (rsurface.vertex3f + 3 * surface->num_firstvertex) + (j+i) * 3, center);
+ VectorScale(center, 0.25f, center);
+ VectorCopy((rsurface.normal3f + 3 * surface->num_firstvertex) + j*3, forward);
+ VectorCopy((rsurface.svector3f + 3 * surface->num_firstvertex) + j*3, right);
+ VectorCopy((rsurface.tvector3f + 3 * surface->num_firstvertex) + j*3, up);
+ for (i = 0;i < 4;i++)
+ {
+ VectorSubtract((rsurface.vertex3f + 3 * surface->num_firstvertex) + (j+i)*3, center, v);
+ VectorMAMAMAM(1, center, DotProduct(forward, v), newforward, DotProduct(right, v), newright, DotProduct(up, v), newup, rsurface.array_deformedvertex3f + (surface->num_firstvertex+i+j) * 3);
+ }
+ }
+ Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformednormal3f, r_smoothnormals_areaweighting.integer);
+ Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer);
+ }
+ rsurface.vertex3f = rsurface.array_deformedvertex3f;
+ rsurface.vertex3f_bufferobject = 0;
+ rsurface.vertex3f_bufferoffset = 0;
+ rsurface.svector3f = rsurface.array_deformedsvector3f;
+ rsurface.svector3f_bufferobject = 0;
+ rsurface.svector3f_bufferoffset = 0;
+ rsurface.tvector3f = rsurface.array_deformedtvector3f;
+ rsurface.tvector3f_bufferobject = 0;
+ rsurface.tvector3f_bufferoffset = 0;
+ rsurface.normal3f = rsurface.array_deformednormal3f;
+ rsurface.normal3f_bufferobject = 0;
+ rsurface.normal3f_bufferoffset = 0;
+ break;
+ case Q3DEFORM_AUTOSPRITE2:
+ Matrix4x4_Transform3x3(&rsurface.inversematrix, r_view.forward, newforward);
+ Matrix4x4_Transform3x3(&rsurface.inversematrix, r_view.right, newright);
+ Matrix4x4_Transform3x3(&rsurface.inversematrix, r_view.up, newup);
+ VectorNormalize(newforward);
+ VectorNormalize(newright);
+ VectorNormalize(newup);
+ // make deformed versions of only the model vertices used by the specified surfaces
+ for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+ {
+ const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+ const float *v1, *v2;
+ vec3_t start, end;
+ float f, l;
+ struct
+ {
+ float length2;
+ const float *v1;
+ const float *v2;
+ }
+ shortest[2];
+ memset(shortest, 0, sizeof(shortest));
+ // a single autosprite surface can contain multiple sprites...
+ for (j = 0;j < surface->num_vertices - 3;j += 4)
+ {
+ VectorClear(center);
+ for (i = 0;i < 4;i++)
+ VectorAdd(center, (rsurface.vertex3f + 3 * surface->num_firstvertex) + (j+i) * 3, center);
+ VectorScale(center, 0.25f, center);
+ // find the two shortest edges, then use them to define the
+ // axis vectors for rotating around the central axis
+ for (i = 0;i < 6;i++)
+ {
+ v1 = rsurface.vertex3f + 3 * (surface->num_firstvertex + quadedges[i][0]);
+ v2 = rsurface.vertex3f + 3 * (surface->num_firstvertex + quadedges[i][1]);
+#if 0
+ Debug_PolygonBegin(NULL, 0, false, 0);
+ Debug_PolygonVertex(v1[0], v1[1], v1[2], 0, 0, 1, 0, 0, 1);
+ Debug_PolygonVertex((v1[0] + v2[0]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 4, (v1[1] + v2[1]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1], (v1[2] + v2[2]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2], 0, 0, 1, 1, 0, 1);
+ Debug_PolygonVertex(v2[0], v2[1], v2[2], 0, 0, 1, 0, 0, 1);
+ Debug_PolygonEnd();
+#endif
+ l = VectorDistance2(v1, v2);
+ // this length bias tries to make sense of square polygons, assuming they are meant to be upright
+ if (v1[2] != v2[2])
+ l += (1.0f / 1024.0f);
+ if (shortest[0].length2 > l || i == 0)
+ {
+ shortest[1] = shortest[0];
+ shortest[0].length2 = l;
+ shortest[0].v1 = v1;
+ shortest[0].v2 = v2;
+ }
+ else if (shortest[1].length2 > l || i == 1)
+ {
+ shortest[1].length2 = l;
+ shortest[1].v1 = v1;
+ shortest[1].v2 = v2;
+ }
+ }
+ VectorLerp(shortest[0].v1, 0.5f, shortest[0].v2, start);
+ VectorLerp(shortest[1].v1, 0.5f, shortest[1].v2, end);
+#if 0
+ Debug_PolygonBegin(NULL, 0, false, 0);
+ Debug_PolygonVertex(start[0], start[1], start[2], 0, 0, 1, 1, 0, 1);
+ Debug_PolygonVertex(center[0] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 4, center[1] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1] * 4, center[2] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2] * 4, 0, 0, 0, 1, 0, 1);
+ Debug_PolygonVertex(end[0], end[1], end[2], 0, 0, 0, 1, 1, 1);
+ Debug_PolygonEnd();
+#endif
+ // this calculates the right vector from the shortest edge
+ // and the up vector from the edge midpoints
+ VectorSubtract(shortest[0].v1, shortest[0].v2, right);
+ VectorNormalize(right);
+ VectorSubtract(end, start, up);
+ VectorNormalize(up);
+ // calculate a forward vector to use instead of the original plane normal (this is how we get a new right vector)
+ //VectorSubtract(rsurface.modelorg, center, forward);
+ Matrix4x4_Transform3x3(&rsurface.inversematrix, r_view.forward, forward);
+ VectorNegate(forward, forward);
+ VectorReflect(forward, 0, up, forward);
+ VectorNormalize(forward);
+ CrossProduct(up, forward, newright);
+ VectorNormalize(newright);
+#if 0
+ Debug_PolygonBegin(NULL, 0, false, 0);
+ Debug_PolygonVertex(center[0] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 8, center[1] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1] * 8, center[2] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2] * 8, 0, 0, 1, 0, 0, 1);
+ Debug_PolygonVertex(center[0] + right[0] * 8, center[1] + right[1] * 8, center[2] + right[2] * 8, 0, 0, 0, 1, 0, 1);
+ Debug_PolygonVertex(center[0] + up [0] * 8, center[1] + up [1] * 8, center[2] + up [2] * 8, 0, 0, 0, 0, 1, 1);
+ Debug_PolygonEnd();
+#endif
+#if 0
+ Debug_PolygonBegin(NULL, 0, false, 0);
+ Debug_PolygonVertex(center[0] + forward [0] * 8, center[1] + forward [1] * 8, center[2] + forward [2] * 8, 0, 0, 1, 0, 0, 1);
+ Debug_PolygonVertex(center[0] + newright[0] * 8, center[1] + newright[1] * 8, center[2] + newright[2] * 8, 0, 0, 0, 1, 0, 1);
+ Debug_PolygonVertex(center[0] + up [0] * 8, center[1] + up [1] * 8, center[2] + up [2] * 8, 0, 0, 0, 0, 1, 1);
+ Debug_PolygonEnd();
+#endif
+ // rotate the quad around the up axis vector, this is made
+ // especially easy by the fact we know the quad is flat,
+ // so we only have to subtract the center position and
+ // measure distance along the right vector, and then
+ // multiply that by the newright vector and add back the
+ // center position
+ // we also need to subtract the old position to undo the
+ // displacement from the center, which we do with a
+ // DotProduct, the subtraction/addition of center is also
+ // optimized into DotProducts here
+ l = DotProduct(right, center);
+ for (i = 0;i < 4;i++)
+ {
+ v1 = rsurface.vertex3f + 3 * (surface->num_firstvertex + j + i);
+ f = DotProduct(right, v1) - l;
+ VectorMAMAM(1, v1, -f, right, f, newright, rsurface.array_deformedvertex3f + (surface->num_firstvertex+i+j) * 3);
+ }
+ }
+ Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformednormal3f, r_smoothnormals_areaweighting.integer);
+ Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer);
+ }
+ rsurface.vertex3f = rsurface.array_deformedvertex3f;
+ rsurface.vertex3f_bufferobject = 0;
+ rsurface.vertex3f_bufferoffset = 0;
+ rsurface.svector3f = rsurface.array_deformedsvector3f;
+ rsurface.svector3f_bufferobject = 0;
+ rsurface.svector3f_bufferoffset = 0;
+ rsurface.tvector3f = rsurface.array_deformedtvector3f;
+ rsurface.tvector3f_bufferobject = 0;
+ rsurface.tvector3f_bufferoffset = 0;
+ rsurface.normal3f = rsurface.array_deformednormal3f;
+ rsurface.normal3f_bufferobject = 0;
+ rsurface.normal3f_bufferoffset = 0;
+ break;
+ case Q3DEFORM_NORMAL:
+ // deform the normals to make reflections wavey
+ for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+ {
+ const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+ for (j = 0;j < surface->num_vertices;j++)
+ {
+ float vertex[3];
+ float *normal = (rsurface.array_deformednormal3f + 3 * surface->num_firstvertex) + j*3;
+ VectorScale((rsurface.vertex3f + 3 * surface->num_firstvertex) + j*3, 0.98f, vertex);
+ VectorCopy((rsurface.normal3f + 3 * surface->num_firstvertex) + j*3, normal);
+ normal[0] += deform->parms[0] * noise4f( vertex[0], vertex[1], vertex[2], r_refdef.time * deform->parms[1]);
+ normal[1] += deform->parms[0] * noise4f( 98 + vertex[0], vertex[1], vertex[2], r_refdef.time * deform->parms[1]);
+ normal[2] += deform->parms[0] * noise4f(196 + vertex[0], vertex[1], vertex[2], r_refdef.time * deform->parms[1]);
+ VectorNormalize(normal);
+ }
+ Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer);
+ }
+ rsurface.svector3f = rsurface.array_deformedsvector3f;
+ rsurface.svector3f_bufferobject = 0;
+ rsurface.svector3f_bufferoffset = 0;
+ rsurface.tvector3f = rsurface.array_deformedtvector3f;
+ rsurface.tvector3f_bufferobject = 0;
+ rsurface.tvector3f_bufferoffset = 0;
+ rsurface.normal3f = rsurface.array_deformednormal3f;
+ rsurface.normal3f_bufferobject = 0;
+ rsurface.normal3f_bufferoffset = 0;
+ break;
+ case Q3DEFORM_WAVE:
+ // deform vertex array to make wavey water and flags and such
+ waveparms[0] = deform->waveparms[0];
+ waveparms[1] = deform->waveparms[1];
+ waveparms[2] = deform->waveparms[2];
+ waveparms[3] = deform->waveparms[3];
+ // this is how a divisor of vertex influence on deformation
+ animpos = deform->parms[0] ? 1.0f / deform->parms[0] : 100.0f;
+ scale = R_EvaluateQ3WaveFunc(deform->wavefunc, waveparms);
+ for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+ {
+ const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+ for (j = 0;j < surface->num_vertices;j++)
+ {
+ float *vertex = (rsurface.array_deformedvertex3f + 3 * surface->num_firstvertex) + j*3;
+ VectorCopy((rsurface.vertex3f + 3 * surface->num_firstvertex) + j*3, vertex);
+ // if the wavefunc depends on time, evaluate it per-vertex
+ if (waveparms[3])
+ {
+ waveparms[2] = deform->waveparms[2] + (vertex[0] + vertex[1] + vertex[2]) * animpos;
+ scale = R_EvaluateQ3WaveFunc(deform->wavefunc, waveparms);
+ }
+ VectorMA(vertex, scale, (rsurface.normal3f + 3 * surface->num_firstvertex) + j*3, vertex);
+ }
+ }
+ rsurface.vertex3f = rsurface.array_deformedvertex3f;
+ rsurface.vertex3f_bufferobject = 0;
+ rsurface.vertex3f_bufferoffset = 0;
+ break;
+ case Q3DEFORM_BULGE:
+ // deform vertex array to make the surface have moving bulges
+ for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+ {
+ const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+ for (j = 0;j < surface->num_vertices;j++)
+ {
+ scale = sin((rsurface.modeltexcoordtexture2f[2 * (surface->num_firstvertex + j)] * deform->parms[0] + r_refdef.time * deform->parms[2])) * deform->parms[1];
+ VectorMA(rsurface.vertex3f + 3 * (surface->num_firstvertex + j), scale, rsurface.normal3f + 3 * (surface->num_firstvertex + j), rsurface.array_deformedvertex3f + 3 * (surface->num_firstvertex + j));
+ }
+ }
+ rsurface.vertex3f = rsurface.array_deformedvertex3f;
+ rsurface.vertex3f_bufferobject = 0;
+ rsurface.vertex3f_bufferoffset = 0;
+ break;
+ case Q3DEFORM_MOVE:
+ // deform vertex array
+ scale = R_EvaluateQ3WaveFunc(deform->wavefunc, deform->waveparms);
+ VectorScale(deform->parms, scale, waveparms);
+ for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+ {
+ const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+ for (j = 0;j < surface->num_vertices;j++)
+ VectorAdd(rsurface.vertex3f + 3 * (surface->num_firstvertex + j), waveparms, rsurface.array_deformedvertex3f + 3 * (surface->num_firstvertex + j));
+ }
+ rsurface.vertex3f = rsurface.array_deformedvertex3f;
+ rsurface.vertex3f_bufferobject = 0;
+ rsurface.vertex3f_bufferoffset = 0;
+ break;
+ }
+ }
+ // generate texcoords based on the chosen texcoord source
+ switch(rsurface.texture->tcgen.tcgen)
+ {
+ default:
+ case Q3TCGEN_TEXTURE:
+ rsurface.texcoordtexture2f = rsurface.modeltexcoordtexture2f;
+ rsurface.texcoordtexture2f_bufferobject = rsurface.modeltexcoordtexture2f_bufferobject;
+ rsurface.texcoordtexture2f_bufferoffset = rsurface.modeltexcoordtexture2f_bufferoffset;
+ break;
+ case Q3TCGEN_LIGHTMAP:
+ rsurface.texcoordtexture2f = rsurface.modeltexcoordlightmap2f;
+ rsurface.texcoordtexture2f_bufferobject = rsurface.modeltexcoordlightmap2f_bufferobject;
+ rsurface.texcoordtexture2f_bufferoffset = rsurface.modeltexcoordlightmap2f_bufferoffset;
+ break;
+ case Q3TCGEN_VECTOR:
+ for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+ {
+ const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+ for (j = 0, v1 = rsurface.modelvertex3f + 3 * surface->num_firstvertex, out_tc = rsurface.array_generatedtexcoordtexture2f + 2 * surface->num_firstvertex;j < surface->num_vertices;j++, v1 += 3, out_tc += 2)
+ {
+ out_tc[0] = DotProduct(v1, rsurface.texture->tcgen.parms);
+ out_tc[1] = DotProduct(v1, rsurface.texture->tcgen.parms + 3);
+ }
+ }
+ rsurface.texcoordtexture2f = rsurface.array_generatedtexcoordtexture2f;
+ rsurface.texcoordtexture2f_bufferobject = 0;
+ rsurface.texcoordtexture2f_bufferoffset = 0;
+ break;
+ case Q3TCGEN_ENVIRONMENT:
+ // make environment reflections using a spheremap
+ for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+ {
+ const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+ const float *vertex = rsurface.modelvertex3f + 3 * surface->num_firstvertex;
+ const float *normal = rsurface.modelnormal3f + 3 * surface->num_firstvertex;
+ float *out_tc = rsurface.array_generatedtexcoordtexture2f + 2 * surface->num_firstvertex;
+ for (j = 0;j < surface->num_vertices;j++, vertex += 3, normal += 3, out_tc += 2)
+ {
+ float l, d, eyedir[3];
+ VectorSubtract(rsurface.modelorg, vertex, eyedir);
+ l = 0.5f / VectorLength(eyedir);
+ d = DotProduct(normal, eyedir)*2;
+ out_tc[0] = 0.5f + (normal[1]*d - eyedir[1])*l;
+ out_tc[1] = 0.5f - (normal[2]*d - eyedir[2])*l;
+ }
+ }
+ rsurface.texcoordtexture2f = rsurface.array_generatedtexcoordtexture2f;
+ rsurface.texcoordtexture2f_bufferobject = 0;
+ rsurface.texcoordtexture2f_bufferoffset = 0;
+ break;
+ }
+ // the only tcmod that needs software vertex processing is turbulent, so
+ // check for it here and apply the changes if needed
+ // and we only support that as the first one
+ // (handling a mixture of turbulent and other tcmods would be problematic
+ // without punting it entirely to a software path)
+ if (rsurface.texture->tcmods[0].tcmod == Q3TCMOD_TURBULENT)
+ {
+ amplitude = rsurface.texture->tcmods[0].parms[1];
+ animpos = rsurface.texture->tcmods[0].parms[2] + r_refdef.time * rsurface.texture->tcmods[0].parms[3];
+ for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+ {
+ const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+ for (j = 0, v1 = rsurface.modelvertex3f + 3 * surface->num_firstvertex, in_tc = rsurface.texcoordtexture2f + 2 * surface->num_firstvertex, out_tc = rsurface.array_generatedtexcoordtexture2f + 2 * surface->num_firstvertex;j < surface->num_vertices;j++, v1 += 3, in_tc += 2, out_tc += 2)
+ {
+ out_tc[0] = in_tc[0] + amplitude * sin(((v1[0] + v1[2]) * 1.0 / 1024.0f + animpos) * M_PI * 2);
+ out_tc[1] = in_tc[1] + amplitude * sin(((v1[1] ) * 1.0 / 1024.0f + animpos) * M_PI * 2);
+ }
+ }
+ rsurface.texcoordtexture2f = rsurface.array_generatedtexcoordtexture2f;
+ rsurface.texcoordtexture2f_bufferobject = 0;
+ rsurface.texcoordtexture2f_bufferoffset = 0;
+ }
+ rsurface.texcoordlightmap2f = rsurface.modeltexcoordlightmap2f;
+ rsurface.texcoordlightmap2f_bufferobject = rsurface.modeltexcoordlightmap2f_bufferobject;
+ rsurface.texcoordlightmap2f_bufferoffset = rsurface.modeltexcoordlightmap2f_bufferoffset;
+ R_Mesh_VertexPointer(rsurface.vertex3f, rsurface.vertex3f_bufferobject, rsurface.vertex3f_bufferoffset);
+}
+
+void RSurf_DrawBatch_Simple(int texturenumsurfaces, msurface_t **texturesurfacelist)
+{
+ int i, j;
+ const msurface_t *surface = texturesurfacelist[0];
+ const msurface_t *surface2;
+ int firstvertex;
+ int endvertex;
+ int numvertices;
+ int numtriangles;
+ // TODO: lock all array ranges before render, rather than on each surface
+ if (texturenumsurfaces == 1)
+ {
+ GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+ R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+ }
+ else if (r_batchmode.integer == 2)
+ {
+ #define MAXBATCHTRIANGLES 4096
+ int batchtriangles = 0;
+ int batchelements[MAXBATCHTRIANGLES*3];
+ for (i = 0;i < texturenumsurfaces;i = j)
+ {
+ surface = texturesurfacelist[i];
+ j = i + 1;
+ if (surface->num_triangles > MAXBATCHTRIANGLES)
+ {
+ R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+ continue;
+ }
+ memcpy(batchelements, rsurface.modelelement3i + 3 * surface->num_firsttriangle, surface->num_triangles * sizeof(int[3]));
+ batchtriangles = surface->num_triangles;
+ firstvertex = surface->num_firstvertex;
+ endvertex = surface->num_firstvertex + surface->num_vertices;
+ for (;j < texturenumsurfaces;j++)
+ {
+ surface2 = texturesurfacelist[j];
+ if (batchtriangles + surface2->num_triangles > MAXBATCHTRIANGLES)
+ break;
+ memcpy(batchelements + batchtriangles * 3, rsurface.modelelement3i + 3 * surface2->num_firsttriangle, surface2->num_triangles * sizeof(int[3]));
+ batchtriangles += surface2->num_triangles;
+ firstvertex = min(firstvertex, surface2->num_firstvertex);
+ endvertex = max(endvertex, surface2->num_firstvertex + surface2->num_vertices);
+ }
+ surface2 = texturesurfacelist[j-1];
+ numvertices = endvertex - firstvertex;
+ R_Mesh_Draw(firstvertex, numvertices, batchtriangles, batchelements, 0, 0);
+ }
+ }
+ else if (r_batchmode.integer == 1)
+ {
+ for (i = 0;i < texturenumsurfaces;i = j)
+ {
+ surface = texturesurfacelist[i];
+ for (j = i + 1, surface2 = surface + 1;j < texturenumsurfaces;j++, surface2++)
+ if (texturesurfacelist[j] != surface2)
+ break;
+ surface2 = texturesurfacelist[j-1];
+ numvertices = surface2->num_firstvertex + surface2->num_vertices - surface->num_firstvertex;
+ numtriangles = surface2->num_firsttriangle + surface2->num_triangles - surface->num_firsttriangle;
+ GL_LockArrays(surface->num_firstvertex, numvertices);
+ R_Mesh_Draw(surface->num_firstvertex, numvertices, numtriangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+ }
+ }
+ else
+ {
+ for (i = 0;i < texturenumsurfaces;i++)
+ {
+ surface = texturesurfacelist[i];
+ GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+ R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+ }
+ }
+}
+
+static void RSurf_DrawBatch_WithLightmapSwitching_WithWaterTextureSwitching(int texturenumsurfaces, msurface_t **texturesurfacelist, int lightmaptexunit, int deluxemaptexunit, int refractiontexunit, int reflectiontexunit)
+{
+ int i, planeindex, vertexindex;
+ float d, bestd;
+ vec3_t vert;
+ const float *v;
+ r_waterstate_waterplane_t *p, *bestp;
+ msurface_t *surface;
+ if (r_waterstate.renderingscene)
+ return;
+ for (i = 0;i < texturenumsurfaces;i++)
+ {
+ surface = texturesurfacelist[i];
+ if (lightmaptexunit >= 0)
+ R_Mesh_TexBind(lightmaptexunit, R_GetTexture(surface->lightmaptexture));
+ if (deluxemaptexunit >= 0)
+ R_Mesh_TexBind(deluxemaptexunit, R_GetTexture(surface->deluxemaptexture));
+ // pick the closest matching water plane
+ bestd = 0;
+ bestp = NULL;
+ for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
+ {
+ d = 0;
+ for (vertexindex = 0, v = rsurface.modelvertex3f + surface->num_firstvertex * 3;vertexindex < surface->num_vertices;vertexindex++, v += 3)
+ {
+ Matrix4x4_Transform(&rsurface.matrix, v, vert);
+ d += fabs(PlaneDiff(vert, &p->plane));
+ }
+ if (bestd > d || !bestp)
+ {
+ bestd = d;
+ bestp = p;
+ }
+ }
+ if (bestp)
+ {
+ if (refractiontexunit >= 0)
+ R_Mesh_TexBind(refractiontexunit, R_GetTexture(bestp->texture_refraction));
+ if (reflectiontexunit >= 0)
+ R_Mesh_TexBind(reflectiontexunit, R_GetTexture(bestp->texture_reflection));
+ }
+ else
+ {
+ if (refractiontexunit >= 0)
+ R_Mesh_TexBind(refractiontexunit, R_GetTexture(r_texture_black));
+ if (reflectiontexunit >= 0)
+ R_Mesh_TexBind(reflectiontexunit, R_GetTexture(r_texture_black));
+ }
+ GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+ R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface.modelelement3i + 3 * surface->num_firsttriangle), rsurface.modelelement3i_bufferobject, (sizeof(int[3]) * surface->num_firsttriangle));
+ }
+}