Beispiel #1
0
void R_DrawSpecialEntities (const entity_t* ents)
{
	const entity_t* e;

	if (!ents)
		return;

	e = ents;

	R_EnableBlend(true);
	R_EnableDrawAsGlow(true);

	while (e) {
		if (e->flags & RF_BOX) {
			R_DrawBox(e);
		} else if (e->flags & RF_PATH) {
			R_DrawFloor(e);
		} else if (e->flags & RF_ARROW) {
			R_DrawArrow(e);
		}
		e = e->next;
	}

	R_EnableDrawAsGlow(false);
	R_EnableBlend(false);
}
Beispiel #2
0
/*
 * R_EnableColorShell
 */
void R_EnableShell(boolean_t enable) {

	if (enable == r_state.shell_enabled)
		return;

	r_state.shell_enabled = enable;

	if (enable) {
		glEnable(GL_POLYGON_OFFSET_FILL);
		glPolygonOffset(-1.0, 0.0);

		R_EnableBlend(true);
		R_BlendFunc(GL_SRC_ALPHA, GL_ONE);

		if (r_state.lighting_enabled)
			R_ProgramParameter1f("OFFSET", r_view.time / 3.0);
	} else {
		R_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		R_EnableBlend(false);

		glPolygonOffset(0.0, 0.0);
		glDisable(GL_POLYGON_OFFSET_FILL);

		if (r_state.lighting_enabled)
			R_ProgramParameter1f("OFFSET", 0.0);
	}
}
Beispiel #3
0
/**
 * @brief Re-draws the mesh using the stencil test.  Meshes with stale lighting
 * information, or with a lighting point above our view, are not drawn.
 */
static void R_DrawMeshShadow (entity_t *e, const mAliasMesh_t *mesh)
{
	vec4_t color;
	const bool oldBlend = r_state.blend_enabled;
	const bool lighting = r_state.lighting_enabled;
	r_program_t *program = r_state.active_program;

	if (!r_stencilshadows->integer)
		return;

	if (!r_shadows->value)
		return;

	if (r_wire->integer)
		return;

	if (e->flags & RF_NO_SHADOW)
		return;

	if (e->flags & RF_TRANSLUCENT)
		return;

	if (!R_UpdateShadowOrigin(e))
		return;

	if (e->lighting->shadowOrigin[2] > refdef.viewOrigin[2])
		return;

	Vector4Set(color, 0.0, 0.0, 0.0, r_shadows->value * MESH_SHADOW_ALPHA);
	R_Color(color);
	R_EnableTexture(&texunit_diffuse, false);
	R_EnableBlend(true);
	R_RotateForMeshShadow(e);
	R_EnableStencilTest(true);

	if (lighting)
		R_EnableLighting(NULL, false);
	glDrawArrays(GL_TRIANGLES, 0, mesh->num_tris * 3);
	refdef.batchCount++;
	if (lighting)
		R_EnableLighting(program, true);

	R_EnableStencilTest(false);
	R_RotateForMeshShadow(NULL);
	R_EnableBlend(oldBlend);
	R_EnableTexture(&texunit_diffuse, true);
	R_Color(NULL);
}
Beispiel #4
0
/**
 * @sa R_GetEntityLists
 */
void R_DrawBlendMeshEntities (entity_t* ents)
{
	if (!ents)
		return;

	if (!(refdef.rendererFlags & RDF_NOWORLDMODEL)) {
		R_EnableLighting(r_state.model_program, true);
	}
	R_EnableBlend(true);

	R_DrawMeshEntities(R_MergeSortEntList(ents));

	R_EnableBlend(false);
	if (!(refdef.rendererFlags & RDF_NOWORLDMODEL)) {
		R_EnableLighting(nullptr, false);
		R_EnableGlowMap(nullptr);
	}
}
Beispiel #5
0
/**
 * @sa R_GetEntityLists
 */
void R_DrawBlendMeshEntities (entity_t *ents)
{
	if (!ents)
		return;

	if (!(refdef.rendererFlags & RDF_NOWORLDMODEL)) {
		R_EnableLighting(r_state.world_program, qtrue);
	}
	R_EnableBlend(qtrue);

	R_DrawMeshEntities(R_MergeSortEntList(ents));

	R_EnableBlend(qfalse);
	if (!(refdef.rendererFlags & RDF_NOWORLDMODEL)) {
		R_EnableLighting(NULL, qfalse);
		R_EnableGlowMap(NULL);
	}
}
Beispiel #6
0
/**
 * @sa R_RenderFrame
 * @sa R_BeginFrame
 */
void R_EndFrame (void)
{
	R_EnableBlend(true);

	R_DrawChars();  /* draw all chars accumulated above */

	/* restore draw color */
	R_Color(NULL);

	R_EnableBlend(false);

	if (vid_gamma->modified) {
		if (!vid_ignoregamma->integer) {
			const float g = vid_gamma->value;
			SDL_SetGamma(g, g, g);
		}
		vid_gamma->modified = false;
	}

	R_ClearScene();

	SDL_GL_SwapBuffers();
}
Beispiel #7
0
/**
 * @brief Draws shadow and highlight effects for the entities (actors)
 * @note The origins are already transformed
 */
void R_DrawEntityEffects (void)
{
	const int mask = r_stencilshadows->integer ? RF_BLOOD : (RF_SHADOW | RF_BLOOD);
	GLint oldDepthFunc;
	glGetIntegerv(GL_DEPTH_FUNC, &oldDepthFunc);

	R_EnableBlend(true);

	if (actorIndicator == nullptr) {
		selectedActorIndicator = R_FindImage("pics/sfx/actor_selected", it_effect);
		actorIndicator = R_FindImage("pics/sfx/actor", it_effect);
	}

	for (int i = 0; i < refdef.numEntities; i++) {
		const entity_t* e = &r_entities[i];

		if (e->flags <= RF_BOX)
			continue;

		glPushMatrix();
		glMultMatrixf(e->transform.matrix);

		if (e->flags & mask) {
			const vec3_t points[] = { { -18.0, 14.0, -28.5 }, { 10.0, 14.0, -28.5 }, { 10.0, -14.0, -28.5 }, { -18.0,
					-14.0, -28.5 } };
			/** @todo use default_texcoords */
			const vec2_t texcoords[] = { { 0.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.0 }, { 0.0, 0.0 } };

			if (e->flags & RF_SHADOW) {
				R_BindTexture(shadow->texnum);
			} else {
				assert(e->texture);
				R_BindTexture(e->texture->texnum);
			}

			R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords);
			R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points);
			glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
			R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
			R_BindDefaultArray(GL_VERTEX_ARRAY);

			refdef.batchCount++;
		}

		if (e->flags & RF_ACTOR) {
			const float size = 15.0;
			int texnum;
			/* draw the circles for team-members and allied troops */
			vec4_t color = {1, 1, 1, 1};
			const vec3_t points[] = { { -size, size, -SELECTION_DELTA }, { size, size, -SELECTION_DELTA }, { size, -size,
					-SELECTION_DELTA }, { -size, -size, -SELECTION_DELTA } };
			/** @todo use default_texcoords */
			const vec2_t texcoords[] = { { 0.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.0 }, { 0.0, 0.0 } };

			if (e->flags & RF_SELECTED)
				Vector4Set(color, 0, 1, 0, 0.5);
			else if (e->flags & RF_MEMBER)
				Vector4Set(color, 0, 0.8, 0, 0.5);
			else if (e->flags & RF_ALLIED)
				Vector4Set(color, 0, 1, 0.5, 0.5);
			else if (e->flags & RF_NEUTRAL)
				Vector4Set(color, 1, 1, 0, 0.5);
			else if (e->flags & RF_OPPONENT)
				Vector4Set(color, 1, 0, 0, 0.5);
			else
				Vector4Set(color, 0.3, 0.3, 0.3, 0.5);

			if (e->flags & RF_SELECTED)
				texnum = selectedActorIndicator->texnum;
			else
				texnum = actorIndicator->texnum;

			R_BindTexture(texnum);
			R_Color(color);
			R_EnableDrawAsGlow(true);

			/* circle points */
			R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords);
			R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points);

			glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

			refdef.batchCount++;

			/* add transparency when something is in front of the circle */
			color[3] *= 0.25;
			R_Color(color);
			glDepthFunc(GL_GREATER);
			glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
			glDepthFunc(oldDepthFunc);

			refdef.batchCount++;

			R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
			R_BindDefaultArray(GL_VERTEX_ARRAY);

			R_Color(nullptr);
			R_EnableDrawAsGlow(false);
		}
		glPopMatrix();
	}
}
Beispiel #8
0
/*
 * @brief Main entry point for drawing the scene (world and entities).
 */
void R_DrawView(void) {

	R_UpdateFrustum();

	R_UpdateVis();

	R_MarkBspSurfaces();

	R_EnableFog(true);

	R_DrawSkyBox();

	// wait for the client to populate our lights array
	Thread_Wait(&r_view.thread);

	// now dispatch another thread to cull entities while we draw the world
	r_view.thread = Thread_Create(R_CullEntities, NULL);

	R_MarkLights();

	const r_sorted_bsp_surfaces_t *surfs = r_model_state.world->bsp->sorted_surfaces;

	R_DrawOpaqueBspSurfaces(&surfs->opaque);

	R_DrawOpaqueWarpBspSurfaces(&surfs->opaque_warp);

	R_DrawAlphaTestBspSurfaces(&surfs->alpha_test);

	R_EnableBlend(true);

	R_DrawBackBspSurfaces(&surfs->back);

	R_DrawMaterialBspSurfaces(&surfs->material);

	R_DrawFlareBspSurfaces(&surfs->flare);

	R_EnableBlend(false);

	R_DrawBspNormals();

	// ensure the thread has finished culling entities
	Thread_Wait(&r_view.thread);

	// now dispatch another thread to update particles while we draw entities
	r_view.thread = Thread_Create(R_UpdateParticles, NULL);

	R_DrawEntities();

	R_EnableBlend(true);

	R_DrawBspLights();

	R_DrawBlendBspSurfaces(&surfs->blend);

	R_DrawBlendWarpBspSurfaces(&surfs->blend_warp);

	// ensure the thread has finished updating particles
	Thread_Wait(&r_view.thread);

	R_DrawParticles();

	R_EnableFog(false);

	R_DrawCoronas();

	R_DrawBspLeafs();

	R_EnableBlend(false);

	R_ResetArrayState();
}
Beispiel #9
0
/*
 * @brief Main entry point for drawing the scene (world and entities).
 */
void R_DrawView(void) {

	R_UpdateFrustum();

	R_UpdateVis();

	R_MarkBspSurfaces();

	R_EnableFog(true);

	R_DrawSkyBox();

	// wait for the client to fully populate the scene
	Thread_Wait(r_view.thread);

	// dispatch threads to cull entities and sort elements while we draw the world
	thread_t *cull_entities = Thread_Create(R_CullEntities, NULL);
	thread_t *sort_elements = Thread_Create(R_SortElements, NULL);

	R_MarkLights();

	const r_sorted_bsp_surfaces_t *surfs = r_model_state.world->bsp->sorted_surfaces;

	R_DrawOpaqueBspSurfaces(&surfs->opaque);

	R_DrawOpaqueWarpBspSurfaces(&surfs->opaque_warp);

	R_DrawAlphaTestBspSurfaces(&surfs->alpha_test);

	R_EnableBlend(true);

	R_DrawBackBspSurfaces(&surfs->back);

	R_DrawMaterialBspSurfaces(&surfs->material);

	R_DrawFlareBspSurfaces(&surfs->flare);

	R_EnableBlend(false);

	// wait for entity culling to complete
	Thread_Wait(cull_entities);

	R_DrawEntities();

	R_EnableBlend(true);

	// wait for element sorting to complete
	Thread_Wait(sort_elements);

	R_DrawElements();

	R_EnableFog(false);

	R_DrawDeveloperTools();

	R_DrawCoronas();

	R_EnableBlend(false);

	R_ResetArrayState();

#if 0
	vec3_t tmp;
	VectorMA(r_view.origin, MAX_WORLD_DIST, r_view.forward, tmp);

	cm_trace_t tr = Cl_Trace(r_view.origin, tmp, NULL, NULL, cl.client_num + 1, MASK_SOLID);
	if (tr.fraction > 0.0 && tr.fraction < 1.0) {
		Com_Print("%s: %d: %s\n", tr.surface->name, tr.plane.num, vtos(tr.plane.normal));
	}

#endif
}
Beispiel #10
0
/**
 * @sa R_BeginFrame
 * @sa R_EndFrame
 */
void R_RenderFrame (void)
{
	R_Setup3D();

	/* activate wire mode */
	if (r_wire->integer)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	if (!(refdef.rendererFlags & RDF_NOWORLDMODEL)) {
		int tile;
		if (r_threads->integer) {
			while (r_threadstate.state != THREAD_RENDERER)
				Sys_Sleep(0);

			r_threadstate.state = THREAD_CLIENT;
		} else {
			R_SetupFrustum();

			/* draw brushes on current worldlevel */
			R_GetLevelSurfaceLists();
		}

		R_UpdateSustainedLights();

		R_CheckError();

		for (tile = 0; tile < r_numMapTiles; tile++) {
			const model_t *mapTile = r_mapTiles[tile];
			const mBspModel_t *bsp = &mapTile->bsp;

			R_AddBspRRef(bsp, vec3_origin, vec3_origin, false);
		}

		R_GetEntityLists();

		R_EnableFog(true);

		R_RenderOpaqueBspRRefs();
		R_RenderOpaqueWarpBspRRefs();
		R_DrawOpaqueMeshEntities(r_opaque_mesh_entities);

		R_RenderAlphaTestBspRRefs();

		R_EnableBlend(true);
		R_RenderMaterialBspRRefs();

		R_EnableFog(false);

		R_RenderBlendBspRRefs();
		R_RenderBlendWarpBspRRefs();
		R_DrawBlendMeshEntities(r_blend_mesh_entities);

		R_EnableFog(true);
		R_RenderFlareBspRRefs();
		R_EnableFog(false);

		if (r_debug_lights->integer) {
			int i;

			for (i = 0; i < refdef.numStaticLights; i++) {
				const light_t *l = &refdef.staticLights[i];
				R_AddCorona(l->origin, l->radius, l->color);
			}
			for (i = 0; i < refdef.numDynamicLights; i++) {
				const light_t *l = &refdef.dynamicLights[i];
				R_AddCorona(l->origin, l->radius, l->color);
			}
		}

		R_DrawCoronas();
		R_EnableBlend(false);

		for (tile = 0; tile < r_numMapTiles; tile++) {
			R_DrawBspNormals(tile);
		}

		R_Color(NULL);
		R_DrawSpecialEntities(r_special_entities);
		R_DrawNullEntities(r_null_entities);
		R_DrawEntityEffects();
	} else {
		glClear(GL_DEPTH_BUFFER_BIT);

		R_GetEntityLists();

		R_RenderOpaqueBspRRefs();
		R_RenderOpaqueWarpBspRRefs();
		R_DrawOpaqueMeshEntities(r_opaque_mesh_entities);
		R_RenderAlphaTestBspRRefs();

		R_EnableBlend(true);

		R_RenderMaterialBspRRefs();

		R_RenderBlendBspRRefs();
		R_RenderBlendWarpBspRRefs();
		R_DrawBlendMeshEntities(r_blend_mesh_entities);

		R_RenderFlareBspRRefs();

		R_EnableBlend(false);

		R_Color(NULL);
		R_DrawSpecialEntities(r_special_entities);
		R_DrawNullEntities(r_null_entities);
		R_DrawEntityEffects();
	}

	R_EnableBlend(true);

	R_DrawParticles();

	R_EnableBlend(false);

	/* leave wire mode again */
	if (r_wire->integer)
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	R_DrawBloom();

	R_DrawBoundingBoxes();

	R_ResetArrayState();

	/* go back into 2D mode for hud and the like */
	R_Setup2D();

	R_CheckError();
}
Beispiel #11
0
/**
 * @brief Flares are batched by their texture.  Usually, this means one draw operation
 * for all flares in view.  Flare visibility is calculated every few millis, and
 * flare alpha is ramped up or down depending on the results of the visibility
 * trace.  Flares are also faded according to the angle of their surface to the
 * view origin.
 */
void R_DrawFlareSurfaces (const mBspSurfaces_t* surfs, glElementIndex_t* indexPtr)
{
	const image_t* image;
	int i, j, k, l, m;
	vec3_t view, verts[4];
	vec3_t right, up, upright, downright;
	float dot, dist, scale, alpha;
	bool visible;
	bool oldblend;

	if (!r_flares->integer)
		return;

	if (!surfs->count)
		return;

	oldblend = r_state.blend_enabled;

	R_EnableColorArray(true);

	R_ResetArrayState();

	/** @todo better GL state handling */
	glDisable(GL_DEPTH_TEST);
	R_EnableBlend(true);
	R_BlendFunc(GL_SRC_ALPHA, GL_ONE);

	image = r_flaretextures[0];
	R_BindTexture(image->texnum);

	j = k = l = 0;
	for (i = 0; i < surfs->count; i++) {
		mBspSurface_t* surf = surfs->surfaces[i];
		mBspFlare_t* f;

		if (surf->frame != r_locals.frame)
			continue;

		f = surf->flare;

		/* bind the flare's texture */
		if (f->image != image) {
			R_DrawArrays(0, l / 3);
			j = k = l = 0;

			refdef.batchCount++;

			image = f->image;
			R_BindTexture(image->texnum);
		}

		/* periodically test visibility to ramp alpha */
		if (refdef.time - f->time > 0.02) {
			if (refdef.time - f->time > 0.5) /* reset old flares */
				f->alpha = 0;

			R_Trace(refdef.viewOrigin, f->origin, 0, MASK_SOLID);
			visible = refdef.trace.fraction == 1.0;

			f->alpha += (visible ? 0.03 : -0.15); /* ramp */

			if (f->alpha > 0.75) /* clamp */
				f->alpha = 0.75;
			else if (f->alpha < 0)
				f->alpha = 0.0;

			f->time = refdef.time;
		}

		VectorSubtract(f->origin, refdef.viewOrigin, view);
		dist = VectorNormalize(view);

		/* fade according to angle */
		dot = DotProduct(surf->normal, view);
		if (dot > 0)
			continue;

		alpha = 0.1 + -dot * r_flares->value;

		if (alpha > 1.0)
			alpha = 1.0;

		alpha = f->alpha * alpha;

		/* scale according to distance */
		scale = f->radius + (f->radius * dist * .0005);

		VectorScale(r_locals.right, scale, right);
		VectorScale(r_locals.up, scale, up);

		VectorAdd(up, right, upright);
		VectorSubtract(right, up, downright);

		VectorSubtract(f->origin, downright, verts[0]);
		VectorAdd(f->origin, upright, verts[1]);
		VectorAdd(f->origin, downright, verts[2]);
		VectorSubtract(f->origin, upright, verts[3]);

		for (m = 0; m < 4; m++) { /* duplicate color data to all 4 verts */
			memcpy(&r_state.color_array[j], f->color, sizeof(vec3_t));
			r_state.color_array[j + 3] = alpha;
			j += 4;
		}

		/* copy texcoord info */
		memcpy(&texunit_diffuse.texcoord_array[k], default_texcoords, sizeof(vec2_t) * 4);
		k += sizeof(vec2_t) / sizeof(vec_t) * 4;

		/* and lastly copy the 4 verts */
		memcpy(&r_state.vertex_array_3d[l], verts, sizeof(vec3_t) * 4);
		l += sizeof(vec3_t) / sizeof(vec_t) * 4;
	}

	R_DrawArrays(0, l / 3);

	refdef.batchCount++;

	R_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	R_EnableBlend(oldblend);
	glEnable(GL_DEPTH_TEST);

	R_EnableColorArray(false);

	R_Color(nullptr);
}
Beispiel #12
0
/**
 * @brief Draws weather effects
 */
void Weather::render (void)
{
	/* Don't play the weather particles if the user doesn't want them */
	if (!Cvar_GetInteger("cl_particleweather")) {
		return;
	}

	GLfloat prtPos[3 * 4 * Weather::MAX_PARTICLES];
	GLfloat prtTexcoord[2 * 4 * Weather::MAX_PARTICLES];
	GLushort prtIndex[3 * 2 * Weather::MAX_PARTICLES];
	size_t prtCount = 0;
	//const float splashTimeScale = 0.001f / splashTime; /* from msec to 1/sec units, so division is done only once per frame */
	/** @todo shadowcasting at least for the sunlight */
#if 0 // disabled because of bizarre colors
	vec4_t prtColor = {color[0] * (refdef.ambientColor[0] + refdef.sunDiffuseColor[0]),
		color[1] * (refdef.ambientColor[1] + refdef.sunDiffuseColor[1]),
		color[2] * (refdef.ambientColor[2] + refdef.sunDiffuseColor[2]),
		color[3]};
#else
	vec_t prtBrightness = VectorLength(refdef.ambientColor) + VectorLength(refdef.sunDiffuseColor); /** @todo proper color to brightness mapping */
	vec4_t prtColor = {color[0] * prtBrightness, color[1] * prtBrightness, color[2] * prtBrightness, color[3]}; /* alpha is not to be scaled */
#endif

	for (size_t i = 0; i < Weather::MAX_PARTICLES; i++) {
		Weather::particle &prt = particles[i];

		if (prt.ttl < 0)
			continue;

		/* if particle is alive, construct a camera-facing quad */
		GLfloat x, y, z;
		GLfloat dx, dy, dz;
		GLfloat thisParticleSize = particleSize;
		if (prt.vz == 0 && splashTime > 0) {
			/* splash particle, do zoom and other things */
			/** @todo alpha decay */
			const float splashFactor = prt.ttl / 500.0f;//1.0f - prt.ttl * splashTimeScale;
			thisParticleSize *= (splashSize - 1.0f) * splashFactor  + 1.0f;
			dx = dy = thisParticleSize;
		} else {
			dx = i & 1 ? thisParticleSize* 2 : thisParticleSize; dy = i & 1 ? thisParticleSize : thisParticleSize * 2 ; /** @todo make a proper projection instead of this hack */
		}

		x = prt.x; y = prt.y; z = prt.z;
		dz = smearLength * prt.vz * 0.5; /** @todo should use proper velocity vector ... or maybe not */
		/* construct a particle geometry; */
		GLfloat *pos = &prtPos[3 * 4 * prtCount];
		GLfloat *texcoord = &prtTexcoord[2 * 4 * prtCount];
		GLushort *idx = &prtIndex[3 * 2 * prtCount];

		/** @todo actually rotate billboard in the correct position */
		pos[0] = x - dx; pos[1] = y - dy; pos[2] = z + dz * 2;
		pos[3] = x + dx; pos[4] = y - dy; pos[5] = z + dz *2;
		pos[6] = x + dx; pos[7] = y + dy; pos[8] = z;
		pos[9] = x - dx; pos[10] = y + dy; pos[11] = z;

		texcoord[0] = 0; texcoord[1] = 0; /* upper left vertex */
		texcoord[2] = 1.0f; texcoord[3] = 0; /* upper right vertex */
		texcoord[4] = 1.0f; texcoord[5] = 1.0f; /* bottom right vertex */
		texcoord[6] = 0; texcoord[7] = 1.0f; /* bottom left vertex */

		idx[0] = 4 * prtCount; idx[1] = 4 * prtCount + 1; idx[2] = 4 * prtCount + 2;
		idx[3] = 4 * prtCount + 2; idx[4] = 4 * prtCount + 3; idx[5] = 4 * prtCount;

		prtCount++;
	}

	if (prtCount < 1)
		return;

	/* draw the generated array */
	R_Color(prtColor);
	glBindTexture(GL_TEXTURE_2D, wpTexture());
	R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, prtPos);
	R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, prtTexcoord);
	R_EnableBlend(true);
	glDrawElements(GL_TRIANGLES, prtCount * 3 * 2, GL_UNSIGNED_SHORT, prtIndex);
	R_EnableBlend(false);
	R_ResetArrayState();
	R_Color(nullptr);

	refdef.batchCount++;
}