Merge branch 'master' into sdl2

Conflicts:
	Makefile
	code/renderercommon/qgl.h
	code/renderergl1/tr_local.h
	code/sdl/sdl_glimp.c
This commit is contained in:
Tim Angus 2013-05-08 14:27:15 +01:00
commit d9d52f0306
427 changed files with 66082 additions and 14083 deletions

View file

@ -0,0 +1,70 @@
uniform sampler2D u_TextureMap;
uniform vec4 u_Color;
uniform vec2 u_InvTexRes;
varying vec2 var_TexCoords;
void main()
{
vec4 color;
vec2 tc;
#if 0
float c[7] = float[7](1.0, 0.9659258263, 0.8660254038, 0.7071067812, 0.5, 0.2588190451, 0.0);
tc = var_TexCoords + u_InvTexRes * vec2( c[0], c[6]); color = texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[1], c[5]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[2], c[4]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[3], c[3]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[4], c[2]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[5], c[1]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[6], c[0]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[1], -c[5]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[2], -c[4]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[3], -c[3]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[4], -c[2]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[5], -c[1]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[6], -c[0]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[0], c[6]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[1], c[5]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[2], c[4]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[3], c[3]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[4], c[2]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[5], c[1]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[1], -c[5]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[2], -c[4]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[3], -c[3]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[4], -c[2]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[5], -c[1]); color += texture2D(u_TextureMap, tc);
gl_FragColor = color * 0.04166667 * u_Color;
#endif
float c[5] = float[5](1.0, 0.9238795325, 0.7071067812, 0.3826834324, 0.0);
tc = var_TexCoords + u_InvTexRes * vec2( c[0], c[4]); color = texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[1], c[3]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[2], c[2]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[3], c[1]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[4], c[0]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[1], -c[3]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[2], -c[2]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[3], -c[1]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( c[4], -c[0]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[0], c[4]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[1], c[3]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[2], c[2]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[3], c[1]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[1], -c[3]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[2], -c[2]); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( -c[3], -c[1]); color += texture2D(u_TextureMap, tc);
gl_FragColor = color * 0.0625 * u_Color;
}

View file

@ -0,0 +1,13 @@
attribute vec4 attr_Position;
attribute vec4 attr_TexCoord0;
uniform mat4 u_ModelViewProjectionMatrix;
varying vec2 var_TexCoords;
void main()
{
gl_Position = u_ModelViewProjectionMatrix * attr_Position;
var_TexCoords = attr_TexCoord0.st;
}

View file

@ -0,0 +1,55 @@
uniform sampler2D u_TextureMap;
uniform vec4 u_Color;
uniform vec2 u_InvTexRes;
varying vec2 var_TexCoords;
const vec3 LUMINANCE_VECTOR = vec3(0.2125, 0.7154, 0.0721); //vec3(0.299, 0.587, 0.114);
vec3 GetValues(vec2 offset, vec3 current)
{
vec3 minAvgMax;
vec2 tc = var_TexCoords + u_InvTexRes * offset; minAvgMax = texture2D(u_TextureMap, tc).rgb;
#ifdef FIRST_PASS
float lumi = max(dot(LUMINANCE_VECTOR, minAvgMax), 0.000001);
float loglumi = clamp(log2(lumi), -10.0, 10.0);
minAvgMax = vec3(loglumi * 0.05 + 0.5);
#endif
return vec3(min(current.x, minAvgMax.x), current.y + minAvgMax.y, max(current.z, minAvgMax.z));
}
void main()
{
vec3 current = vec3(1.0, 0.0, 0.0);
#ifdef FIRST_PASS
current = GetValues(vec2( 0.0, 0.0), current);
#else
current = GetValues(vec2(-1.5, -1.5), current);
current = GetValues(vec2(-0.5, -1.5), current);
current = GetValues(vec2( 0.5, -1.5), current);
current = GetValues(vec2( 1.5, -1.5), current);
current = GetValues(vec2(-1.5, -0.5), current);
current = GetValues(vec2(-0.5, -0.5), current);
current = GetValues(vec2( 0.5, -0.5), current);
current = GetValues(vec2( 1.5, -0.5), current);
current = GetValues(vec2(-1.5, 0.5), current);
current = GetValues(vec2(-0.5, 0.5), current);
current = GetValues(vec2( 0.5, 0.5), current);
current = GetValues(vec2( 1.5, 0.5), current);
current = GetValues(vec2(-1.5, 1.5), current);
current = GetValues(vec2(-0.5, 1.5), current);
current = GetValues(vec2( 0.5, 1.5), current);
current = GetValues(vec2( 1.5, 1.5), current);
current.y *= 0.0625;
#endif
gl_FragColor = vec4(current, 1.0f);
}

View file

@ -0,0 +1,13 @@
attribute vec4 attr_Position;
attribute vec4 attr_TexCoord0;
uniform mat4 u_ModelViewProjectionMatrix;
varying vec2 var_TexCoords;
void main()
{
gl_Position = u_ModelViewProjectionMatrix * attr_Position;
var_TexCoords = attr_TexCoord0.st;
}

View file

@ -0,0 +1,58 @@
uniform sampler2D u_ScreenImageMap;
uniform sampler2D u_ScreenDepthMap;
uniform vec4 u_ViewInfo; // zfar / znear, zfar
varying vec2 var_ScreenTex;
//float gauss[5] = float[5](0.30, 0.23, 0.097, 0.024, 0.0033);
float gauss[4] = float[4](0.40, 0.24, 0.054, 0.0044);
//float gauss[3] = float[3](0.60, 0.19, 0.0066);
#define GAUSS_SIZE 4
float getLinearDepth(sampler2D depthMap, const vec2 tex, const float zFarDivZNear)
{
float sampleZDivW = texture2D(depthMap, tex).r;
return 1.0 / mix(zFarDivZNear, 1.0, sampleZDivW);
}
vec4 depthGaussian1D(sampler2D imageMap, sampler2D depthMap, vec2 tex, float zFarDivZNear, float zFar)
{
float scale = 1.0 / 256.0;
#if defined(USE_HORIZONTAL_BLUR)
vec2 direction = vec2(1.0, 0.0) * scale;
#else // if defined(USE_VERTICAL_BLUR)
vec2 direction = vec2(0.0, 1.0) * scale;
#endif
float depthCenter = zFar * getLinearDepth(depthMap, tex, zFarDivZNear);
vec2 centerSlope = vec2(dFdx(depthCenter), dFdy(depthCenter)) / vec2(dFdx(tex.x), dFdy(tex.y));
vec4 result = texture2D(imageMap, tex) * gauss[0];
float total = gauss[0];
int i, j;
for (i = 0; i < 2; i++)
{
for (j = 1; j < GAUSS_SIZE; j++)
{
vec2 offset = direction * j;
float depthSample = zFar * getLinearDepth(depthMap, tex + offset, zFarDivZNear);
float depthExpected = depthCenter + dot(centerSlope, offset);
if(abs(depthSample - depthExpected) < 5.0)
{
result += texture2D(imageMap, tex + offset) * gauss[j];
total += gauss[j];
}
}
direction = -direction;
}
return result / total;
}
void main()
{
gl_FragColor = depthGaussian1D(u_ScreenImageMap, u_ScreenDepthMap, var_ScreenTex, u_ViewInfo.x, u_ViewInfo.y);
}

View file

@ -0,0 +1,12 @@
attribute vec4 attr_Position;
attribute vec4 attr_TexCoord0;
varying vec2 var_ScreenTex;
void main()
{
gl_Position = attr_Position;
var_ScreenTex = attr_TexCoord0.xy;
//vec2 screenCoords = gl_Position.xy / gl_Position.w;
//var_ScreenTex = screenCoords * 0.5 + 0.5;
}

View file

@ -0,0 +1,12 @@
uniform sampler2D u_DiffuseMap;
varying vec2 var_Tex1;
varying vec4 var_Color;
void main()
{
vec4 color = texture2D(u_DiffuseMap, var_Tex1);
gl_FragColor = color * var_Color;
}

View file

@ -0,0 +1,92 @@
attribute vec4 attr_Position;
attribute vec4 attr_TexCoord0;
attribute vec3 attr_Normal;
uniform vec4 u_DlightInfo;
#if defined(USE_DEFORM_VERTEXES)
uniform int u_DeformGen;
uniform float u_DeformParams[5];
uniform float u_Time;
#endif
uniform vec4 u_Color;
uniform mat4 u_ModelViewProjectionMatrix;
varying vec2 var_Tex1;
varying vec4 var_Color;
#if defined(USE_DEFORM_VERTEXES)
vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st)
{
if (u_DeformGen == 0)
{
return pos;
}
float base = u_DeformParams[0];
float amplitude = u_DeformParams[1];
float phase = u_DeformParams[2];
float frequency = u_DeformParams[3];
float spread = u_DeformParams[4];
if (u_DeformGen == DGEN_BULGE)
{
phase *= M_PI * 0.25 * st.x;
}
else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH)
{
phase += dot(pos.xyz, vec3(spread));
}
float value = phase + (u_Time * frequency);
float func;
if (u_DeformGen == DGEN_WAVE_SIN)
{
func = sin(value * 2.0 * M_PI);
}
else if (u_DeformGen == DGEN_WAVE_SQUARE)
{
func = sign(sin(value * 2.0 * M_PI));
}
else if (u_DeformGen == DGEN_WAVE_TRIANGLE)
{
func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0;
}
else if (u_DeformGen == DGEN_WAVE_SAWTOOTH)
{
func = fract(value);
}
else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH)
{
func = (1.0 - fract(value));
}
else if (u_DeformGen == DGEN_BULGE)
{
func = sin(value);
}
return pos + normal * (base + func * amplitude);
}
#endif
void main()
{
vec4 position = attr_Position;
vec3 normal = attr_Normal;
#if defined(USE_DEFORM_VERTEXES)
position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st);
#endif
gl_Position = u_ModelViewProjectionMatrix * position;
vec3 dist = u_DlightInfo.xyz - position.xyz;
var_Tex1 = dist.xy * u_DlightInfo.a + vec2(0.5);
float dlightmod = step(0.0, dot(dist, normal));
dlightmod *= clamp(2.0 * (1.0 - abs(dist.z) * u_DlightInfo.a), 0.0, 1.0);
var_Color = u_Color * dlightmod;
}

View file

@ -0,0 +1,34 @@
uniform sampler2D u_TextureMap;
uniform vec2 u_InvTexRes;
varying vec2 var_TexCoords;
void main()
{
vec4 color;
vec2 tc;
tc = var_TexCoords + u_InvTexRes * vec2(-1.5, -1.5); color = texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2(-0.5, -1.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( 0.5, -1.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( 1.5, -1.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2(-1.5, -0.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2(-0.5, -0.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( 0.5, -0.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( 1.5, -0.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2(-1.5, 0.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2(-0.5, 0.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( 0.5, 0.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( 1.5, 0.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2(-1.5, 1.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2(-0.5, 1.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( 0.5, 1.5); color += texture2D(u_TextureMap, tc);
tc = var_TexCoords + u_InvTexRes * vec2( 1.5, 1.5); color += texture2D(u_TextureMap, tc);
color *= 0.0625;
gl_FragColor = color;
}

View file

@ -0,0 +1,13 @@
attribute vec4 attr_Position;
attribute vec4 attr_TexCoord0;
uniform mat4 u_ModelViewProjectionMatrix;
varying vec2 var_TexCoords;
void main()
{
gl_Position = u_ModelViewProjectionMatrix * attr_Position;
var_TexCoords = attr_TexCoord0.st;
}

View file

@ -0,0 +1,9 @@
uniform vec4 u_Color;
varying float var_Scale;
void main()
{
gl_FragColor = u_Color;
gl_FragColor.a *= sqrt(clamp(var_Scale, 0.0, 1.0));
}

View file

@ -0,0 +1,117 @@
attribute vec4 attr_Position;
attribute vec3 attr_Normal;
attribute vec4 attr_TexCoord0;
//#if defined(USE_VERTEX_ANIMATION)
attribute vec4 attr_Position2;
attribute vec3 attr_Normal2;
//#endif
uniform vec4 u_FogDistance;
uniform vec4 u_FogDepth;
uniform float u_FogEyeT;
//#if defined(USE_DEFORM_VERTEXES)
uniform int u_DeformGen;
uniform float u_DeformParams[5];
//#endif
uniform float u_Time;
uniform mat4 u_ModelViewProjectionMatrix;
//#if defined(USE_VERTEX_ANIMATION)
uniform float u_VertexLerp;
//#endif
varying float var_Scale;
#if defined(USE_DEFORM_VERTEXES)
vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st)
{
if (u_DeformGen == 0)
{
return pos;
}
float base = u_DeformParams[0];
float amplitude = u_DeformParams[1];
float phase = u_DeformParams[2];
float frequency = u_DeformParams[3];
float spread = u_DeformParams[4];
if (u_DeformGen == DGEN_BULGE)
{
phase *= M_PI * 0.25 * st.x;
}
else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH)
{
phase += dot(pos.xyz, vec3(spread));
}
float value = phase + (u_Time * frequency);
float func;
if (u_DeformGen == DGEN_WAVE_SIN)
{
func = sin(value * 2.0 * M_PI);
}
else if (u_DeformGen == DGEN_WAVE_SQUARE)
{
func = sign(sin(value * 2.0 * M_PI));
}
else if (u_DeformGen == DGEN_WAVE_TRIANGLE)
{
func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0;
}
else if (u_DeformGen == DGEN_WAVE_SAWTOOTH)
{
func = fract(value);
}
else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH)
{
func = (1.0 - fract(value));
}
else if (u_DeformGen == DGEN_BULGE)
{
func = sin(value);
}
return pos + normal * (base + func * amplitude);
}
#endif
float CalcFog(vec4 position)
{
float s = dot(position, u_FogDistance) * 8.0;
float t = dot(position, u_FogDepth);
if (t < 1.0)
{
t = step(step(0.0, -u_FogEyeT), t);
}
else
{
t /= t - min(u_FogEyeT, 0.0);
}
return s * t;
}
void main()
{
#if defined(USE_VERTEX_ANIMATION)
vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp);
vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp));
#else
vec4 position = attr_Position;
vec3 normal = attr_Normal;
#endif
#if defined(USE_DEFORM_VERTEXES)
position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st);
#endif
gl_Position = u_ModelViewProjectionMatrix * position;
var_Scale = CalcFog(position);
}

View file

@ -0,0 +1,43 @@
uniform sampler2D u_DiffuseMap;
#if defined(USE_LIGHTMAP)
uniform sampler2D u_LightMap;
uniform int u_Texture1Env;
#endif
varying vec2 var_DiffuseTex;
#if defined(USE_LIGHTMAP)
varying vec2 var_LightTex;
#endif
varying vec4 var_Color;
void main()
{
vec4 color = texture2D(u_DiffuseMap, var_DiffuseTex);
#if defined(USE_LIGHTMAP)
vec4 color2 = texture2D(u_LightMap, var_LightTex);
#if defined(RGBE_LIGHTMAP)
color2.rgb *= exp2(color2.a * 255.0 - 128.0);
color2.a = 1.0;
#endif
if (u_Texture1Env == TEXENV_MODULATE)
{
color *= color2;
}
else if (u_Texture1Env == TEXENV_ADD)
{
color += color2;
}
else if (u_Texture1Env == TEXENV_REPLACE)
{
color = color2;
}
#endif
gl_FragColor = color * var_Color;
}

View file

@ -0,0 +1,251 @@
attribute vec4 attr_Position;
attribute vec3 attr_Normal;
#if defined(USE_VERTEX_ANIMATION)
attribute vec4 attr_Position2;
attribute vec3 attr_Normal2;
#endif
attribute vec4 attr_Color;
attribute vec4 attr_TexCoord0;
#if defined(USE_LIGHTMAP) || defined(USE_TCGEN)
attribute vec4 attr_TexCoord1;
#endif
uniform vec4 u_DiffuseTexMatrix;
uniform vec4 u_DiffuseTexOffTurb;
#if defined(USE_TCGEN) || defined(USE_RGBAGEN)
uniform vec3 u_ViewOrigin;
#endif
#if defined(USE_TCGEN)
uniform int u_TCGen0;
uniform vec3 u_TCGen0Vector0;
uniform vec3 u_TCGen0Vector1;
#endif
#if defined(USE_FOG)
uniform vec4 u_FogDistance;
uniform vec4 u_FogDepth;
uniform float u_FogEyeT;
uniform vec4 u_FogColorMask;
#endif
#if defined(USE_DEFORM_VERTEXES)
uniform int u_DeformGen;
uniform float u_DeformParams[5];
uniform float u_Time;
#endif
uniform mat4 u_ModelViewProjectionMatrix;
uniform vec4 u_BaseColor;
uniform vec4 u_VertColor;
#if defined(USE_RGBAGEN)
uniform int u_ColorGen;
uniform int u_AlphaGen;
uniform vec3 u_AmbientLight;
uniform vec3 u_DirectedLight;
uniform vec4 u_LightOrigin;
uniform float u_PortalRange;
#endif
#if defined(USE_VERTEX_ANIMATION)
uniform float u_VertexLerp;
#endif
varying vec2 var_DiffuseTex;
#if defined(USE_LIGHTMAP)
varying vec2 var_LightTex;
#endif
varying vec4 var_Color;
#if defined(USE_DEFORM_VERTEXES)
vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st)
{
float base = u_DeformParams[0];
float amplitude = u_DeformParams[1];
float phase = u_DeformParams[2];
float frequency = u_DeformParams[3];
float spread = u_DeformParams[4];
if (u_DeformGen == DGEN_BULGE)
{
phase *= M_PI * 0.25 * st.x;
}
else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH)
{
phase += dot(pos.xyz, vec3(spread));
}
float value = phase + (u_Time * frequency);
float func;
if (u_DeformGen == DGEN_WAVE_SIN)
{
func = sin(value * 2.0 * M_PI);
}
else if (u_DeformGen == DGEN_WAVE_SQUARE)
{
func = sign(sin(value * 2.0 * M_PI));
}
else if (u_DeformGen == DGEN_WAVE_TRIANGLE)
{
func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0;
}
else if (u_DeformGen == DGEN_WAVE_SAWTOOTH)
{
func = fract(value);
}
else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH)
{
func = (1.0 - fract(value));
}
else if (u_DeformGen == DGEN_BULGE)
{
func = sin(value);
}
return pos + normal * (base + func * amplitude);
}
#endif
#if defined(USE_TCGEN)
vec2 GenTexCoords(int TCGen, vec3 position, vec3 normal, vec3 TCGenVector0, vec3 TCGenVector1)
{
vec2 tex = attr_TexCoord0.st;
if (TCGen == TCGEN_LIGHTMAP)
{
tex = attr_TexCoord1.st;
}
else if (TCGen == TCGEN_ENVIRONMENT_MAPPED)
{
vec3 viewer = normalize(u_ViewOrigin - position);
tex = -reflect(viewer, normal).yz * vec2(0.5, -0.5) + 0.5;
}
else if (TCGen == TCGEN_VECTOR)
{
tex = vec2(dot(position, TCGenVector0), dot(position, TCGenVector1));
}
return tex;
}
#endif
#if defined(USE_TCMOD)
vec2 ModTexCoords(vec2 st, vec3 position, vec4 texMatrix, vec4 offTurb)
{
float amplitude = offTurb.z;
float phase = offTurb.w;
vec2 st2 = vec2(dot(st, texMatrix.xz), dot(st, texMatrix.yw)) + offTurb.xy;
vec3 offsetPos = position / 1024.0;
offsetPos.x += offsetPos.z;
vec2 texOffset = sin((offsetPos.xy + vec2(phase)) * 2.0 * M_PI);
return st2 + texOffset * amplitude;
}
#endif
#if defined(USE_RGBAGEN)
vec4 CalcColor(vec3 position, vec3 normal)
{
vec4 color = u_VertColor * attr_Color + u_BaseColor;
if (u_ColorGen == CGEN_LIGHTING_DIFFUSE)
{
float incoming = clamp(dot(normal, u_LightOrigin.xyz), 0.0, 1.0);
color.rgb = clamp(u_DirectedLight * incoming + u_AmbientLight, 0.0, 1.0);
}
vec3 toView = u_ViewOrigin - position;
vec3 viewer = normalize(u_ViewOrigin - position);
if (u_AlphaGen == AGEN_LIGHTING_SPECULAR)
{
vec3 lightDir = normalize(vec3(-960.0, -1980.0, 96.0) - position.xyz);
vec3 halfangle = normalize(lightDir + viewer);
color.a = pow(max(dot(normal, halfangle), 0.0), 8.0);
}
else if (u_AlphaGen == AGEN_PORTAL)
{
float alpha = length(toView) / u_PortalRange;
color.a = clamp(alpha, 0.0, 1.0);
}
else if (u_AlphaGen == AGEN_FRESNEL)
{
color.a = 0.10 + 0.90 * pow(1.0 - dot(normal, viewer), 5);
}
return color;
}
#endif
#if defined(USE_FOG)
float CalcFog(vec4 position)
{
float s = dot(position, u_FogDistance) * 8.0;
float t = dot(position, u_FogDepth);
if (t < 1.0)
{
t = step(step(0.0, -u_FogEyeT), t);
}
else
{
t /= t - min(u_FogEyeT, 0.0);
}
return s * t;
}
#endif
void main()
{
#if defined(USE_VERTEX_ANIMATION)
vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp);
vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp));
#else
vec4 position = attr_Position;
vec3 normal = attr_Normal;
#endif
#if defined(USE_DEFORM_VERTEXES)
position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st);
#endif
gl_Position = u_ModelViewProjectionMatrix * position;
#if defined(USE_TCGEN)
vec2 tex = GenTexCoords(u_TCGen0, position.xyz, normal, u_TCGen0Vector0, u_TCGen0Vector1);
#else
vec2 tex = attr_TexCoord0.st;
#endif
#if defined(USE_TCMOD)
var_DiffuseTex = ModTexCoords(tex, position.xyz, u_DiffuseTexMatrix, u_DiffuseTexOffTurb);
#else
var_DiffuseTex = tex;
#endif
#if defined(USE_LIGHTMAP)
var_LightTex = attr_TexCoord1.st;
#endif
#if defined(USE_RGBAGEN)
var_Color = CalcColor(position.xyz, normal);
#else
var_Color = u_VertColor * attr_Color + u_BaseColor;
#endif
#if defined(USE_FOG)
var_Color *= vec4(1.0) - u_FogColorMask * sqrt(clamp(CalcFog(position), 0.0, 1.0));
#endif
}

View file

@ -0,0 +1,429 @@
uniform sampler2D u_DiffuseMap;
#if defined(USE_LIGHTMAP)
uniform sampler2D u_LightMap;
#endif
#if defined(USE_NORMALMAP)
uniform sampler2D u_NormalMap;
#endif
#if defined(USE_DELUXEMAP)
uniform sampler2D u_DeluxeMap;
#endif
#if defined(USE_SPECULARMAP)
uniform sampler2D u_SpecularMap;
#endif
#if defined(USE_SHADOWMAP)
uniform sampler2D u_ShadowMap;
#endif
uniform vec3 u_ViewOrigin;
#if defined(USE_TCGEN)
uniform int u_TCGen0;
#endif
#if defined(USE_LIGHT_VECTOR)
uniform vec4 u_LightOrigin;
uniform vec3 u_DirectedLight;
uniform vec3 u_AmbientLight;
uniform float u_LightRadius;
#endif
#if defined(USE_PRIMARY_LIGHT) || defined(USE_SHADOWMAP)
uniform vec3 u_PrimaryLightColor;
uniform vec3 u_PrimaryLightAmbient;
uniform float u_PrimaryLightRadius;
#endif
#if defined(USE_LIGHT)
uniform vec2 u_MaterialInfo;
#endif
varying vec2 var_DiffuseTex;
#if defined(USE_LIGHTMAP)
varying vec2 var_LightTex;
#endif
varying vec4 var_Color;
#if defined(USE_NORMALMAP) && !defined(USE_VERT_TANGENT_SPACE)
varying vec3 var_Position;
#endif
#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT))
varying vec3 var_SampleToView;
#endif
#if !defined(USE_FAST_LIGHT)
varying vec3 var_Normal;
#endif
#if defined(USE_VERT_TANGENT_SPACE)
varying vec3 var_Tangent;
varying vec3 var_Bitangent;
#endif
varying vec3 var_VertLight;
#if defined(USE_LIGHT) && !defined(USE_DELUXEMAP)
varying vec3 var_LightDirection;
#endif
#if defined(USE_PRIMARY_LIGHT) || defined(USE_SHADOWMAP)
varying vec3 var_PrimaryLightDirection;
#endif
#define EPSILON 0.00000001
#if defined(USE_PARALLAXMAP)
float SampleHeight(sampler2D normalMap, vec2 t)
{
#if defined(SWIZZLE_NORMALMAP)
return texture2D(normalMap, t).r;
#else
return texture2D(normalMap, t).a;
#endif
}
float RayIntersectDisplaceMap(vec2 dp, vec2 ds, sampler2D normalMap)
{
const int linearSearchSteps = 16;
const int binarySearchSteps = 6;
float depthStep = 1.0 / float(linearSearchSteps);
// current size of search window
float size = depthStep;
// current depth position
float depth = 0.0;
// best match found (starts with last position 1.0)
float bestDepth = 1.0;
// search front to back for first point inside object
for(int i = 0; i < linearSearchSteps - 1; ++i)
{
depth += size;
float t = 1.0 - SampleHeight(normalMap, dp + ds * depth);
if(bestDepth > 0.996) // if no depth found yet
if(depth >= t)
bestDepth = depth; // store best depth
}
depth = bestDepth;
// recurse around first point (depth) for closest match
for(int i = 0; i < binarySearchSteps; ++i)
{
size *= 0.5;
float t = 1.0 - SampleHeight(normalMap, dp + ds * depth);
if(depth >= t)
{
bestDepth = depth;
depth -= 2.0 * size;
}
depth += size;
}
return bestDepth;
}
#endif
vec3 CalcDiffuse(vec3 diffuseAlbedo, vec3 N, vec3 L, vec3 E, float NE, float NL, float shininess)
{
#if defined(USE_OREN_NAYAR) || defined(USE_TRIACE_OREN_NAYAR)
float gamma = dot(E, L) - NE * NL;
float B = 2.22222 + 0.1 * shininess;
#if defined(USE_OREN_NAYAR)
float A = 1.0 - 1.0 / (2.0 + 0.33 * shininess);
gamma = clamp(gamma, 0.0, 1.0);
#endif
#if defined(USE_TRIACE_OREN_NAYAR)
float A = 1.0 - 1.0 / (2.0 + 0.65 * shininess);
if (gamma >= 0.0)
#endif
{
B *= max(max(NL, NE), EPSILON);
}
return diffuseAlbedo * (A + gamma / B);
#else
return diffuseAlbedo;
#endif
}
#if defined(USE_SPECULARMAP)
vec3 CalcSpecular(vec3 specularReflectance, float NH, float NL, float NE, float EH, float shininess)
{
#if defined(USE_BLINN) || defined(USE_TRIACE) || defined(USE_TORRANCE_SPARROW)
float blinn = pow(NH, shininess);
#endif
#if defined(USE_BLINN)
return specularReflectance * blinn;
#endif
#if defined(USE_COOK_TORRANCE) || defined (USE_TRIACE) || defined (USE_TORRANCE_SPARROW)
vec3 fresnel = specularReflectance + (vec3(1.0) - specularReflectance) * pow(1.0 - EH, 5);
#endif
#if defined(USE_COOK_TORRANCE) || defined(USE_TORRANCE_SPARROW)
float geo = 2.0 * NH * min(NE, NL);
geo /= max(EH, geo);
#endif
#if defined(USE_COOK_TORRANCE)
float m_sq = 2.0 / max(shininess, EPSILON);
float NH_sq = NH * NH;
float m_NH_sq = m_sq * NH_sq;
float beckmann = exp((NH_sq - 1.0) / max(m_NH_sq, EPSILON)) / max(4.0 * m_NH_sq * NH_sq, EPSILON);
return fresnel * geo * beckmann / max(NE, EPSILON);
#endif
#if defined(USE_TRIACE)
float scale = 0.1248582 * shininess + 0.2691817;
return fresnel * scale * blinn / max(max(NL, NE), EPSILON);
#endif
#if defined(USE_TORRANCE_SPARROW)
float scale = 0.125 * shininess + 1.0;
return fresnel * geo * scale * blinn / max(NE, EPSILON);
#endif
}
#endif
void main()
{
#if !defined(USE_FAST_LIGHT) && (defined(USE_LIGHT) || defined(USE_NORMALMAP))
vec3 surfN = normalize(var_Normal);
#endif
#if defined(USE_DELUXEMAP)
vec3 L = 2.0 * texture2D(u_DeluxeMap, var_LightTex).xyz - vec3(1.0);
//L += var_LightDirection * 0.0001;
#elif defined(USE_LIGHT)
vec3 L = var_LightDirection;
#endif
#if defined(USE_LIGHTMAP)
vec4 lightSample = texture2D(u_LightMap, var_LightTex).rgba;
#if defined(RGBE_LIGHTMAP)
lightSample.rgb *= exp2(lightSample.a * 255.0 - 128.0);
#endif
vec3 lightColor = lightSample.rgb;
#elif defined(USE_LIGHT_VECTOR) && !defined(USE_FAST_LIGHT)
#if defined(USE_INVSQRLIGHT)
float intensity = 1.0 / dot(L, L);
#else
float intensity = clamp((1.0 - dot(L, L) / (u_LightRadius * u_LightRadius)) * 1.07, 0.0, 1.0);
#endif
vec3 lightColor = u_DirectedLight * intensity;
vec3 ambientColor = u_AmbientLight;
#elif defined(USE_LIGHT_VERTEX) && !defined(USE_FAST_LIGHT)
vec3 lightColor = var_VertLight;
#endif
#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT))
vec3 E = normalize(var_SampleToView);
#endif
vec2 texCoords = var_DiffuseTex;
float ambientDiff = 1.0;
#if defined(USE_NORMALMAP)
#if defined(USE_VERT_TANGENT_SPACE)
mat3 tangentToWorld = mat3(var_Tangent, var_Bitangent, var_Normal);
#else
vec3 q0 = dFdx(var_Position);
vec3 q1 = dFdy(var_Position);
vec2 st0 = dFdx(texCoords);
vec2 st1 = dFdy(texCoords);
float dir = sign(st1.t * st0.s - st0.t * st1.s);
vec3 tangent = normalize(q0 * st1.t - q1 * st0.t) * dir;
vec3 bitangent = -normalize(q0 * st1.s - q1 * st0.s) * dir;
mat3 tangentToWorld = mat3(tangent, bitangent, var_Normal);
#endif
#if defined(USE_PARALLAXMAP)
vec3 offsetDir = normalize(E * tangentToWorld);
offsetDir.xy *= -0.05 / offsetDir.z;
texCoords += offsetDir.xy * RayIntersectDisplaceMap(texCoords, offsetDir.xy, u_NormalMap);
#endif
vec3 texN;
#if defined(SWIZZLE_NORMALMAP)
texN.xy = 2.0 * texture2D(u_NormalMap, texCoords).ag - 1.0;
#else
texN.xy = 2.0 * texture2D(u_NormalMap, texCoords).rg - 1.0;
#endif
texN.z = sqrt(clamp(1.0 - dot(texN.xy, texN.xy), 0.0, 1.0));
vec3 N = tangentToWorld * texN;
#if defined(r_normalAmbient)
ambientDiff = 0.781341 * texN.z + 0.218659;
#endif
#elif defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)
vec3 N = surfN;
#endif
#if (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) || (defined(USE_TCGEN) && defined(USE_NORMALMAP))
N = normalize(N);
#endif
#if defined(USE_TCGEN) && defined(USE_NORMALMAP)
if (u_TCGen0 == TCGEN_ENVIRONMENT_MAPPED)
{
texCoords = -reflect(E, N).yz * vec2(0.5, -0.5) + 0.5;
}
#endif
vec4 diffuseAlbedo = texture2D(u_DiffuseMap, texCoords);
#if defined(USE_LIGHT) && defined(USE_GAMMA2_TEXTURES)
diffuseAlbedo.rgb *= diffuseAlbedo.rgb;
#endif
#if defined(USE_LIGHT) && defined(USE_FAST_LIGHT)
gl_FragColor = diffuse.rgb;
#if defined(USE_LIGHTMAP)
gl_FragColor *= lightColor;
#endif
#elif defined(USE_LIGHT)
L = normalize(L);
float surfNL = clamp(dot(surfN, L), 0.0, 1.0);
#if defined(USE_SHADOWMAP)
vec2 shadowTex = gl_FragCoord.xy * r_FBufScale;
float shadowValue = texture2D(u_ShadowMap, shadowTex).r;
// surfaces not facing the light are always shadowed
shadowValue *= step(0.0, dot(surfN, var_PrimaryLightDirection));
#if defined(SHADOWMAP_MODULATE)
//vec3 shadowColor = min(u_PrimaryLightAmbient, lightColor);
vec3 shadowColor = u_PrimaryLightAmbient * lightColor;
#if 0
// Only shadow when the world light is parallel to the primary light
shadowValue = 1.0 + (shadowValue - 1.0) * clamp(dot(L, var_PrimaryLightDirection), 0.0, 1.0);
#endif
lightColor = mix(shadowColor, lightColor, shadowValue);
#endif
#endif
#if defined(USE_LIGHTMAP) || defined(USE_LIGHT_VERTEX)
#if defined(USE_STANDARD_DELUXEMAP)
// Standard deluxe mapping treats the light sample as fully directed
// and doesn't compensate for light angle attenuation.
vec3 ambientColor = vec3(0.0);
#else
// Separate the light sample into directed and ambient parts.
//
// ambientMax - if the cosine of the angle between the surface
// normal and the light is below this value, the light
// is fully ambient.
// directedMax - if the cosine of the angle between the surface
// normal and the light is above this value, the light
// is fully directed.
const float ambientMax = 0.25;
const float directedMax = 0.5;
float directedScale = clamp((surfNL - ambientMax) / (directedMax - ambientMax), 0.0, 1.0);
// Scale the directed portion to compensate for the baked-in
// light angle attenuation.
directedScale /= max(surfNL, ambientMax);
#if defined(r_normalAmbient)
directedScale *= 1.0 - r_normalAmbient;
#endif
// Recover any unused light as ambient
vec3 ambientColor = lightColor;
lightColor *= directedScale;
ambientColor -= lightColor * surfNL;
#endif
#endif
float NL = clamp(dot(N, L), 0.0, 1.0);
float NE = clamp(dot(N, E), 0.0, 1.0);
float maxReflectance = u_MaterialInfo.x;
float shininess = u_MaterialInfo.y;
#if defined(USE_SPECULARMAP)
vec4 specularReflectance = texture2D(u_SpecularMap, texCoords);
specularReflectance.rgb *= maxReflectance;
shininess *= specularReflectance.a;
// adjust diffuse by specular reflectance, to maintain energy conservation
diffuseAlbedo.rgb *= vec3(1.0) - specularReflectance.rgb;
#endif
gl_FragColor.rgb = lightColor * NL * CalcDiffuse(diffuseAlbedo.rgb, N, L, E, NE, NL, shininess);
gl_FragColor.rgb += ambientDiff * ambientColor * diffuseAlbedo.rgb;
#if defined(USE_PRIMARY_LIGHT)
vec3 L2 = var_PrimaryLightDirection;
float NL2 = clamp(dot(N, L2), 0.0, 1.0);
#if defined(USE_SHADOWMAP)
gl_FragColor.rgb += u_PrimaryLightColor * shadowValue * NL2 * CalcDiffuse(diffuseAlbedo.rgb, N, L2, E, NE, NL2, shininess);
#else
gl_FragColor.rgb += u_PrimaryLightColor * NL2 * CalcDiffuse(diffuseAlbedo.rgb, N, L2, E, NE, NL2, shininess);
#endif
#endif
#if defined(USE_SPECULARMAP)
vec3 H = normalize(L + E);
float EH = clamp(dot(E, H), 0.0, 1.0);
float NH = clamp(dot(N, H), 0.0, 1.0);
gl_FragColor.rgb += lightColor * NL * CalcSpecular(specularReflectance.rgb, NH, NL, NE, EH, shininess);
#if defined(r_normalAmbient)
vec3 ambientHalf = normalize(surfN + E);
float ambientSpec = max(dot(ambientHalf, N) + 0.5, 0.0);
ambientSpec *= ambientSpec * 0.44;
gl_FragColor.rgb += specularReflectance.rgb * ambientSpec * ambientColor;
#endif
#if defined(USE_PRIMARY_LIGHT)
vec3 H2 = normalize(L2 + E);
float EH2 = clamp(dot(E, H2), 0.0, 1.0);
float NH2 = clamp(dot(N, H2), 0.0, 1.0);
#if defined(USE_SHADOWMAP)
gl_FragColor.rgb += u_PrimaryLightColor * shadowValue * NL2 * CalcSpecular(specularReflectance.rgb, NH2, NL2, NE, EH2, shininess);
#else
gl_FragColor.rgb += u_PrimaryLightColor * NL2 * CalcSpecular(specularReflectance.rgb, NH2, NL2, NE, EH2, shininess);
#endif
#endif
#endif
#else
gl_FragColor.rgb = diffuseAlbedo.rgb;
#endif
gl_FragColor.a = diffuseAlbedo.a;
gl_FragColor *= var_Color;
}

View file

@ -0,0 +1,245 @@
attribute vec4 attr_TexCoord0;
#if defined(USE_LIGHTMAP) || defined(USE_TCGEN)
attribute vec4 attr_TexCoord1;
#endif
attribute vec4 attr_Color;
attribute vec4 attr_Position;
attribute vec3 attr_Normal;
#if defined(USE_VERT_TANGENT_SPACE)
attribute vec3 attr_Tangent;
attribute vec3 attr_Bitangent;
#endif
#if defined(USE_VERTEX_ANIMATION)
attribute vec4 attr_Position2;
attribute vec3 attr_Normal2;
#if defined(USE_VERT_TANGENT_SPACE)
attribute vec3 attr_Tangent2;
attribute vec3 attr_Bitangent2;
#endif
#endif
#if defined(USE_LIGHT) && !defined(USE_LIGHT_VECTOR)
attribute vec3 attr_LightDirection;
#endif
#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)
uniform vec3 u_ViewOrigin;
#endif
#if defined(USE_TCGEN)
uniform int u_TCGen0;
uniform vec3 u_TCGen0Vector0;
uniform vec3 u_TCGen0Vector1;
#endif
#if defined(USE_TCMOD)
uniform vec4 u_DiffuseTexMatrix;
uniform vec4 u_DiffuseTexOffTurb;
#endif
uniform mat4 u_ModelViewProjectionMatrix;
uniform vec4 u_BaseColor;
uniform vec4 u_VertColor;
#if defined(USE_MODELMATRIX)
uniform mat4 u_ModelMatrix;
#endif
#if defined(USE_VERTEX_ANIMATION)
uniform float u_VertexLerp;
#endif
#if defined(USE_LIGHT_VECTOR)
uniform vec4 u_LightOrigin;
#if defined(USE_FAST_LIGHT)
uniform vec3 u_DirectedLight;
uniform vec3 u_AmbientLight;
uniform float u_LightRadius;
#endif
#endif
#if defined(USE_PRIMARY_LIGHT) || defined(USE_SHADOWMAP)
uniform vec4 u_PrimaryLightOrigin;
#endif
varying vec2 var_DiffuseTex;
#if defined(USE_LIGHTMAP)
varying vec2 var_LightTex;
#endif
#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT))
varying vec3 var_SampleToView;
#endif
varying vec4 var_Color;
#if defined(USE_NORMALMAP) && !defined(USE_VERT_TANGENT_SPACE)
varying vec3 var_Position;
#endif
#if !defined(USE_FAST_LIGHT)
varying vec3 var_Normal;
#if defined(USE_VERT_TANGENT_SPACE)
varying vec3 var_Tangent;
varying vec3 var_Bitangent;
#endif
#endif
#if defined(USE_LIGHT_VERTEX) && !defined(USE_FAST_LIGHT)
varying vec3 var_VertLight;
#endif
#if defined(USE_LIGHT) && !defined(USE_DELUXEMAP) && !defined(USE_FAST_LIGHT)
varying vec3 var_LightDirection;
#endif
#if defined(USE_PRIMARY_LIGHT) || defined(USE_SHADOWMAP)
varying vec3 var_PrimaryLightDirection;
#endif
#if defined(USE_TCGEN)
vec2 GenTexCoords(int TCGen, vec3 position, vec3 normal, vec3 TCGenVector0, vec3 TCGenVector1)
{
vec2 tex = attr_TexCoord0.st;
if (TCGen == TCGEN_LIGHTMAP)
{
tex = attr_TexCoord1.st;
}
else if (TCGen == TCGEN_ENVIRONMENT_MAPPED)
{
vec3 viewer = normalize(u_ViewOrigin - position);
tex = -reflect(viewer, normal).yz * vec2(0.5, -0.5) + 0.5;
}
else if (TCGen == TCGEN_VECTOR)
{
tex = vec2(dot(position, TCGenVector0), dot(position, TCGenVector1));
}
return tex;
}
#endif
#if defined(USE_TCMOD)
vec2 ModTexCoords(vec2 st, vec3 position, vec4 texMatrix, vec4 offTurb)
{
float amplitude = offTurb.z;
float phase = offTurb.w;
vec2 st2 = vec2(dot(st, texMatrix.xz), dot(st, texMatrix.yw)) + offTurb.xy;
vec3 offsetPos = vec3(0); //position / 1024.0;
offsetPos.x += offsetPos.z;
vec2 texOffset = sin((offsetPos.xy + vec2(phase)) * 2.0 * M_PI);
return st2 + texOffset * amplitude;
}
#endif
void main()
{
#if defined(USE_VERTEX_ANIMATION)
vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp);
vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp));
#if defined(USE_VERT_TANGENT_SPACE)
vec3 tangent = normalize(mix(attr_Tangent, attr_Tangent2, u_VertexLerp));
vec3 bitangent = normalize(mix(attr_Bitangent, attr_Bitangent2, u_VertexLerp));
#endif
#else
vec4 position = attr_Position;
vec3 normal = attr_Normal;
#if defined(USE_VERT_TANGENT_SPACE)
vec3 tangent = attr_Tangent;
vec3 bitangent = attr_Bitangent;
#endif
#endif
gl_Position = u_ModelViewProjectionMatrix * position;
#if (defined(USE_LIGHTMAP) || defined(USE_LIGHT_VERTEX)) && !defined(USE_DELUXEMAP) && !defined(USE_FAST_LIGHT)
vec3 L = attr_LightDirection;
#endif
#if defined(USE_MODELMATRIX)
position = u_ModelMatrix * position;
normal = (u_ModelMatrix * vec4(normal, 0.0)).xyz;
#if defined(USE_VERT_TANGENT_SPACE)
tangent = (u_ModelMatrix * vec4(tangent, 0.0)).xyz;
bitangent = (u_ModelMatrix * vec4(bitangent, 0.0)).xyz;
#endif
#if defined(USE_LIGHTMAP) && !defined(USE_DELUXEMAP) && !defined(USE_FAST_LIGHT)
L = (u_ModelMatrix * vec4(L, 0.0)).xyz;
#endif
#endif
#if defined(USE_NORMALMAP) && !defined(USE_VERT_TANGENT_SPACE)
var_Position = position.xyz;
#endif
#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT))
var_SampleToView = u_ViewOrigin - position.xyz;
#endif
#if defined(USE_TCGEN)
vec2 texCoords = GenTexCoords(u_TCGen0, position.xyz, normal, u_TCGen0Vector0, u_TCGen0Vector1);
#else
vec2 texCoords = attr_TexCoord0.st;
#endif
#if defined(USE_TCMOD)
var_DiffuseTex = ModTexCoords(texCoords, position.xyz, u_DiffuseTexMatrix, u_DiffuseTexOffTurb);
#else
var_DiffuseTex = texCoords;
#endif
#if defined(USE_LIGHTMAP)
var_LightTex = attr_TexCoord1.st;
#endif
#if !defined(USE_FAST_LIGHT)
var_Normal = normal;
#if defined(USE_VERT_TANGENT_SPACE)
var_Tangent = tangent;
var_Bitangent = bitangent;
#endif
#endif
#if defined(USE_LIGHT) && !defined(USE_DELUXEMAP)
#if defined(USE_LIGHT_VECTOR)
vec3 L = u_LightOrigin.xyz - (position.xyz * u_LightOrigin.w);
#endif
#if !defined(USE_FAST_LIGHT)
var_LightDirection = L;
#endif
#endif
#if defined(USE_LIGHT_VERTEX) && !defined(USE_FAST_LIGHT)
var_VertLight = u_VertColor.rgb * attr_Color.rgb;
var_Color.rgb = vec3(1.0);
var_Color.a = u_VertColor.a * attr_Color.a + u_BaseColor.a;
#else
var_Color = u_VertColor * attr_Color + u_BaseColor;
#endif
#if defined(USE_LIGHT_VECTOR) && defined(USE_FAST_LIGHT)
#if defined(USE_INVSQRLIGHT)
float intensity = 1.0 / dot(L, L);
#else
float intensity = clamp((1.0 - dot(L, L) / (u_LightRadius * u_LightRadius)) * 1.07, 0.0, 1.0);
#endif
float NL = clamp(dot(normal, normalize(L)), 0.0, 1.0);
var_Color.rgb *= u_DirectedLight * intensity * NL + u_AmbientLight;
#endif
#if defined(USE_PRIMARY_LIGHT) || defined(USE_SHADOWMAP)
var_PrimaryLightDirection = u_PrimaryLightOrigin.xyz - (position.xyz * u_PrimaryLightOrigin.w);
#endif
}

View file

@ -0,0 +1,98 @@
uniform sampler2D u_ShadowMap;
uniform vec3 u_LightForward;
uniform vec3 u_LightUp;
uniform vec3 u_LightRight;
uniform vec4 u_LightOrigin;
uniform float u_LightRadius;
varying vec3 var_Position;
varying vec3 var_Normal;
float sampleDistMap(sampler2D texMap, vec2 uv, float scale)
{
vec3 distv = texture2D(texMap, uv).xyz;
return dot(distv, vec3(1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0)) * scale;
}
void main()
{
vec3 lightToPos = var_Position - u_LightOrigin.xyz;
vec2 st = vec2(-dot(u_LightRight, lightToPos), dot(u_LightUp, lightToPos));
float fade = length(st);
#if defined(USE_DISCARD)
if (fade >= 1.0)
{
discard;
}
#endif
fade = clamp(8.0 - fade * 8.0, 0.0, 1.0);
st = st * 0.5 + vec2(0.5);
#if defined(USE_SOLID_PSHADOWS)
float intensity = max(sign(u_LightRadius - length(lightToPos)), 0.0);
#else
float intensity = clamp((1.0 - dot(lightToPos, lightToPos) / (u_LightRadius * u_LightRadius)) * 2.0, 0.0, 1.0);
#endif
float lightDist = length(lightToPos);
float dist;
#if defined(USE_DISCARD)
if (dot(u_LightForward, lightToPos) <= 0.0)
{
discard;
}
if (dot(var_Normal, lightToPos) > 0.0)
{
discard;
}
#else
intensity *= max(sign(dot(u_LightForward, lightToPos)), 0.0);
intensity *= max(sign(-dot(var_Normal, lightToPos)), 0.0);
#endif
intensity *= fade;
#if defined(USE_PCF)
float part;
dist = sampleDistMap(u_ShadowMap, st + vec2(-1.0/512.0, -1.0/512.0), u_LightRadius);
part = max(sign(lightDist - dist), 0.0);
dist = sampleDistMap(u_ShadowMap, st + vec2( 1.0/512.0, -1.0/512.0), u_LightRadius);
part += max(sign(lightDist - dist), 0.0);
dist = sampleDistMap(u_ShadowMap, st + vec2(-1.0/512.0, 1.0/512.0), u_LightRadius);
part += max(sign(lightDist - dist), 0.0);
dist = sampleDistMap(u_ShadowMap, st + vec2( 1.0/512.0, 1.0/512.0), u_LightRadius);
part += max(sign(lightDist - dist), 0.0);
#if defined(USE_DISCARD)
if (part <= 0.0)
{
discard;
}
#endif
intensity *= part * 0.25;
#else
dist = sampleDistMap(u_ShadowMap, st, u_LightRadius);
#if defined(USE_DISCARD)
if (lightDist - dist <= 0.0)
{
discard;
}
#endif
intensity *= max(sign(lightDist - dist), 0.0);
#endif
gl_FragColor.rgb = vec3(0);
gl_FragColor.a = clamp(intensity, 0.0, 0.75);
}

View file

@ -0,0 +1,17 @@
attribute vec4 attr_Position;
attribute vec3 attr_Normal;
uniform mat4 u_ModelViewProjectionMatrix;
varying vec3 var_Position;
varying vec3 var_Normal;
void main()
{
vec4 position = attr_Position;
gl_Position = u_ModelViewProjectionMatrix * position;
var_Position = position.xyz;
var_Normal = attr_Normal;
}

View file

@ -0,0 +1,41 @@
uniform vec4 u_LightOrigin;
uniform float u_LightRadius;
varying vec3 var_Position;
void main()
{
#if defined(USE_DEPTH)
float depth = length(u_LightOrigin.xyz - var_Position) / u_LightRadius;
#if 0
// 32 bit precision
const vec4 bitSh = vec4( 256 * 256 * 256, 256 * 256, 256, 1);
const vec4 bitMsk = vec4( 0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
vec4 comp;
comp = depth * bitSh;
comp.xyz = fract(comp.xyz);
comp -= comp.xxyz * bitMsk;
gl_FragColor = comp;
#endif
#if 1
// 24 bit precision
const vec3 bitSh = vec3( 256 * 256, 256, 1);
const vec3 bitMsk = vec3( 0, 1.0 / 256.0, 1.0 / 256.0);
vec3 comp;
comp = depth * bitSh;
comp.xy = fract(comp.xy);
comp -= comp.xxy * bitMsk;
gl_FragColor = vec4(comp, 1.0);
#endif
#if 0
// 8 bit precision
gl_FragColor = vec4(depth, depth, depth, 1);
#endif
#else
gl_FragColor = vec4(0, 0, 0, 1);
#endif
}

View file

@ -0,0 +1,89 @@
attribute vec4 attr_Position;
attribute vec3 attr_Normal;
attribute vec4 attr_TexCoord0;
//#if defined(USE_VERTEX_ANIMATION)
attribute vec4 attr_Position2;
attribute vec3 attr_Normal2;
//#endif
//#if defined(USE_DEFORM_VERTEXES)
uniform int u_DeformGen;
uniform float u_DeformParams[5];
//#endif
uniform float u_Time;
uniform mat4 u_ModelViewProjectionMatrix;
uniform mat4 u_ModelMatrix;
//#if defined(USE_VERTEX_ANIMATION)
uniform float u_VertexLerp;
//#endif
varying vec3 var_Position;
vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st)
{
if (u_DeformGen == 0)
{
return pos;
}
float base = u_DeformParams[0];
float amplitude = u_DeformParams[1];
float phase = u_DeformParams[2];
float frequency = u_DeformParams[3];
float spread = u_DeformParams[4];
if (u_DeformGen == DGEN_BULGE)
{
phase *= M_PI * 0.25 * st.x;
}
else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH)
{
phase += dot(pos.xyz, vec3(spread));
}
float value = phase + (u_Time * frequency);
float func;
if (u_DeformGen == DGEN_WAVE_SIN)
{
func = sin(value * 2.0 * M_PI);
}
else if (u_DeformGen == DGEN_WAVE_SQUARE)
{
func = sign(sin(value * 2.0 * M_PI));
}
else if (u_DeformGen == DGEN_WAVE_TRIANGLE)
{
func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0;
}
else if (u_DeformGen == DGEN_WAVE_SAWTOOTH)
{
func = fract(value);
}
else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH)
{
func = (1.0 - fract(value));
}
else if (u_DeformGen == DGEN_BULGE)
{
func = sin(value);
}
return pos + normal * (base + func * amplitude);
}
void main()
{
vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp);
vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp));
position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st);
gl_Position = u_ModelViewProjectionMatrix * position;
var_Position = (u_ModelMatrix * position).xyz;
}

View file

@ -0,0 +1,127 @@
uniform sampler2D u_ScreenDepthMap;
uniform sampler2D u_ShadowMap;
#if defined(USE_SHADOW_CASCADE)
uniform sampler2D u_ShadowMap2;
uniform sampler2D u_ShadowMap3;
#endif
uniform mat4 u_ShadowMvp;
#if defined(USE_SHADOW_CASCADE)
uniform mat4 u_ShadowMvp2;
uniform mat4 u_ShadowMvp3;
#endif
uniform vec3 u_ViewOrigin;
uniform vec4 u_ViewInfo; // zfar / znear, zfar
varying vec2 var_DepthTex;
varying vec3 var_ViewDir;
// Input: It uses texture coords as the random number seed.
// Output: Random number: [0,1), that is between 0.0 and 0.999999... inclusive.
// Author: Michael Pohoreski
// Copyright: Copyleft 2012 :-)
// Source: http://stackoverflow.com/questions/5149544/can-i-generate-a-random-number-inside-a-pixel-shader
float random( const vec2 p )
{
// We need irrationals for pseudo randomness.
// Most (all?) known transcendental numbers will (generally) work.
const vec2 r = vec2(
23.1406926327792690, // e^pi (Gelfond's constant)
2.6651441426902251); // 2^sqrt(2) (Gelfond-Schneider constant)
//return fract( cos( mod( 123456789., 1e-7 + 256. * dot(p,r) ) ) );
return mod( 123456789., 1e-7 + 256. * dot(p,r) );
}
float PCF(const sampler2D shadowmap, const vec2 st, const float dist)
{
float mult;
float scale = 2.0 / r_shadowMapSize;
#if defined(USE_SHADOW_FILTER)
float r = random(var_DepthTex.xy);
float sinr = sin(r) * scale;
float cosr = cos(r) * scale;
mat2 rmat = mat2(cosr, sinr, -sinr, cosr);
mult = step(dist, texture2D(shadowmap, st + rmat * vec2(-0.7055767, 0.196515)).r);
mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.3524343, -0.7791386)).r);
mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.2391056, 0.9189604)).r);
#if defined(USE_SHADOW_FILTER2)
mult += step(dist, texture2D(shadowmap, st + rmat * vec2(-0.07580382, -0.09224417)).r);
mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.5784913, -0.002528916)).r);
mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.192888, 0.4064181)).r);
mult += step(dist, texture2D(shadowmap, st + rmat * vec2(-0.6335801, -0.5247476)).r);
mult += step(dist, texture2D(shadowmap, st + rmat * vec2(-0.5579782, 0.7491854)).r);
mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.7320465, 0.6317794)).r);
mult *= 0.11111;
#else
mult *= 0.33333;
#endif
#else
mult = step(dist, texture2D(shadowmap, st).r);
#endif
return mult;
}
float getLinearDepth(sampler2D depthMap, vec2 tex, float zFarDivZNear)
{
float sampleZDivW = texture2D(depthMap, tex).r;
return 1.0 / mix(zFarDivZNear, 1.0, sampleZDivW);
}
void main()
{
float result;
float depth = getLinearDepth(u_ScreenDepthMap, var_DepthTex, u_ViewInfo.x);
float sampleZ = u_ViewInfo.y * depth;
vec4 biasPos = vec4(u_ViewOrigin + var_ViewDir * depth * 0.99, 1.0);
vec4 shadowpos = u_ShadowMvp * biasPos;
#if defined(USE_SHADOW_CASCADE)
const float fadeTo = 1.0;
result = fadeTo;
#else
result = 0.0;
#endif
if (all(lessThanEqual(abs(shadowpos.xyz), vec3(abs(shadowpos.w)))))
{
shadowpos.xyz = shadowpos.xyz / shadowpos.w * 0.5 + 0.5;
result = PCF(u_ShadowMap, shadowpos.xy, shadowpos.z);
}
#if defined(USE_SHADOW_CASCADE)
else
{
shadowpos = u_ShadowMvp2 * biasPos;
if (all(lessThanEqual(abs(shadowpos.xyz), vec3(abs(shadowpos.w)))))
{
shadowpos.xyz = shadowpos.xyz / shadowpos.w * 0.5 + 0.5;
result = PCF(u_ShadowMap2, shadowpos.xy, shadowpos.z);
}
else
{
shadowpos = u_ShadowMvp3 * biasPos;
if (all(lessThanEqual(abs(shadowpos.xyz), vec3(abs(shadowpos.w)))))
{
shadowpos.xyz = shadowpos.xyz / shadowpos.w * 0.5 + 0.5;
result = PCF(u_ShadowMap3, shadowpos.xy, shadowpos.z);
float fade = clamp(sampleZ / r_shadowCascadeZFar * 10.0 - 9.0, 0.0, 1.0);
result = mix(result, fadeTo, fade);
}
}
}
#endif
gl_FragColor = vec4(vec3(result), 1.0);
}

View file

@ -0,0 +1,18 @@
attribute vec4 attr_Position;
attribute vec4 attr_TexCoord0;
uniform vec3 u_ViewForward;
uniform vec3 u_ViewLeft;
uniform vec3 u_ViewUp;
uniform vec4 u_ViewInfo; // zfar / znear
varying vec2 var_DepthTex;
varying vec3 var_ViewDir;
void main()
{
gl_Position = attr_Position;
vec2 screenCoords = gl_Position.xy / gl_Position.w;
var_DepthTex = attr_TexCoord0.xy;
var_ViewDir = u_ViewForward + u_ViewLeft * -screenCoords.x + u_ViewUp * screenCoords.y;
}

View file

@ -0,0 +1,86 @@
uniform sampler2D u_ScreenDepthMap;
uniform vec4 u_ViewInfo; // zfar / znear, zfar
varying vec2 var_ScreenTex;
vec2 poissonDisc[9] = vec2[9](
vec2(-0.7055767, 0.196515), vec2(0.3524343, -0.7791386),
vec2(0.2391056, 0.9189604), vec2(-0.07580382, -0.09224417),
vec2(0.5784913, -0.002528916), vec2(0.192888, 0.4064181),
vec2(-0.6335801, -0.5247476), vec2(-0.5579782, 0.7491854),
vec2(0.7320465, 0.6317794)
);
// Input: It uses texture coords as the random number seed.
// Output: Random number: [0,1), that is between 0.0 and 0.999999... inclusive.
// Author: Michael Pohoreski
// Copyright: Copyleft 2012 :-)
// Source: http://stackoverflow.com/questions/5149544/can-i-generate-a-random-number-inside-a-pixel-shader
float random( const vec2 p )
{
// We need irrationals for pseudo randomness.
// Most (all?) known transcendental numbers will (generally) work.
const vec2 r = vec2(
23.1406926327792690, // e^pi (Gelfond's constant)
2.6651441426902251); // 2^sqrt(2) (Gelfond-Schneider constant)
//return fract( cos( mod( 123456789., 1e-7 + 256. * dot(p,r) ) ) );
return mod( 123456789., 1e-7 + 256. * dot(p,r) );
}
mat2 randomRotation( const vec2 p )
{
float r = random(p);
float sinr = sin(r);
float cosr = cos(r);
return mat2(cosr, sinr, -sinr, cosr);
}
float getLinearDepth(sampler2D depthMap, const vec2 tex, const float zFarDivZNear)
{
float sampleZDivW = texture2D(depthMap, tex).r;
return 1.0 / mix(zFarDivZNear, 1.0, sampleZDivW);
}
float ambientOcclusion(sampler2D depthMap, const vec2 tex, const float zFarDivZNear, const float zFar)
{
float result = 0;
float sampleZ = zFar * getLinearDepth(depthMap, tex, zFarDivZNear);
vec2 expectedSlope = vec2(dFdx(sampleZ), dFdy(sampleZ)) / vec2(dFdx(tex.x), dFdy(tex.y));
if (length(expectedSlope) > 5000.0)
return 1.0;
vec2 offsetScale = vec2(3.0 / sampleZ);
mat2 rmat = randomRotation(tex);
int i;
for (i = 0; i < 3; i++)
{
vec2 offset = rmat * poissonDisc[i] * offsetScale;
float sampleZ2 = zFar * getLinearDepth(depthMap, tex + offset, zFarDivZNear);
if (abs(sampleZ - sampleZ2) > 20.0)
result += 1.0;
else
{
float expectedZ = sampleZ + dot(expectedSlope, offset);
result += step(expectedZ - 1.0, sampleZ2);
}
}
result *= 0.33333;
return result;
}
void main()
{
float result = ambientOcclusion(u_ScreenDepthMap, var_ScreenTex, u_ViewInfo.x, u_ViewInfo.y);
gl_FragColor = vec4(vec3(result), 1.0);
}

View file

@ -0,0 +1,12 @@
attribute vec4 attr_Position;
attribute vec4 attr_TexCoord0;
varying vec2 var_ScreenTex;
void main()
{
gl_Position = attr_Position;
var_ScreenTex = attr_TexCoord0.xy;
//vec2 screenCoords = gl_Position.xy / gl_Position.w;
//var_ScreenTex = screenCoords * 0.5 + 0.5;
}

View file

@ -0,0 +1,12 @@
#version 120
uniform sampler2D u_DiffuseMap;
uniform vec4 u_Color;
varying vec2 var_Tex1;
void main()
{
gl_FragColor = texture2D(u_DiffuseMap, var_Tex1) * u_Color;
}

View file

@ -0,0 +1,15 @@
#version 120
attribute vec4 attr_Position;
attribute vec4 attr_TexCoord0;
uniform mat4 u_ModelViewProjectionMatrix;
varying vec2 var_Tex1;
void main()
{
gl_Position = u_ModelViewProjectionMatrix * attr_Position;
var_Tex1 = attr_TexCoord0.st;
}

View file

@ -0,0 +1,48 @@
uniform sampler2D u_TextureMap;
uniform sampler2D u_LevelsMap;
uniform vec4 u_Color;
uniform vec2 u_AutoExposureMinMax;
uniform vec3 u_ToneMinAvgMaxLinear;
varying vec2 var_TexCoords;
const vec3 LUMINANCE_VECTOR = vec3(0.2125, 0.7154, 0.0721); //vec3(0.299, 0.587, 0.114);
vec3 FilmicTonemap(vec3 x)
{
const float SS = 0.22; // Shoulder Strength
const float LS = 0.30; // Linear Strength
const float LA = 0.10; // Linear Angle
const float TS = 0.20; // Toe Strength
const float TAN = 0.01; // Toe Angle Numerator
const float TAD = 0.30; // Toe Angle Denominator
vec3 SSxx = SS * x * x;
vec3 LSx = LS * x;
vec3 LALSx = LSx * LA;
return ((SSxx + LALSx + TS * TAN) / (SSxx + LSx + TS * TAD)) - TAN / TAD;
//return ((x*(SS*x+LA*LS)+TS*TAN)/(x*(SS*x+LS)+TS*TAD)) - TAN/TAD;
}
void main()
{
vec4 color = texture2D(u_TextureMap, var_TexCoords) * u_Color;
vec3 minAvgMax = texture2D(u_LevelsMap, var_TexCoords).rgb;
vec3 logMinAvgMaxLum = clamp(minAvgMax * 20.0 - 10.0, -u_AutoExposureMinMax.y, -u_AutoExposureMinMax.x);
float avgLum = exp2(logMinAvgMaxLum.y);
//float maxLum = exp2(logMinAvgMaxLum.z);
color.rgb *= u_ToneMinAvgMaxLinear.y / avgLum;
color.rgb = max(vec3(0.0), color.rgb - vec3(u_ToneMinAvgMaxLinear.x));
vec3 fWhite = 1.0 / FilmicTonemap(vec3(u_ToneMinAvgMaxLinear.z - u_ToneMinAvgMaxLinear.x));
color.rgb = FilmicTonemap(color.rgb) * fWhite;
gl_FragColor = clamp(color, 0.0, 1.0);
}

View file

@ -0,0 +1,13 @@
attribute vec4 attr_Position;
attribute vec4 attr_TexCoord0;
uniform mat4 u_ModelViewProjectionMatrix;
varying vec2 var_TexCoords;
void main()
{
gl_Position = u_ModelViewProjectionMatrix * attr_Position;
var_TexCoords = attr_TexCoord0.st;
}

View file

@ -0,0 +1,656 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "tr_local.h"
/*
All bones should be an identity orientation to display the mesh exactly
as it is specified.
For all other frames, the bones represent the transformation from the
orientation of the bone in the base frame to the orientation in this
frame.
*/
/*
==============
R_AddAnimSurfaces
==============
*/
void R_AddAnimSurfaces( trRefEntity_t *ent ) {
md4Header_t *header;
md4Surface_t *surface;
md4LOD_t *lod;
shader_t *shader;
int i;
header = (md4Header_t *) tr.currentModel->modelData;
lod = (md4LOD_t *)( (byte *)header + header->ofsLODs );
surface = (md4Surface_t *)( (byte *)lod + lod->ofsSurfaces );
for ( i = 0 ; i < lod->numSurfaces ; i++ ) {
shader = R_GetShaderByHandle( surface->shaderIndex );
R_AddDrawSurf( (void *)surface, shader, 0 /*fogNum*/, qfalse, qfalse );
surface = (md4Surface_t *)( (byte *)surface + surface->ofsEnd );
}
}
/*
==============
RB_SurfaceAnim
==============
*/
void RB_SurfaceAnim( md4Surface_t *surface ) {
int i, j, k;
float frontlerp, backlerp;
int *triangles;
int indexes;
int baseIndex, baseVertex;
int numVerts;
md4Vertex_t *v;
md4Bone_t bones[MD4_MAX_BONES];
md4Bone_t *bonePtr, *bone;
md4Header_t *header;
md4Frame_t *frame;
md4Frame_t *oldFrame;
int frameSize;
if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) {
backlerp = 0;
frontlerp = 1;
} else {
backlerp = backEnd.currentEntity->e.backlerp;
frontlerp = 1.0f - backlerp;
}
header = (md4Header_t *)((byte *)surface + surface->ofsHeader);
frameSize = (size_t)( &((md4Frame_t *)0)->bones[ header->numBones ] );
frame = (md4Frame_t *)((byte *)header + header->ofsFrames +
backEnd.currentEntity->e.frame * frameSize );
oldFrame = (md4Frame_t *)((byte *)header + header->ofsFrames +
backEnd.currentEntity->e.oldframe * frameSize );
RB_CheckOverflow( surface->numVerts, surface->numTriangles * 3 );
triangles = (int *) ((byte *)surface + surface->ofsTriangles);
indexes = surface->numTriangles * 3;
baseIndex = tess.numIndexes;
baseVertex = tess.numVertexes;
for (j = 0 ; j < indexes ; j++) {
tess.indexes[baseIndex + j] = baseIndex + triangles[j];
}
tess.numIndexes += indexes;
//
// lerp all the needed bones
//
if ( !backlerp ) {
// no lerping needed
bonePtr = frame->bones;
} else {
bonePtr = bones;
for ( i = 0 ; i < header->numBones*12 ; i++ ) {
((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i]
+ backlerp * ((float *)oldFrame->bones)[i];
}
}
//
// deform the vertexes by the lerped bones
//
numVerts = surface->numVerts;
// FIXME
// This makes TFC's skeletons work. Shouldn't be necessary anymore, but left
// in for reference.
//v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts + 12);
v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts);
for ( j = 0; j < numVerts; j++ ) {
vec3_t tempVert, tempNormal;
md4Weight_t *w;
VectorClear( tempVert );
VectorClear( tempNormal );
w = v->weights;
for ( k = 0 ; k < v->numWeights ; k++, w++ ) {
bone = bonePtr + w->boneIndex;
tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] );
tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] );
tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] );
tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal );
tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal );
tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal );
}
tess.xyz[baseVertex + j][0] = tempVert[0];
tess.xyz[baseVertex + j][1] = tempVert[1];
tess.xyz[baseVertex + j][2] = tempVert[2];
tess.normal[baseVertex + j][0] = tempNormal[0];
tess.normal[baseVertex + j][1] = tempNormal[1];
tess.normal[baseVertex + j][2] = tempNormal[2];
tess.texCoords[baseVertex + j][0][0] = v->texCoords[0];
tess.texCoords[baseVertex + j][0][1] = v->texCoords[1];
// FIXME
// This makes TFC's skeletons work. Shouldn't be necessary anymore, but left
// in for reference.
//v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 );
v = (md4Vertex_t *)&v->weights[v->numWeights];
}
tess.numVertexes += surface->numVerts;
}
// copied and adapted from tr_mesh.c
/*
=============
R_MDRCullModel
=============
*/
static int R_MDRCullModel( mdrHeader_t *header, trRefEntity_t *ent ) {
vec3_t bounds[2];
mdrFrame_t *oldFrame, *newFrame;
int i, frameSize;
frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] );
// compute frame pointers
newFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame);
oldFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.oldframe);
// cull bounding sphere ONLY if this is not an upscaled entity
if ( !ent->e.nonNormalizedAxes )
{
if ( ent->e.frame == ent->e.oldframe )
{
switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) )
{
// Ummm... yeah yeah I know we don't really have an md3 here.. but we pretend
// we do. After all, the purpose of md4s are not that different, are they?
case CULL_OUT:
tr.pc.c_sphere_cull_md3_out++;
return CULL_OUT;
case CULL_IN:
tr.pc.c_sphere_cull_md3_in++;
return CULL_IN;
case CULL_CLIP:
tr.pc.c_sphere_cull_md3_clip++;
break;
}
}
else
{
int sphereCull, sphereCullB;
sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius );
if ( newFrame == oldFrame ) {
sphereCullB = sphereCull;
} else {
sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius );
}
if ( sphereCull == sphereCullB )
{
if ( sphereCull == CULL_OUT )
{
tr.pc.c_sphere_cull_md3_out++;
return CULL_OUT;
}
else if ( sphereCull == CULL_IN )
{
tr.pc.c_sphere_cull_md3_in++;
return CULL_IN;
}
else
{
tr.pc.c_sphere_cull_md3_clip++;
}
}
}
}
// calculate a bounding box in the current coordinate system
for (i = 0 ; i < 3 ; i++) {
bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i];
bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i];
}
switch ( R_CullLocalBox( bounds ) )
{
case CULL_IN:
tr.pc.c_box_cull_md3_in++;
return CULL_IN;
case CULL_CLIP:
tr.pc.c_box_cull_md3_clip++;
return CULL_CLIP;
case CULL_OUT:
default:
tr.pc.c_box_cull_md3_out++;
return CULL_OUT;
}
}
/*
=================
R_MDRComputeFogNum
=================
*/
int R_MDRComputeFogNum( mdrHeader_t *header, trRefEntity_t *ent ) {
int i, j;
fog_t *fog;
mdrFrame_t *mdrFrame;
vec3_t localOrigin;
int frameSize;
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
return 0;
}
frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] );
// FIXME: non-normalized axis issues
mdrFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame);
VectorAdd( ent->e.origin, mdrFrame->localOrigin, localOrigin );
for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
fog = &tr.world->fogs[i];
for ( j = 0 ; j < 3 ; j++ ) {
if ( localOrigin[j] - mdrFrame->radius >= fog->bounds[1][j] ) {
break;
}
if ( localOrigin[j] + mdrFrame->radius <= fog->bounds[0][j] ) {
break;
}
}
if ( j == 3 ) {
return i;
}
}
return 0;
}
/*
==============
R_MDRAddAnimSurfaces
==============
*/
// much stuff in there is just copied from R_AddMd3Surfaces in tr_mesh.c
void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) {
mdrHeader_t *header;
mdrSurface_t *surface;
mdrLOD_t *lod;
shader_t *shader;
skin_t *skin;
int i, j;
int lodnum = 0;
int fogNum = 0;
int cull;
qboolean personalModel;
header = (mdrHeader_t *) tr.currentModel->modelData;
personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal;
if ( ent->e.renderfx & RF_WRAP_FRAMES )
{
ent->e.frame %= header->numFrames;
ent->e.oldframe %= header->numFrames;
}
//
// Validate the frames so there is no chance of a crash.
// This will write directly into the entity structure, so
// when the surfaces are rendered, they don't need to be
// range checked again.
//
if ((ent->e.frame >= header->numFrames)
|| (ent->e.frame < 0)
|| (ent->e.oldframe >= header->numFrames)
|| (ent->e.oldframe < 0) )
{
ri.Printf( PRINT_DEVELOPER, "R_MDRAddAnimSurfaces: no such frame %d to %d for '%s'\n",
ent->e.oldframe, ent->e.frame, tr.currentModel->name );
ent->e.frame = 0;
ent->e.oldframe = 0;
}
//
// cull the entire model if merged bounding box of both frames
// is outside the view frustum.
//
cull = R_MDRCullModel (header, ent);
if ( cull == CULL_OUT ) {
return;
}
// figure out the current LOD of the model we're rendering, and set the lod pointer respectively.
lodnum = R_ComputeLOD(ent);
// check whether this model has as that many LODs at all. If not, try the closest thing we got.
if(header->numLODs <= 0)
return;
if(header->numLODs <= lodnum)
lodnum = header->numLODs - 1;
lod = (mdrLOD_t *)( (byte *)header + header->ofsLODs);
for(i = 0; i < lodnum; i++)
{
lod = (mdrLOD_t *) ((byte *) lod + lod->ofsEnd);
}
// set up lighting
if ( !personalModel || r_shadows->integer > 1 )
{
R_SetupEntityLighting( &tr.refdef, ent );
}
// fogNum?
fogNum = R_MDRComputeFogNum( header, ent );
surface = (mdrSurface_t *)( (byte *)lod + lod->ofsSurfaces );
for ( i = 0 ; i < lod->numSurfaces ; i++ )
{
if(ent->e.customShader)
shader = R_GetShaderByHandle(ent->e.customShader);
else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins)
{
skin = R_GetSkinByHandle(ent->e.customSkin);
shader = tr.defaultShader;
for(j = 0; j < skin->numSurfaces; j++)
{
if (!strcmp(skin->surfaces[j]->name, surface->name))
{
shader = skin->surfaces[j]->shader;
break;
}
}
}
else if(surface->shaderIndex > 0)
shader = R_GetShaderByHandle( surface->shaderIndex );
else
shader = tr.defaultShader;
// we will add shadows even if the main object isn't visible in the view
// stencil shadows can't do personal models unless I polyhedron clip
if ( !personalModel
&& r_shadows->integer == 2
&& fogNum == 0
&& !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) )
&& shader->sort == SS_OPAQUE )
{
R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse, qfalse );
}
// projection shadows work fine with personal models
if ( r_shadows->integer == 3
&& fogNum == 0
&& (ent->e.renderfx & RF_SHADOW_PLANE )
&& shader->sort == SS_OPAQUE )
{
R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse, qfalse );
}
if (!personalModel)
R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse, qfalse );
surface = (mdrSurface_t *)( (byte *)surface + surface->ofsEnd );
}
}
/*
==============
RB_MDRSurfaceAnim
==============
*/
void RB_MDRSurfaceAnim( md4Surface_t *surface )
{
int i, j, k;
float frontlerp, backlerp;
int *triangles;
int indexes;
int baseIndex, baseVertex;
int numVerts;
mdrVertex_t *v;
mdrHeader_t *header;
mdrFrame_t *frame;
mdrFrame_t *oldFrame;
mdrBone_t bones[MD4_MAX_BONES], *bonePtr, *bone;
int frameSize;
// don't lerp if lerping off, or this is the only frame, or the last frame...
//
if (backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame)
{
backlerp = 0; // if backlerp is 0, lerping is off and frontlerp is never used
frontlerp = 1;
}
else
{
backlerp = backEnd.currentEntity->e.backlerp;
frontlerp = 1.0f - backlerp;
}
header = (mdrHeader_t *)((byte *)surface + surface->ofsHeader);
frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] );
frame = (mdrFrame_t *)((byte *)header + header->ofsFrames +
backEnd.currentEntity->e.frame * frameSize );
oldFrame = (mdrFrame_t *)((byte *)header + header->ofsFrames +
backEnd.currentEntity->e.oldframe * frameSize );
RB_CheckOverflow( surface->numVerts, surface->numTriangles );
triangles = (int *) ((byte *)surface + surface->ofsTriangles);
indexes = surface->numTriangles * 3;
baseIndex = tess.numIndexes;
baseVertex = tess.numVertexes;
// Set up all triangles.
for (j = 0 ; j < indexes ; j++)
{
tess.indexes[baseIndex + j] = baseVertex + triangles[j];
}
tess.numIndexes += indexes;
//
// lerp all the needed bones
//
if ( !backlerp )
{
// no lerping needed
bonePtr = frame->bones;
}
else
{
bonePtr = bones;
for ( i = 0 ; i < header->numBones*12 ; i++ )
{
((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] + backlerp * ((float *)oldFrame->bones)[i];
}
}
//
// deform the vertexes by the lerped bones
//
numVerts = surface->numVerts;
v = (mdrVertex_t *) ((byte *)surface + surface->ofsVerts);
for ( j = 0; j < numVerts; j++ )
{
vec3_t tempVert, tempNormal;
mdrWeight_t *w;
VectorClear( tempVert );
VectorClear( tempNormal );
w = v->weights;
for ( k = 0 ; k < v->numWeights ; k++, w++ )
{
bone = bonePtr + w->boneIndex;
tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] );
tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] );
tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] );
tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal );
tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal );
tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal );
}
tess.xyz[baseVertex + j][0] = tempVert[0];
tess.xyz[baseVertex + j][1] = tempVert[1];
tess.xyz[baseVertex + j][2] = tempVert[2];
tess.normal[baseVertex + j][0] = tempNormal[0];
tess.normal[baseVertex + j][1] = tempNormal[1];
tess.normal[baseVertex + j][2] = tempNormal[2];
tess.texCoords[baseVertex + j][0][0] = v->texCoords[0];
tess.texCoords[baseVertex + j][0][1] = v->texCoords[1];
v = (mdrVertex_t *)&v->weights[v->numWeights];
}
tess.numVertexes += surface->numVerts;
}
#define MC_MASK_X ((1<<(MC_BITS_X))-1)
#define MC_MASK_Y ((1<<(MC_BITS_Y))-1)
#define MC_MASK_Z ((1<<(MC_BITS_Z))-1)
#define MC_MASK_VECT ((1<<(MC_BITS_VECT))-1)
#define MC_SCALE_VECT (1.0f/(float)((1<<(MC_BITS_VECT-1))-2))
#define MC_POS_X (0)
#define MC_SHIFT_X (0)
#define MC_POS_Y ((((MC_BITS_X))/8))
#define MC_SHIFT_Y ((((MC_BITS_X)%8)))
#define MC_POS_Z ((((MC_BITS_X+MC_BITS_Y))/8))
#define MC_SHIFT_Z ((((MC_BITS_X+MC_BITS_Y)%8)))
#define MC_POS_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z))/8))
#define MC_SHIFT_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z)%8)))
#define MC_POS_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT))/8))
#define MC_SHIFT_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT)%8)))
#define MC_POS_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2))/8))
#define MC_SHIFT_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2)%8)))
#define MC_POS_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3))/8))
#define MC_SHIFT_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3)%8)))
#define MC_POS_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4))/8))
#define MC_SHIFT_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4)%8)))
#define MC_POS_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5))/8))
#define MC_SHIFT_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5)%8)))
#define MC_POS_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6))/8))
#define MC_SHIFT_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6)%8)))
#define MC_POS_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7))/8))
#define MC_SHIFT_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7)%8)))
#define MC_POS_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8))/8))
#define MC_SHIFT_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8)%8)))
void MC_UnCompress(float mat[3][4],const unsigned char * comp)
{
int val;
val=(int)((unsigned short *)(comp))[0];
val-=1<<(MC_BITS_X-1);
mat[0][3]=((float)(val))*MC_SCALE_X;
val=(int)((unsigned short *)(comp))[1];
val-=1<<(MC_BITS_Y-1);
mat[1][3]=((float)(val))*MC_SCALE_Y;
val=(int)((unsigned short *)(comp))[2];
val-=1<<(MC_BITS_Z-1);
mat[2][3]=((float)(val))*MC_SCALE_Z;
val=(int)((unsigned short *)(comp))[3];
val-=1<<(MC_BITS_VECT-1);
mat[0][0]=((float)(val))*MC_SCALE_VECT;
val=(int)((unsigned short *)(comp))[4];
val-=1<<(MC_BITS_VECT-1);
mat[0][1]=((float)(val))*MC_SCALE_VECT;
val=(int)((unsigned short *)(comp))[5];
val-=1<<(MC_BITS_VECT-1);
mat[0][2]=((float)(val))*MC_SCALE_VECT;
val=(int)((unsigned short *)(comp))[6];
val-=1<<(MC_BITS_VECT-1);
mat[1][0]=((float)(val))*MC_SCALE_VECT;
val=(int)((unsigned short *)(comp))[7];
val-=1<<(MC_BITS_VECT-1);
mat[1][1]=((float)(val))*MC_SCALE_VECT;
val=(int)((unsigned short *)(comp))[8];
val-=1<<(MC_BITS_VECT-1);
mat[1][2]=((float)(val))*MC_SCALE_VECT;
val=(int)((unsigned short *)(comp))[9];
val-=1<<(MC_BITS_VECT-1);
mat[2][0]=((float)(val))*MC_SCALE_VECT;
val=(int)((unsigned short *)(comp))[10];
val-=1<<(MC_BITS_VECT-1);
mat[2][1]=((float)(val))*MC_SCALE_VECT;
val=(int)((unsigned short *)(comp))[11];
val-=1<<(MC_BITS_VECT-1);
mat[2][2]=((float)(val))*MC_SCALE_VECT;
}

File diff suppressed because it is too large Load diff

3554
code/renderergl2/tr_bsp.c Normal file

File diff suppressed because it is too large Load diff

583
code/renderergl2/tr_cmds.c Normal file
View file

@ -0,0 +1,583 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "tr_local.h"
volatile renderCommandList_t *renderCommandList;
/*
=====================
R_PerformanceCounters
=====================
*/
void R_PerformanceCounters( void ) {
if ( !r_speeds->integer ) {
// clear the counters even if we aren't printing
Com_Memset( &tr.pc, 0, sizeof( tr.pc ) );
Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) );
return;
}
if (r_speeds->integer == 1) {
ri.Printf (PRINT_ALL, "%i/%i/%i shaders/batches/surfs %i leafs %i verts %i/%i tris %.2f mtex %.2f dc\n",
backEnd.pc.c_shaders, backEnd.pc.c_surfBatches, backEnd.pc.c_surfaces, tr.pc.c_leafs, backEnd.pc.c_vertexes,
backEnd.pc.c_indexes/3, backEnd.pc.c_totalIndexes/3,
R_SumOfUsedImages()/(1000000.0f), backEnd.pc.c_overDraw / (float)(glConfig.vidWidth * glConfig.vidHeight) );
} else if (r_speeds->integer == 2) {
ri.Printf (PRINT_ALL, "(patch) %i sin %i sclip %i sout %i bin %i bclip %i bout\n",
tr.pc.c_sphere_cull_patch_in, tr.pc.c_sphere_cull_patch_clip, tr.pc.c_sphere_cull_patch_out,
tr.pc.c_box_cull_patch_in, tr.pc.c_box_cull_patch_clip, tr.pc.c_box_cull_patch_out );
ri.Printf (PRINT_ALL, "(md3) %i sin %i sclip %i sout %i bin %i bclip %i bout\n",
tr.pc.c_sphere_cull_md3_in, tr.pc.c_sphere_cull_md3_clip, tr.pc.c_sphere_cull_md3_out,
tr.pc.c_box_cull_md3_in, tr.pc.c_box_cull_md3_clip, tr.pc.c_box_cull_md3_out );
} else if (r_speeds->integer == 3) {
ri.Printf (PRINT_ALL, "viewcluster: %i\n", tr.viewCluster );
} else if (r_speeds->integer == 4) {
if ( backEnd.pc.c_dlightVertexes ) {
ri.Printf (PRINT_ALL, "dlight srf:%i culled:%i verts:%i tris:%i\n",
tr.pc.c_dlightSurfaces, tr.pc.c_dlightSurfacesCulled,
backEnd.pc.c_dlightVertexes, backEnd.pc.c_dlightIndexes / 3 );
}
}
else if (r_speeds->integer == 5 )
{
ri.Printf( PRINT_ALL, "zFar: %.0f\n", tr.viewParms.zFar );
}
else if (r_speeds->integer == 6 )
{
ri.Printf( PRINT_ALL, "flare adds:%i tests:%i renders:%i\n",
backEnd.pc.c_flareAdds, backEnd.pc.c_flareTests, backEnd.pc.c_flareRenders );
}
else if (r_speeds->integer == 7 )
{
ri.Printf( PRINT_ALL, "VBO draws: static %i dynamic %i\nMultidraws: %i merged %i\n",
backEnd.pc.c_staticVboDraws, backEnd.pc.c_dynamicVboDraws, backEnd.pc.c_multidraws, backEnd.pc.c_multidrawsMerged );
ri.Printf( PRINT_ALL, "GLSL binds: %i draws: gen %i light %i fog %i dlight %i\n",
backEnd.pc.c_glslShaderBinds, backEnd.pc.c_genericDraws, backEnd.pc.c_lightallDraws, backEnd.pc.c_fogDraws, backEnd.pc.c_dlightDraws);
}
Com_Memset( &tr.pc, 0, sizeof( tr.pc ) );
Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) );
}
/*
====================
R_IssueRenderCommands
====================
*/
void R_IssueRenderCommands( qboolean runPerformanceCounters ) {
renderCommandList_t *cmdList;
cmdList = &backEndData->commands;
assert(cmdList);
// add an end-of-list command
*(int *)(cmdList->cmds + cmdList->used) = RC_END_OF_LIST;
// clear it out, in case this is a sync and not a buffer flip
cmdList->used = 0;
if ( runPerformanceCounters ) {
R_PerformanceCounters();
}
// actually start the commands going
if ( !r_skipBackEnd->integer ) {
// let it start on the new batch
RB_ExecuteRenderCommands( cmdList->cmds );
}
}
/*
====================
R_IssuePendingRenderCommands
Issue any pending commands and wait for them to complete.
====================
*/
void R_IssuePendingRenderCommands( void ) {
if ( !tr.registered ) {
return;
}
R_IssueRenderCommands( qfalse );
}
/*
============
R_GetCommandBuffer
make sure there is enough command space
============
*/
void *R_GetCommandBuffer( int bytes ) {
renderCommandList_t *cmdList;
cmdList = &backEndData->commands;
bytes = PAD(bytes, sizeof(void *));
// always leave room for the end of list command
if ( cmdList->used + bytes + 4 > MAX_RENDER_COMMANDS ) {
if ( bytes > MAX_RENDER_COMMANDS - 4 ) {
ri.Error( ERR_FATAL, "R_GetCommandBuffer: bad size %i", bytes );
}
// if we run out of room, just start dropping commands
return NULL;
}
cmdList->used += bytes;
return cmdList->cmds + cmdList->used - bytes;
}
/*
=============
R_AddDrawSurfCmd
=============
*/
void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ) {
drawSurfsCommand_t *cmd;
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
if ( !cmd ) {
return;
}
cmd->commandId = RC_DRAW_SURFS;
cmd->drawSurfs = drawSurfs;
cmd->numDrawSurfs = numDrawSurfs;
cmd->refdef = tr.refdef;
cmd->viewParms = tr.viewParms;
}
/*
=============
R_AddCapShadowmapCmd
=============
*/
void R_AddCapShadowmapCmd( int map, int cubeSide ) {
capShadowmapCommand_t *cmd;
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
if ( !cmd ) {
return;
}
cmd->commandId = RC_CAPSHADOWMAP;
cmd->map = map;
cmd->cubeSide = cubeSide;
}
/*
=============
R_PostProcessingCmd
=============
*/
void R_AddPostProcessCmd( ) {
postProcessCommand_t *cmd;
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
if ( !cmd ) {
return;
}
cmd->commandId = RC_POSTPROCESS;
cmd->refdef = tr.refdef;
cmd->viewParms = tr.viewParms;
}
/*
=============
RE_SetColor
Passing NULL will set the color to white
=============
*/
void RE_SetColor( const float *rgba ) {
setColorCommand_t *cmd;
if ( !tr.registered ) {
return;
}
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
if ( !cmd ) {
return;
}
cmd->commandId = RC_SET_COLOR;
if ( !rgba ) {
static float colorWhite[4] = { 1, 1, 1, 1 };
rgba = colorWhite;
}
cmd->color[0] = rgba[0];
cmd->color[1] = rgba[1];
cmd->color[2] = rgba[2];
cmd->color[3] = rgba[3];
}
/*
=============
RE_StretchPic
=============
*/
void RE_StretchPic ( float x, float y, float w, float h,
float s1, float t1, float s2, float t2, qhandle_t hShader ) {
stretchPicCommand_t *cmd;
if (!tr.registered) {
return;
}
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
if ( !cmd ) {
return;
}
cmd->commandId = RC_STRETCH_PIC;
cmd->shader = R_GetShaderByHandle( hShader );
cmd->x = x;
cmd->y = y;
cmd->w = w;
cmd->h = h;
cmd->s1 = s1;
cmd->t1 = t1;
cmd->s2 = s2;
cmd->t2 = t2;
}
#define MODE_RED_CYAN 1
#define MODE_RED_BLUE 2
#define MODE_RED_GREEN 3
#define MODE_GREEN_MAGENTA 4
#define MODE_MAX MODE_GREEN_MAGENTA
void R_SetColorMode(GLboolean *rgba, stereoFrame_t stereoFrame, int colormode)
{
rgba[0] = rgba[1] = rgba[2] = rgba[3] = GL_TRUE;
if(colormode > MODE_MAX)
{
if(stereoFrame == STEREO_LEFT)
stereoFrame = STEREO_RIGHT;
else if(stereoFrame == STEREO_RIGHT)
stereoFrame = STEREO_LEFT;
colormode -= MODE_MAX;
}
if(colormode == MODE_GREEN_MAGENTA)
{
if(stereoFrame == STEREO_LEFT)
rgba[0] = rgba[2] = GL_FALSE;
else if(stereoFrame == STEREO_RIGHT)
rgba[1] = GL_FALSE;
}
else
{
if(stereoFrame == STEREO_LEFT)
rgba[1] = rgba[2] = GL_FALSE;
else if(stereoFrame == STEREO_RIGHT)
{
rgba[0] = GL_FALSE;
if(colormode == MODE_RED_BLUE)
rgba[1] = GL_FALSE;
else if(colormode == MODE_RED_GREEN)
rgba[2] = GL_FALSE;
}
}
}
/*
====================
RE_BeginFrame
If running in stereo, RE_BeginFrame will be called twice
for each RE_EndFrame
====================
*/
void RE_BeginFrame( stereoFrame_t stereoFrame ) {
drawBufferCommand_t *cmd = NULL;
colorMaskCommand_t *colcmd = NULL;
if ( !tr.registered ) {
return;
}
glState.finishCalled = qfalse;
tr.frameCount++;
tr.frameSceneNum = 0;
//
// do overdraw measurement
//
if ( r_measureOverdraw->integer )
{
if ( glConfig.stencilBits < 4 )
{
ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits );
ri.Cvar_Set( "r_measureOverdraw", "0" );
r_measureOverdraw->modified = qfalse;
}
else if ( r_shadows->integer == 2 )
{
ri.Printf( PRINT_ALL, "Warning: stencil shadows and overdraw measurement are mutually exclusive\n" );
ri.Cvar_Set( "r_measureOverdraw", "0" );
r_measureOverdraw->modified = qfalse;
}
else
{
R_IssuePendingRenderCommands();
qglEnable( GL_STENCIL_TEST );
qglStencilMask( ~0U );
qglClearStencil( 0U );
qglStencilFunc( GL_ALWAYS, 0U, ~0U );
qglStencilOp( GL_KEEP, GL_INCR, GL_INCR );
}
r_measureOverdraw->modified = qfalse;
}
else
{
// this is only reached if it was on and is now off
if ( r_measureOverdraw->modified ) {
R_IssuePendingRenderCommands();
qglDisable( GL_STENCIL_TEST );
}
r_measureOverdraw->modified = qfalse;
}
//
// texturemode stuff
//
if ( r_textureMode->modified ) {
R_IssuePendingRenderCommands();
GL_TextureMode( r_textureMode->string );
r_textureMode->modified = qfalse;
}
//
// gamma stuff
//
if ( r_gamma->modified ) {
r_gamma->modified = qfalse;
R_IssuePendingRenderCommands();
R_SetColorMappings();
}
// check for errors
if ( !r_ignoreGLErrors->integer )
{
int err;
R_IssuePendingRenderCommands();
if ((err = qglGetError()) != GL_NO_ERROR)
ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!", err);
}
if (glConfig.stereoEnabled) {
if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
return;
cmd->commandId = RC_DRAW_BUFFER;
if ( stereoFrame == STEREO_LEFT ) {
cmd->buffer = (int)GL_BACK_LEFT;
} else if ( stereoFrame == STEREO_RIGHT ) {
cmd->buffer = (int)GL_BACK_RIGHT;
} else {
ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame );
}
}
else
{
if(r_anaglyphMode->integer)
{
if(r_anaglyphMode->modified)
{
// clear both, front and backbuffer.
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
backEnd.colorMask[0] = GL_FALSE;
backEnd.colorMask[1] = GL_FALSE;
backEnd.colorMask[2] = GL_FALSE;
backEnd.colorMask[3] = GL_FALSE;
qglClearColor(0.0f, 0.0f, 0.0f, 1.0f);
if (glRefConfig.framebufferObject)
{
// clear all framebuffers
if (tr.msaaResolveFbo)
{
FBO_Bind(tr.msaaResolveFbo);
qglClear(GL_COLOR_BUFFER_BIT);
}
if (tr.renderFbo)
{
FBO_Bind(tr.renderFbo);
qglClear(GL_COLOR_BUFFER_BIT);
}
if (tr.screenScratchFbo)
{
FBO_Bind(tr.screenScratchFbo);
qglClear(GL_COLOR_BUFFER_BIT);
}
FBO_Bind(NULL);
}
qglDrawBuffer(GL_FRONT);
qglClear(GL_COLOR_BUFFER_BIT);
qglDrawBuffer(GL_BACK);
qglClear(GL_COLOR_BUFFER_BIT);
r_anaglyphMode->modified = qfalse;
}
if(stereoFrame == STEREO_LEFT)
{
if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
return;
if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) )
return;
}
else if(stereoFrame == STEREO_RIGHT)
{
clearDepthCommand_t *cldcmd;
if( !(cldcmd = R_GetCommandBuffer(sizeof(*cldcmd))) )
return;
cldcmd->commandId = RC_CLEARDEPTH;
if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) )
return;
}
else
ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame );
R_SetColorMode(colcmd->rgba, stereoFrame, r_anaglyphMode->integer);
colcmd->commandId = RC_COLORMASK;
}
else
{
if(stereoFrame != STEREO_CENTER)
ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame );
if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
return;
}
if(cmd)
{
cmd->commandId = RC_DRAW_BUFFER;
if(r_anaglyphMode->modified)
{
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
backEnd.colorMask[0] = 0;
backEnd.colorMask[1] = 0;
backEnd.colorMask[2] = 0;
backEnd.colorMask[3] = 0;
r_anaglyphMode->modified = qfalse;
}
if (!Q_stricmp(r_drawBuffer->string, "GL_FRONT"))
cmd->buffer = (int)GL_FRONT;
else
cmd->buffer = (int)GL_BACK;
}
}
tr.refdef.stereoFrame = stereoFrame;
}
/*
=============
RE_EndFrame
Returns the number of msec spent in the back end
=============
*/
void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) {
swapBuffersCommand_t *cmd;
if ( !tr.registered ) {
return;
}
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
if ( !cmd ) {
return;
}
cmd->commandId = RC_SWAP_BUFFERS;
R_IssueRenderCommands( qtrue );
R_InitNextFrame();
if ( frontEndMsec ) {
*frontEndMsec = tr.frontEndMsec;
}
tr.frontEndMsec = 0;
if ( backEndMsec ) {
*backEndMsec = backEnd.pc.msec;
}
backEnd.pc.msec = 0;
}
/*
=============
RE_TakeVideoFrame
=============
*/
void RE_TakeVideoFrame( int width, int height,
byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg )
{
videoFrameCommand_t *cmd;
if( !tr.registered ) {
return;
}
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
if( !cmd ) {
return;
}
cmd->commandId = RC_VIDEOFRAME;
cmd->width = width;
cmd->height = height;
cmd->captureBuffer = captureBuffer;
cmd->encodeBuffer = encodeBuffer;
cmd->motionJpeg = motionJpeg;
}

806
code/renderergl2/tr_curve.c Normal file
View file

@ -0,0 +1,806 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "tr_local.h"
/*
This file does all of the processing necessary to turn a raw grid of points
read from the map file into a srfGridMesh_t ready for rendering.
The level of detail solution is direction independent, based only on subdivided
distance from the true curve.
Only a single entry point:
srfGridMesh_t *R_SubdividePatchToGrid( int width, int height,
srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) {
*/
/*
============
LerpDrawVert
============
*/
static void LerpDrawVert( srfVert_t *a, srfVert_t *b, srfVert_t *out ) {
out->xyz[0] = 0.5f * (a->xyz[0] + b->xyz[0]);
out->xyz[1] = 0.5f * (a->xyz[1] + b->xyz[1]);
out->xyz[2] = 0.5f * (a->xyz[2] + b->xyz[2]);
out->st[0] = 0.5f * (a->st[0] + b->st[0]);
out->st[1] = 0.5f * (a->st[1] + b->st[1]);
out->lightmap[0] = 0.5f * (a->lightmap[0] + b->lightmap[0]);
out->lightmap[1] = 0.5f * (a->lightmap[1] + b->lightmap[1]);
out->vertexColors[0] = 0.5f * (a->vertexColors[0] + b->vertexColors[0]);
out->vertexColors[1] = 0.5f * (a->vertexColors[1] + b->vertexColors[1]);
out->vertexColors[2] = 0.5f * (a->vertexColors[2] + b->vertexColors[2]);
out->vertexColors[3] = 0.5f * (a->vertexColors[3] + b->vertexColors[3]);
}
/*
============
Transpose
============
*/
static void Transpose( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) {
int i, j;
srfVert_t temp;
if ( width > height ) {
for ( i = 0 ; i < height ; i++ ) {
for ( j = i + 1 ; j < width ; j++ ) {
if ( j < height ) {
// swap the value
temp = ctrl[j][i];
ctrl[j][i] = ctrl[i][j];
ctrl[i][j] = temp;
} else {
// just copy
ctrl[j][i] = ctrl[i][j];
}
}
}
} else {
for ( i = 0 ; i < width ; i++ ) {
for ( j = i + 1 ; j < height ; j++ ) {
if ( j < width ) {
// swap the value
temp = ctrl[i][j];
ctrl[i][j] = ctrl[j][i];
ctrl[j][i] = temp;
} else {
// just copy
ctrl[i][j] = ctrl[j][i];
}
}
}
}
}
/*
=================
MakeMeshNormals
Handles all the complicated wrapping and degenerate cases
=================
*/
static void MakeMeshNormals( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) {
int i, j, k, dist;
vec3_t normal;
vec3_t sum;
int count = 0;
vec3_t base;
vec3_t delta;
int x, y;
srfVert_t *dv;
vec3_t around[8], temp;
qboolean good[8];
qboolean wrapWidth, wrapHeight;
float len;
static int neighbors[8][2] = {
{0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
};
wrapWidth = qfalse;
for ( i = 0 ; i < height ; i++ ) {
VectorSubtract( ctrl[i][0].xyz, ctrl[i][width-1].xyz, delta );
len = VectorLengthSquared( delta );
if ( len > 1.0 ) {
break;
}
}
if ( i == height ) {
wrapWidth = qtrue;
}
wrapHeight = qfalse;
for ( i = 0 ; i < width ; i++ ) {
VectorSubtract( ctrl[0][i].xyz, ctrl[height-1][i].xyz, delta );
len = VectorLengthSquared( delta );
if ( len > 1.0 ) {
break;
}
}
if ( i == width) {
wrapHeight = qtrue;
}
for ( i = 0 ; i < width ; i++ ) {
for ( j = 0 ; j < height ; j++ ) {
count = 0;
dv = &ctrl[j][i];
VectorCopy( dv->xyz, base );
for ( k = 0 ; k < 8 ; k++ ) {
VectorClear( around[k] );
good[k] = qfalse;
for ( dist = 1 ; dist <= 3 ; dist++ ) {
x = i + neighbors[k][0] * dist;
y = j + neighbors[k][1] * dist;
if ( wrapWidth ) {
if ( x < 0 ) {
x = width - 1 + x;
} else if ( x >= width ) {
x = 1 + x - width;
}
}
if ( wrapHeight ) {
if ( y < 0 ) {
y = height - 1 + y;
} else if ( y >= height ) {
y = 1 + y - height;
}
}
if ( x < 0 || x >= width || y < 0 || y >= height ) {
break; // edge of patch
}
VectorSubtract( ctrl[y][x].xyz, base, temp );
if ( VectorNormalize2( temp, temp ) == 0 ) {
continue; // degenerate edge, get more dist
} else {
good[k] = qtrue;
VectorCopy( temp, around[k] );
break; // good edge
}
}
}
VectorClear( sum );
for ( k = 0 ; k < 8 ; k++ ) {
if ( !good[k] || !good[(k+1)&7] ) {
continue; // didn't get two points
}
CrossProduct( around[(k+1)&7], around[k], normal );
if ( VectorNormalize2( normal, normal ) == 0 ) {
continue;
}
VectorAdd( normal, sum, sum );
count++;
}
//if ( count == 0 ) {
// printf("bad normal\n");
//}
VectorNormalize2( sum, dv->normal );
}
}
}
#ifdef USE_VERT_TANGENT_SPACE
static void MakeMeshTangentVectors(int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], int numTriangles,
srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2])
{
int i, j;
srfVert_t *dv[3];
static srfVert_t ctrl2[MAX_GRID_SIZE * MAX_GRID_SIZE];
srfTriangle_t *tri;
// FIXME: use more elegant way
for(i = 0; i < width; i++)
{
for(j = 0; j < height; j++)
{
dv[0] = &ctrl2[j * width + i];
*dv[0] = ctrl[j][i];
}
}
for(i = 0, tri = triangles; i < numTriangles; i++, tri++)
{
dv[0] = &ctrl2[tri->indexes[0]];
dv[1] = &ctrl2[tri->indexes[1]];
dv[2] = &ctrl2[tri->indexes[2]];
R_CalcTangentVectors(dv);
}
#if 0
for(i = 0; i < (width * height); i++)
{
dv0 = &ctrl2[i];
VectorNormalize(dv0->normal);
#if 0
VectorNormalize(dv0->tangent);
VectorNormalize(dv0->bitangent);
#else
d = DotProduct(dv0->tangent, dv0->normal);
VectorMA(dv0->tangent, -d, dv0->normal, dv0->tangent);
VectorNormalize(dv0->tangent);
d = DotProduct(dv0->bitangent, dv0->normal);
VectorMA(dv0->bitangent, -d, dv0->normal, dv0->bitangent);
VectorNormalize(dv0->bitangent);
#endif
}
#endif
#if 0
// do another extra smoothing for normals to avoid flat shading
for(i = 0; i < (width * height); i++)
{
for(j = 0; j < (width * height); j++)
{
if(R_CompareVert(&ctrl2[i], &ctrl2[j], qfalse))
{
VectorAdd(ctrl2[i].normal, ctrl2[j].normal, ctrl2[i].normal);
}
}
VectorNormalize(ctrl2[i].normal);
}
#endif
for(i = 0; i < width; i++)
{
for(j = 0; j < height; j++)
{
dv[0] = &ctrl2[j * width + i];
dv[1] = &ctrl[j][i];
VectorCopy(dv[0]->tangent, dv[1]->tangent);
VectorCopy(dv[0]->bitangent, dv[1]->bitangent);
}
}
}
#endif
static int MakeMeshTriangles(int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE],
srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2])
{
int i, j;
int numTriangles;
int w, h;
srfVert_t *dv;
static srfVert_t ctrl2[MAX_GRID_SIZE * MAX_GRID_SIZE];
h = height - 1;
w = width - 1;
numTriangles = 0;
for(i = 0; i < h; i++)
{
for(j = 0; j < w; j++)
{
int v1, v2, v3, v4;
// vertex order to be reckognized as tristrips
v1 = i * width + j + 1;
v2 = v1 - 1;
v3 = v2 + width;
v4 = v3 + 1;
triangles[numTriangles].indexes[0] = v2;
triangles[numTriangles].indexes[1] = v3;
triangles[numTriangles].indexes[2] = v1;
numTriangles++;
triangles[numTriangles].indexes[0] = v1;
triangles[numTriangles].indexes[1] = v3;
triangles[numTriangles].indexes[2] = v4;
numTriangles++;
}
}
R_CalcSurfaceTriangleNeighbors(numTriangles, triangles);
// FIXME: use more elegant way
for(i = 0; i < width; i++)
{
for(j = 0; j < height; j++)
{
dv = &ctrl2[j * width + i];
*dv = ctrl[j][i];
}
}
R_CalcSurfaceTrianglePlanes(numTriangles, triangles, ctrl2);
return numTriangles;
}
/*
============
InvertCtrl
============
*/
static void InvertCtrl( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) {
int i, j;
srfVert_t temp;
for ( i = 0 ; i < height ; i++ ) {
for ( j = 0 ; j < width/2 ; j++ ) {
temp = ctrl[i][j];
ctrl[i][j] = ctrl[i][width-1-j];
ctrl[i][width-1-j] = temp;
}
}
}
/*
=================
InvertErrorTable
=================
*/
static void InvertErrorTable( float errorTable[2][MAX_GRID_SIZE], int width, int height ) {
int i;
float copy[2][MAX_GRID_SIZE];
Com_Memcpy( copy, errorTable, sizeof( copy ) );
for ( i = 0 ; i < width ; i++ ) {
errorTable[1][i] = copy[0][i]; //[width-1-i];
}
for ( i = 0 ; i < height ; i++ ) {
errorTable[0][i] = copy[1][height-1-i];
}
}
/*
==================
PutPointsOnCurve
==================
*/
static void PutPointsOnCurve( srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE],
int width, int height ) {
int i, j;
srfVert_t prev, next;
for ( i = 0 ; i < width ; i++ ) {
for ( j = 1 ; j < height ; j += 2 ) {
LerpDrawVert( &ctrl[j][i], &ctrl[j+1][i], &prev );
LerpDrawVert( &ctrl[j][i], &ctrl[j-1][i], &next );
LerpDrawVert( &prev, &next, &ctrl[j][i] );
}
}
for ( j = 0 ; j < height ; j++ ) {
for ( i = 1 ; i < width ; i += 2 ) {
LerpDrawVert( &ctrl[j][i], &ctrl[j][i+1], &prev );
LerpDrawVert( &ctrl[j][i], &ctrl[j][i-1], &next );
LerpDrawVert( &prev, &next, &ctrl[j][i] );
}
}
}
/*
=================
R_CreateSurfaceGridMesh
=================
*/
srfGridMesh_t *R_CreateSurfaceGridMesh(int width, int height,
srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], float errorTable[2][MAX_GRID_SIZE],
int numTriangles, srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]) {
int i, j, size;
srfVert_t *vert;
vec3_t tmpVec;
srfGridMesh_t *grid;
// copy the results out to a grid
size = (width * height - 1) * sizeof( srfVert_t ) + sizeof( *grid );
#ifdef PATCH_STITCHING
grid = /*ri.Hunk_Alloc*/ ri.Malloc( size );
Com_Memset(grid, 0, size);
grid->widthLodError = /*ri.Hunk_Alloc*/ ri.Malloc( width * 4 );
Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 );
grid->heightLodError = /*ri.Hunk_Alloc*/ ri.Malloc( height * 4 );
Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 );
grid->numTriangles = numTriangles;
grid->triangles = ri.Malloc(grid->numTriangles * sizeof(srfTriangle_t));
Com_Memcpy(grid->triangles, triangles, numTriangles * sizeof(srfTriangle_t));
grid->numVerts = (width * height);
grid->verts = ri.Malloc(grid->numVerts * sizeof(srfVert_t));
#else
grid = ri.Hunk_Alloc( size );
Com_Memset(grid, 0, size);
grid->widthLodError = ri.Hunk_Alloc( width * 4 );
Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 );
grid->heightLodError = ri.Hunk_Alloc( height * 4 );
Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 );
grid->numTriangles = numTriangles;
grid->triangles = ri.Hunk_Alloc(grid->numTriangles * sizeof(srfTriangle_t), h_low);
Com_Memcpy(grid->triangles, triangles, numTriangles * sizeof(srfTriangle_t));
grid->numVerts = (width * height);
grid->verts = ri.Hunk_Alloc(grid->numVerts * sizeof(srfVert_t), h_low);
#endif
grid->width = width;
grid->height = height;
grid->surfaceType = SF_GRID;
ClearBounds( grid->meshBounds[0], grid->meshBounds[1] );
for ( i = 0 ; i < width ; i++ ) {
for ( j = 0 ; j < height ; j++ ) {
vert = &grid->verts[j*width+i];
*vert = ctrl[j][i];
AddPointToBounds( vert->xyz, grid->meshBounds[0], grid->meshBounds[1] );
}
}
// compute local origin and bounds
VectorAdd( grid->meshBounds[0], grid->meshBounds[1], grid->localOrigin );
VectorScale( grid->localOrigin, 0.5f, grid->localOrigin );
VectorSubtract( grid->meshBounds[0], grid->localOrigin, tmpVec );
grid->meshRadius = VectorLength( tmpVec );
VectorCopy( grid->localOrigin, grid->lodOrigin );
grid->lodRadius = grid->meshRadius;
//
return grid;
}
/*
=================
R_FreeSurfaceGridMesh
=================
*/
void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ) {
ri.Free(grid->widthLodError);
ri.Free(grid->heightLodError);
ri.Free(grid->triangles);
ri.Free(grid->verts);
ri.Free(grid);
}
/*
=================
R_SubdividePatchToGrid
=================
*/
srfGridMesh_t *R_SubdividePatchToGrid( int width, int height,
srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) {
int i, j, k, l;
srfVert_t_cleared( prev );
srfVert_t_cleared( next );
srfVert_t_cleared( mid );
float len, maxLen;
int dir;
int t;
srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE];
float errorTable[2][MAX_GRID_SIZE];
int numTriangles;
static srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2];
int consecutiveComplete;
for ( i = 0 ; i < width ; i++ ) {
for ( j = 0 ; j < height ; j++ ) {
ctrl[j][i] = points[j*width+i];
}
}
for ( dir = 0 ; dir < 2 ; dir++ ) {
for ( j = 0 ; j < MAX_GRID_SIZE ; j++ ) {
errorTable[dir][j] = 0;
}
consecutiveComplete = 0;
// horizontal subdivisions
for ( j = 0 ; ; j = (j + 2) % (width - 1) ) {
// check subdivided midpoints against control points
// FIXME: also check midpoints of adjacent patches against the control points
// this would basically stitch all patches in the same LOD group together.
maxLen = 0;
for ( i = 0 ; i < height ; i++ ) {
vec3_t midxyz;
vec3_t midxyz2;
vec3_t dir;
vec3_t projected;
float d;
// calculate the point on the curve
for ( l = 0 ; l < 3 ; l++ ) {
midxyz[l] = (ctrl[i][j].xyz[l] + ctrl[i][j+1].xyz[l] * 2
+ ctrl[i][j+2].xyz[l] ) * 0.25f;
}
// see how far off the line it is
// using dist-from-line will not account for internal
// texture warping, but it gives a lot less polygons than
// dist-from-midpoint
VectorSubtract( midxyz, ctrl[i][j].xyz, midxyz );
VectorSubtract( ctrl[i][j+2].xyz, ctrl[i][j].xyz, dir );
VectorNormalize( dir );
d = DotProduct( midxyz, dir );
VectorScale( dir, d, projected );
VectorSubtract( midxyz, projected, midxyz2);
len = VectorLengthSquared( midxyz2 ); // we will do the sqrt later
if ( len > maxLen ) {
maxLen = len;
}
}
maxLen = sqrt(maxLen);
// if all the points are on the lines, remove the entire columns
if ( maxLen < 0.1f ) {
errorTable[dir][j+1] = 999;
// if we go over the whole grid twice without adding any columns, stop
if (++consecutiveComplete >= width)
break;
continue;
}
// see if we want to insert subdivided columns
if ( width + 2 > MAX_GRID_SIZE ) {
errorTable[dir][j+1] = 1.0f/maxLen;
break; // can't subdivide any more
}
if ( maxLen <= r_subdivisions->value ) {
errorTable[dir][j+1] = 1.0f/maxLen;
// if we go over the whole grid twice without adding any columns, stop
if (++consecutiveComplete >= width)
break;
continue; // didn't need subdivision
}
errorTable[dir][j+2] = 1.0f/maxLen;
consecutiveComplete = 0;
// insert two columns and replace the peak
width += 2;
for ( i = 0 ; i < height ; i++ ) {
LerpDrawVert( &ctrl[i][j], &ctrl[i][j+1], &prev );
LerpDrawVert( &ctrl[i][j+1], &ctrl[i][j+2], &next );
LerpDrawVert( &prev, &next, &mid );
for ( k = width - 1 ; k > j + 3 ; k-- ) {
ctrl[i][k] = ctrl[i][k-2];
}
ctrl[i][j + 1] = prev;
ctrl[i][j + 2] = mid;
ctrl[i][j + 3] = next;
}
// skip the new one, we'll get it on the next pass
j += 2;
}
Transpose( width, height, ctrl );
t = width;
width = height;
height = t;
}
// put all the aproximating points on the curve
PutPointsOnCurve( ctrl, width, height );
// cull out any rows or columns that are colinear
for ( i = 1 ; i < width-1 ; i++ ) {
if ( errorTable[0][i] != 999 ) {
continue;
}
for ( j = i+1 ; j < width ; j++ ) {
for ( k = 0 ; k < height ; k++ ) {
ctrl[k][j-1] = ctrl[k][j];
}
errorTable[0][j-1] = errorTable[0][j];
}
width--;
}
for ( i = 1 ; i < height-1 ; i++ ) {
if ( errorTable[1][i] != 999 ) {
continue;
}
for ( j = i+1 ; j < height ; j++ ) {
for ( k = 0 ; k < width ; k++ ) {
ctrl[j-1][k] = ctrl[j][k];
}
errorTable[1][j-1] = errorTable[1][j];
}
height--;
}
#if 1
// flip for longest tristrips as an optimization
// the results should be visually identical with or
// without this step
if ( height > width ) {
Transpose( width, height, ctrl );
InvertErrorTable( errorTable, width, height );
t = width;
width = height;
height = t;
InvertCtrl( width, height, ctrl );
}
#endif
// calculate triangles
numTriangles = MakeMeshTriangles(width, height, ctrl, triangles);
// calculate normals
MakeMeshNormals( width, height, ctrl );
#ifdef USE_VERT_TANGENT_SPACE
MakeMeshTangentVectors(width, height, ctrl, numTriangles, triangles);
#endif
return R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles);
}
/*
===============
R_GridInsertColumn
===============
*/
srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ) {
int i, j;
int width, height, oldwidth;
srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE];
float errorTable[2][MAX_GRID_SIZE];
float lodRadius;
vec3_t lodOrigin;
int numTriangles;
static srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2];
oldwidth = 0;
width = grid->width + 1;
if (width > MAX_GRID_SIZE)
return NULL;
height = grid->height;
for (i = 0; i < width; i++) {
if (i == column) {
//insert new column
for (j = 0; j < grid->height; j++) {
LerpDrawVert( &grid->verts[j * grid->width + i-1], &grid->verts[j * grid->width + i], &ctrl[j][i] );
if (j == row)
VectorCopy(point, ctrl[j][i].xyz);
}
errorTable[0][i] = loderror;
continue;
}
errorTable[0][i] = grid->widthLodError[oldwidth];
for (j = 0; j < grid->height; j++) {
ctrl[j][i] = grid->verts[j * grid->width + oldwidth];
}
oldwidth++;
}
for (j = 0; j < grid->height; j++) {
errorTable[1][j] = grid->heightLodError[j];
}
// put all the aproximating points on the curve
//PutPointsOnCurve( ctrl, width, height );
// calculate triangles
numTriangles = MakeMeshTriangles(width, height, ctrl, triangles);
// calculate normals
MakeMeshNormals( width, height, ctrl );
VectorCopy(grid->lodOrigin, lodOrigin);
lodRadius = grid->lodRadius;
// free the old grid
R_FreeSurfaceGridMesh(grid);
// create a new grid
grid = R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles);
grid->lodRadius = lodRadius;
VectorCopy(lodOrigin, grid->lodOrigin);
return grid;
}
/*
===============
R_GridInsertRow
===============
*/
srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ) {
int i, j;
int width, height, oldheight;
srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE];
float errorTable[2][MAX_GRID_SIZE];
float lodRadius;
vec3_t lodOrigin;
int numTriangles;
static srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2];
oldheight = 0;
width = grid->width;
height = grid->height + 1;
if (height > MAX_GRID_SIZE)
return NULL;
for (i = 0; i < height; i++) {
if (i == row) {
//insert new row
for (j = 0; j < grid->width; j++) {
LerpDrawVert( &grid->verts[(i-1) * grid->width + j], &grid->verts[i * grid->width + j], &ctrl[i][j] );
if (j == column)
VectorCopy(point, ctrl[i][j].xyz);
}
errorTable[1][i] = loderror;
continue;
}
errorTable[1][i] = grid->heightLodError[oldheight];
for (j = 0; j < grid->width; j++) {
ctrl[i][j] = grid->verts[oldheight * grid->width + j];
}
oldheight++;
}
for (j = 0; j < grid->width; j++) {
errorTable[0][j] = grid->widthLodError[j];
}
// put all the aproximating points on the curve
//PutPointsOnCurve( ctrl, width, height );
// calculate triangles
numTriangles = MakeMeshTriangles(width, height, ctrl, triangles);
// calculate normals
MakeMeshNormals( width, height, ctrl );
VectorCopy(grid->lodOrigin, lodOrigin);
lodRadius = grid->lodRadius;
// free the old grid
R_FreeSurfaceGridMesh(grid);
// create a new grid
grid = R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles);
grid->lodRadius = lodRadius;
VectorCopy(lodOrigin, grid->lodOrigin);
return grid;
}

View file

@ -0,0 +1,667 @@
/*
===========================================================================
Copyright (C) 2011 James Canete (use.less01@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_extensions.c - extensions needed by the renderer not in sdl_glimp.c
#ifdef USE_LOCAL_HEADERS
# include "SDL.h"
#else
# include <SDL.h>
#endif
#include "tr_local.h"
// GL_EXT_draw_range_elements
void (APIENTRY * qglDrawRangeElementsEXT) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices);
// GL_EXT_multi_draw_arrays
void (APIENTRY * qglMultiDrawArraysEXT) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
void (APIENTRY * qglMultiDrawElementsEXT) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid **indices, GLsizei primcount);
// GL_ARB_vertex_shader
void (APIENTRY * qglBindAttribLocationARB) (GLhandleARB programObj, GLuint index, const GLcharARB * name);
void (APIENTRY * qglGetActiveAttribARB) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length,
GLint * size, GLenum * type, GLcharARB * name);
GLint(APIENTRY * qglGetAttribLocationARB) (GLhandleARB programObj, const GLcharARB * name);
// GL_ARB_vertex_program
void (APIENTRY * qglVertexAttrib4fARB) (GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
void (APIENTRY * qglVertexAttrib4fvARB) (GLuint, const GLfloat *);
void (APIENTRY * qglVertexAttribPointerARB) (GLuint index, GLint size, GLenum type, GLboolean normalized,
GLsizei stride, const GLvoid * pointer);
void (APIENTRY * qglEnableVertexAttribArrayARB) (GLuint index);
void (APIENTRY * qglDisableVertexAttribArrayARB) (GLuint index);
// GL_ARB_vertex_buffer_object
void (APIENTRY * qglBindBufferARB) (GLenum target, GLuint buffer);
void (APIENTRY * qglDeleteBuffersARB) (GLsizei n, const GLuint * buffers);
void (APIENTRY * qglGenBuffersARB) (GLsizei n, GLuint * buffers);
GLboolean(APIENTRY * qglIsBufferARB) (GLuint buffer);
void (APIENTRY * qglBufferDataARB) (GLenum target, GLsizeiptrARB size, const GLvoid * data, GLenum usage);
void (APIENTRY * qglBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data);
void (APIENTRY * qglGetBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid * data);
void (APIENTRY * qglGetBufferParameterivARB) (GLenum target, GLenum pname, GLint * params);
void (APIENTRY * qglGetBufferPointervARB) (GLenum target, GLenum pname, GLvoid * *params);
// GL_ARB_shader_objects
void (APIENTRY * qglDeleteObjectARB) (GLhandleARB obj);
GLhandleARB(APIENTRY * qglGetHandleARB) (GLenum pname);
void (APIENTRY * qglDetachObjectARB) (GLhandleARB containerObj, GLhandleARB attachedObj);
GLhandleARB(APIENTRY * qglCreateShaderObjectARB) (GLenum shaderType);
void (APIENTRY * qglShaderSourceARB) (GLhandleARB shaderObj, GLsizei count, const GLcharARB * *string,
const GLint * length);
void (APIENTRY * qglCompileShaderARB) (GLhandleARB shaderObj);
GLhandleARB(APIENTRY * qglCreateProgramObjectARB) (void);
void (APIENTRY * qglAttachObjectARB) (GLhandleARB containerObj, GLhandleARB obj);
void (APIENTRY * qglLinkProgramARB) (GLhandleARB programObj);
void (APIENTRY * qglUseProgramObjectARB) (GLhandleARB programObj);
void (APIENTRY * qglValidateProgramARB) (GLhandleARB programObj);
void (APIENTRY * qglUniform1fARB) (GLint location, GLfloat v0);
void (APIENTRY * qglUniform2fARB) (GLint location, GLfloat v0, GLfloat v1);
void (APIENTRY * qglUniform3fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void (APIENTRY * qglUniform4fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
void (APIENTRY * qglUniform1iARB) (GLint location, GLint v0);
void (APIENTRY * qglUniform2iARB) (GLint location, GLint v0, GLint v1);
void (APIENTRY * qglUniform3iARB) (GLint location, GLint v0, GLint v1, GLint v2);
void (APIENTRY * qglUniform4iARB) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
void (APIENTRY * qglUniform1fvARB) (GLint location, GLsizei count, const GLfloat * value);
void (APIENTRY * qglUniform2fvARB) (GLint location, GLsizei count, const GLfloat * value);
void (APIENTRY * qglUniform3fvARB) (GLint location, GLsizei count, const GLfloat * value);
void (APIENTRY * qglUniform4fvARB) (GLint location, GLsizei count, const GLfloat * value);
void (APIENTRY * qglUniform2ivARB) (GLint location, GLsizei count, const GLint * value);
void (APIENTRY * qglUniform3ivARB) (GLint location, GLsizei count, const GLint * value);
void (APIENTRY * qglUniform4ivARB) (GLint location, GLsizei count, const GLint * value);
void (APIENTRY * qglUniformMatrix2fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);
void (APIENTRY * qglUniformMatrix3fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);
void (APIENTRY * qglUniformMatrix4fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);
void (APIENTRY * qglGetObjectParameterfvARB) (GLhandleARB obj, GLenum pname, GLfloat * params);
void (APIENTRY * qglGetObjectParameterivARB) (GLhandleARB obj, GLenum pname, GLint * params);
void (APIENTRY * qglGetInfoLogARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog);
void (APIENTRY * qglGetAttachedObjectsARB) (GLhandleARB containerObj, GLsizei maxCount, GLsizei * count,
GLhandleARB * obj);
GLint(APIENTRY * qglGetUniformLocationARB) (GLhandleARB programObj, const GLcharARB * name);
void (APIENTRY * qglGetActiveUniformARB) (GLhandleARB programObj, GLuint index, GLsizei maxIndex, GLsizei * length,
GLint * size, GLenum * type, GLcharARB * name);
void (APIENTRY * qglGetUniformfvARB) (GLhandleARB programObj, GLint location, GLfloat * params);
void (APIENTRY * qglGetUniformivARB) (GLhandleARB programObj, GLint location, GLint * params);
void (APIENTRY * qglGetShaderSourceARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * source);
// GL_ARB_texture_compression
void (APIENTRY * qglCompressedTexImage3DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data);
void (APIENTRY * qglCompressedTexImage2DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
GLint border, GLsizei imageSize, const GLvoid *data);
void (APIENTRY * qglCompressedTexImage1DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border,
GLsizei imageSize, const GLvoid *data);
void (APIENTRY * qglCompressedTexSubImage3DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data);
void (APIENTRY * qglCompressedTexSubImage2DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data);
void (APIENTRY * qglCompressedTexSubImage1DARB)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format,
GLsizei imageSize, const GLvoid *data);
void (APIENTRY * qglGetCompressedTexImageARB)(GLenum target, GLint lod,
GLvoid *img);
// GL_EXT_framebuffer_object
GLboolean (APIENTRY * qglIsRenderbufferEXT)(GLuint renderbuffer);
void (APIENTRY * qglBindRenderbufferEXT)(GLenum target, GLuint renderbuffer);
void (APIENTRY * qglDeleteRenderbuffersEXT)(GLsizei n, const GLuint *renderbuffers);
void (APIENTRY * qglGenRenderbuffersEXT)(GLsizei n, GLuint *renderbuffers);
void (APIENTRY * qglRenderbufferStorageEXT)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
void (APIENTRY * qglGetRenderbufferParameterivEXT)(GLenum target, GLenum pname, GLint *params);
GLboolean (APIENTRY * qglIsFramebufferEXT)(GLuint framebuffer);
void (APIENTRY * qglBindFramebufferEXT)(GLenum target, GLuint framebuffer);
void (APIENTRY * qglDeleteFramebuffersEXT)(GLsizei n, const GLuint *framebuffers);
void (APIENTRY * qglGenFramebuffersEXT)(GLsizei n, GLuint *framebuffers);
GLenum (APIENTRY * qglCheckFramebufferStatusEXT)(GLenum target);
void (APIENTRY * qglFramebufferTexture1DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture,
GLint level);
void (APIENTRY * qglFramebufferTexture2DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture,
GLint level);
void (APIENTRY * qglFramebufferTexture3DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture,
GLint level, GLint zoffset);
void (APIENTRY * qglFramebufferRenderbufferEXT)(GLenum target, GLenum attachment, GLenum renderbuffertarget,
GLuint renderbuffer);
void (APIENTRY * qglGetFramebufferAttachmentParameterivEXT)(GLenum target, GLenum attachment, GLenum pname, GLint *params);
void (APIENTRY * qglGenerateMipmapEXT)(GLenum target);
// GL_ARB_occlusion_query
void (APIENTRY * qglGenQueriesARB)(GLsizei n, GLuint *ids);
void (APIENTRY * qglDeleteQueriesARB)(GLsizei n, const GLuint *ids);
GLboolean (APIENTRY * qglIsQueryARB)(GLuint id);
void (APIENTRY * qglBeginQueryARB)(GLenum target, GLuint id);
void (APIENTRY * qglEndQueryARB)(GLenum target);
void (APIENTRY * qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params);
void (APIENTRY * qglGetQueryObjectivARB)(GLuint id, GLenum pname, GLint *params);
void (APIENTRY * qglGetQueryObjectuivARB)(GLuint id, GLenum pname, GLuint *params);
// GL_EXT_framebuffer_blit
void (APIENTRY * qglBlitFramebufferEXT)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter);
// GL_EXT_framebuffer_multisample
void (APIENTRY * qglRenderbufferStorageMultisampleEXT)(GLenum target, GLsizei samples,
GLenum internalformat, GLsizei width, GLsizei height);
// GL_ARB_draw_buffers
void (APIENTRY * qglDrawBuffersARB)(GLsizei n, const GLenum *bufs);
static qboolean GLimp_HaveExtension(const char *ext)
{
const char *ptr = Q_stristr( glConfig.extensions_string, ext );
if (ptr == NULL)
return qfalse;
ptr += strlen(ext);
return ((*ptr == ' ') || (*ptr == '\0')); // verify it's complete string.
}
void GLimp_InitExtraExtensions()
{
char *extension;
const char* result[3] = { "...ignoring %s\n", "...using %s\n", "...%s not found\n" };
// GL_EXT_draw_range_elements
extension = "GL_EXT_draw_range_elements";
glRefConfig.drawRangeElements = qfalse;
qglMultiDrawArraysEXT = NULL;
qglMultiDrawElementsEXT = NULL;
if( GLimp_HaveExtension( extension ) )
{
qglDrawRangeElementsEXT = (void *) SDL_GL_GetProcAddress("glDrawRangeElementsEXT");
if ( r_ext_draw_range_elements->integer)
glRefConfig.drawRangeElements = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.drawRangeElements], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_EXT_multi_draw_arrays
extension = "GL_EXT_multi_draw_arrays";
glRefConfig.multiDrawArrays = qfalse;
qglMultiDrawArraysEXT = NULL;
qglMultiDrawElementsEXT = NULL;
if( GLimp_HaveExtension( extension ) )
{
qglMultiDrawArraysEXT = (PFNGLMULTIDRAWARRAYSEXTPROC) SDL_GL_GetProcAddress("glMultiDrawArraysEXT");
qglMultiDrawElementsEXT = (PFNGLMULTIDRAWELEMENTSEXTPROC) SDL_GL_GetProcAddress("glMultiDrawElementsEXT");
if ( r_ext_multi_draw_arrays->integer )
glRefConfig.multiDrawArrays = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.multiDrawArrays], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_ARB_vertex_program
//glRefConfig.vertexProgram = qfalse;
extension = "GL_ARB_vertex_program";
qglVertexAttrib4fARB = NULL;
qglVertexAttrib4fvARB = NULL;
qglVertexAttribPointerARB = NULL;
qglEnableVertexAttribArrayARB = NULL;
qglDisableVertexAttribArrayARB = NULL;
if( GLimp_HaveExtension( extension ) )
{
qglVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) SDL_GL_GetProcAddress("glVertexAttrib4fARB");
qglVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) SDL_GL_GetProcAddress("glVertexAttrib4fvARB");
qglVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) SDL_GL_GetProcAddress("glVertexAttribPointerARB");
qglEnableVertexAttribArrayARB =
(PFNGLENABLEVERTEXATTRIBARRAYARBPROC) SDL_GL_GetProcAddress("glEnableVertexAttribArrayARB");
qglDisableVertexAttribArrayARB =
(PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) SDL_GL_GetProcAddress("glDisableVertexAttribArrayARB");
ri.Printf(PRINT_ALL, result[1], extension);
//glRefConfig.vertexProgram = qtrue;
}
else
{
ri.Error(ERR_FATAL, result[2], extension);
}
// GL_ARB_vertex_buffer_object
//glRefConfig.vertexBufferObject = qfalse;
extension = "GL_ARB_vertex_buffer_object";
qglBindBufferARB = NULL;
qglDeleteBuffersARB = NULL;
qglGenBuffersARB = NULL;
qglIsBufferARB = NULL;
qglBufferDataARB = NULL;
qglBufferSubDataARB = NULL;
qglGetBufferSubDataARB = NULL;
qglGetBufferParameterivARB = NULL;
qglGetBufferPointervARB = NULL;
if( GLimp_HaveExtension( extension ) )
{
qglBindBufferARB = (PFNGLBINDBUFFERARBPROC) SDL_GL_GetProcAddress("glBindBufferARB");
qglDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) SDL_GL_GetProcAddress("glDeleteBuffersARB");
qglGenBuffersARB = (PFNGLGENBUFFERSARBPROC) SDL_GL_GetProcAddress("glGenBuffersARB");
qglIsBufferARB = (PFNGLISBUFFERARBPROC) SDL_GL_GetProcAddress("glIsBufferARB");
qglBufferDataARB = (PFNGLBUFFERDATAARBPROC) SDL_GL_GetProcAddress("glBufferDataARB");
qglBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC) SDL_GL_GetProcAddress("glBufferSubDataARB");
qglGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC) SDL_GL_GetProcAddress("glGetBufferSubDataARB");
qglGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetBufferParameterivARB");
qglGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC) SDL_GL_GetProcAddress("glGetBufferPointervARB");
ri.Printf(PRINT_ALL, result[1], extension);
//glRefConfig.vertexBufferObject = qtrue;
}
else
{
ri.Error(ERR_FATAL, result[2], extension);
}
// GL_ARB_shader_objects
extension = "GL_ARB_shader_objects";
//glRefConfig.shaderObjects = qfalse;
qglDeleteObjectARB = NULL;
qglGetHandleARB = NULL;
qglDetachObjectARB = NULL;
qglCreateShaderObjectARB = NULL;
qglShaderSourceARB = NULL;
qglCompileShaderARB = NULL;
qglCreateProgramObjectARB = NULL;
qglAttachObjectARB = NULL;
qglLinkProgramARB = NULL;
qglUseProgramObjectARB = NULL;
qglValidateProgramARB = NULL;
qglUniform1fARB = NULL;
qglUniform2fARB = NULL;
qglUniform3fARB = NULL;
qglUniform4fARB = NULL;
qglUniform1iARB = NULL;
qglUniform2iARB = NULL;
qglUniform3iARB = NULL;
qglUniform4iARB = NULL;
qglUniform1fvARB = NULL;
qglUniform2fvARB = NULL;
qglUniform3fvARB = NULL;
qglUniform4fvARB = NULL;
qglUniform2ivARB = NULL;
qglUniform3ivARB = NULL;
qglUniform4ivARB = NULL;
qglUniformMatrix2fvARB = NULL;
qglUniformMatrix3fvARB = NULL;
qglUniformMatrix4fvARB = NULL;
qglGetObjectParameterfvARB = NULL;
qglGetObjectParameterivARB = NULL;
qglGetInfoLogARB = NULL;
qglGetAttachedObjectsARB = NULL;
qglGetUniformLocationARB = NULL;
qglGetActiveUniformARB = NULL;
qglGetUniformfvARB = NULL;
qglGetUniformivARB = NULL;
qglGetShaderSourceARB = NULL;
if( GLimp_HaveExtension( extension ) )
{
qglDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB");
qglGetHandleARB = (PFNGLGETHANDLEARBPROC) SDL_GL_GetProcAddress("glGetHandleARB");
qglDetachObjectARB = (PFNGLDETACHOBJECTARBPROC) SDL_GL_GetProcAddress("glDetachObjectARB");
qglCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB");
qglShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB");
qglCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB");
qglCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB");
qglAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB");
qglLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB");
qglUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB");
qglValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC) SDL_GL_GetProcAddress("glValidateProgramARB");
qglUniform1fARB = (PFNGLUNIFORM1FARBPROC) SDL_GL_GetProcAddress("glUniform1fARB");
qglUniform2fARB = (PFNGLUNIFORM2FARBPROC) SDL_GL_GetProcAddress("glUniform2fARB");
qglUniform3fARB = (PFNGLUNIFORM3FARBPROC) SDL_GL_GetProcAddress("glUniform3fARB");
qglUniform4fARB = (PFNGLUNIFORM4FARBPROC) SDL_GL_GetProcAddress("glUniform4fARB");
qglUniform1iARB = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB");
qglUniform2iARB = (PFNGLUNIFORM2IARBPROC) SDL_GL_GetProcAddress("glUniform2iARB");
qglUniform3iARB = (PFNGLUNIFORM3IARBPROC) SDL_GL_GetProcAddress("glUniform3iARB");
qglUniform4iARB = (PFNGLUNIFORM4IARBPROC) SDL_GL_GetProcAddress("glUniform4iARB");
qglUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) SDL_GL_GetProcAddress("glUniform1fvARB");
qglUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) SDL_GL_GetProcAddress("glUniform2fvARB");
qglUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) SDL_GL_GetProcAddress("glUniform3fvARB");
qglUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) SDL_GL_GetProcAddress("glUniform4fvARB");
qglUniform2ivARB = (PFNGLUNIFORM2IVARBPROC) SDL_GL_GetProcAddress("glUniform2ivARB");
qglUniform3ivARB = (PFNGLUNIFORM3IVARBPROC) SDL_GL_GetProcAddress("glUniform3ivARB");
qglUniform4ivARB = (PFNGLUNIFORM4IVARBPROC) SDL_GL_GetProcAddress("glUniform4ivARB");
qglUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix2fvARB");
qglUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix3fvARB");
qglUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix4fvARB");
qglGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterfvARB");
qglGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB");
qglGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB");
qglGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) SDL_GL_GetProcAddress("glGetAttachedObjectsARB");
qglGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB");
qglGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) SDL_GL_GetProcAddress("glGetActiveUniformARB");
qglGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC) SDL_GL_GetProcAddress("glGetUniformfvARB");
qglGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC) SDL_GL_GetProcAddress("glGetUniformivARB");
qglGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glGetShaderSourceARB");
ri.Printf(PRINT_ALL, result[1], extension);
//glRefConfig.shaderObjects = qtrue;
}
else
{
ri.Error(ERR_FATAL, result[2], extension);
}
// GL_ARB_vertex_shader
//glRefConfig.vertexShader = qfalse;
extension = "GL_ARB_vertex_shader";
qglBindAttribLocationARB = NULL;
qglGetActiveAttribARB = NULL;
qglGetAttribLocationARB = NULL;
if( GLimp_HaveExtension( extension ) )
{
//int reservedComponents;
//qglGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &glConfig.maxVertexUniforms);
//qglGetIntegerv(GL_MAX_VARYING_FLOATS_ARB, &glConfig.maxVaryingFloats);
//qglGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &glConfig.maxVertexAttribs);
//reservedComponents = 16 * 10; // approximation how many uniforms we have besides the bone matrices
#if 0
if(glConfig.driverType == GLDRV_MESA)
{
// HACK
// restrict to number of vertex uniforms to 512 because of:
// xreal.x86_64: nv50_program.c:4181: nv50_program_validate_data: Assertion `p->param_nr <= 512' failed
glConfig.maxVertexUniforms = Q_bound(0, glConfig.maxVertexUniforms, 512);
}
#endif
//glConfig.maxVertexSkinningBones = (int) Q_bound(0.0, (Q_max(glConfig.maxVertexUniforms - reservedComponents, 0) / 16), MAX_BONES);
//glConfig.vboVertexSkinningAvailable = r_vboVertexSkinning->integer && ((glConfig.maxVertexSkinningBones >= 12) ? qtrue : qfalse);
qglBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) SDL_GL_GetProcAddress("glBindAttribLocationARB");
qglGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC) SDL_GL_GetProcAddress("glGetActiveAttribARB");
qglGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetAttribLocationARB");
ri.Printf(PRINT_ALL, result[1], extension);
//glRefConfig.vertexShader = qtrue;
}
else
{
ri.Error(ERR_FATAL, result[2], extension);
}
// GL_ARB_shading_language_100
extension = "GL_ARB_shading_language_100";
glRefConfig.textureFloat = qfalse;
if( GLimp_HaveExtension( extension ) )
{
char version[256];
Q_strncpyz( version, (char *) qglGetString (GL_SHADING_LANGUAGE_VERSION_ARB), sizeof( version ) );
sscanf(version, "%d.%d", &glRefConfig.glslMajorVersion, &glRefConfig.glslMinorVersion);
ri.Printf(PRINT_ALL, "...using GLSL version %s\n", version);
}
else
{
ri.Error(ERR_FATAL, result[2], extension);
}
glRefConfig.memInfo = MI_NONE;
if( GLimp_HaveExtension( "GL_NVX_gpu_memory_info" ) )
{
glRefConfig.memInfo = MI_NVX;
}
else if( GLimp_HaveExtension( "GL_ATI_meminfo" ) )
{
glRefConfig.memInfo = MI_ATI;
}
extension = "GL_ARB_texture_non_power_of_two";
glRefConfig.textureNonPowerOfTwo = qfalse;
if( GLimp_HaveExtension( extension ) )
{
if(1) //(r_ext_texture_non_power_of_two->integer)
{
glRefConfig.textureNonPowerOfTwo = qtrue;
}
ri.Printf(PRINT_ALL, result[glRefConfig.textureNonPowerOfTwo], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_ARB_texture_float
extension = "GL_ARB_texture_float";
glRefConfig.textureFloat = qfalse;
if( GLimp_HaveExtension( extension ) )
{
if( r_ext_texture_float->integer )
{
glRefConfig.textureFloat = qtrue;
}
ri.Printf(PRINT_ALL, result[glRefConfig.textureFloat], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_ARB_half_float_pixel
extension = "GL_ARB_half_float_pixel";
glRefConfig.halfFloatPixel = qfalse;
if( GLimp_HaveExtension( extension ) )
{
if( r_arb_half_float_pixel->integer )
glRefConfig.halfFloatPixel = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.halfFloatPixel], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_EXT_framebuffer_object
extension = "GL_EXT_framebuffer_object";
glRefConfig.framebufferObject = qfalse;
if( GLimp_HaveExtension( extension ) )
{
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &glRefConfig.maxRenderbufferSize);
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &glRefConfig.maxColorAttachments);
qglIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC) SDL_GL_GetProcAddress("glIsRenderbufferEXT");
qglBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC) SDL_GL_GetProcAddress("glBindRenderbufferEXT");
qglDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC) SDL_GL_GetProcAddress("glDeleteRenderbuffersEXT");
qglGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC) SDL_GL_GetProcAddress("glGenRenderbuffersEXT");
qglRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC) SDL_GL_GetProcAddress("glRenderbufferStorageEXT");
qglGetRenderbufferParameterivEXT = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) SDL_GL_GetProcAddress("glGetRenderbufferParameterivEXT");
qglIsFramebufferEXT = (PFNGLISFRAMEBUFFEREXTPROC) SDL_GL_GetProcAddress("glIsFramebufferEXT");
qglBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) SDL_GL_GetProcAddress("glBindFramebufferEXT");
qglDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC) SDL_GL_GetProcAddress("glDeleteFramebuffersEXT");
qglGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) SDL_GL_GetProcAddress("glGenFramebuffersEXT");
qglCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT");
qglFramebufferTexture1DEXT = (PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) SDL_GL_GetProcAddress("glFramebufferTexture1DEXT");
qglFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) SDL_GL_GetProcAddress("glFramebufferTexture2DEXT");
qglFramebufferTexture3DEXT = (PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) SDL_GL_GetProcAddress("glFramebufferTexture3DEXT");
qglFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) SDL_GL_GetProcAddress("glFramebufferRenderbufferEXT");
qglGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) SDL_GL_GetProcAddress("glGetFramebufferAttachmentParameterivEXT");
qglGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC) SDL_GL_GetProcAddress("glGenerateMipmapEXT");
if(r_ext_framebuffer_object->value)
glRefConfig.framebufferObject = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.framebufferObject], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_EXT_packed_depth_stencil
extension = "GL_EXT_packed_depth_stencil";
glRefConfig.packedDepthStencil = qfalse;
if( GLimp_HaveExtension(extension))
{
glRefConfig.packedDepthStencil = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.packedDepthStencil], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_ARB_occlusion_query
extension = "GL_ARB_occlusion_query";
glRefConfig.occlusionQuery = qfalse;
if (GLimp_HaveExtension(extension))
{
qglGenQueriesARB = (PFNGLGENQUERIESARBPROC) SDL_GL_GetProcAddress("glGenQueriesARB");
qglDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC) SDL_GL_GetProcAddress("glDeleteQueriesARB");
qglIsQueryARB = (PFNGLISQUERYARBPROC) SDL_GL_GetProcAddress("glIsQueryARB");
qglBeginQueryARB = (PFNGLBEGINQUERYARBPROC) SDL_GL_GetProcAddress("glBeginQueryARB");
qglEndQueryARB = (PFNGLENDQUERYARBPROC) SDL_GL_GetProcAddress("glEndQueryARB");
qglGetQueryivARB = (PFNGLGETQUERYIVARBPROC) SDL_GL_GetProcAddress("glGetQueryivARB");
qglGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC) SDL_GL_GetProcAddress("glGetQueryObjectivARB");
qglGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC) SDL_GL_GetProcAddress("glGetQueryObjectuivARB");
glRefConfig.occlusionQuery = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.occlusionQuery], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_EXT_framebuffer_blit
extension = "GL_EXT_framebuffer_blit";
glRefConfig.framebufferBlit = qfalse;
if (GLimp_HaveExtension(extension))
{
qglBlitFramebufferEXT = (void *)SDL_GL_GetProcAddress("glBlitFramebufferEXT");
glRefConfig.framebufferBlit = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.framebufferBlit], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_EXT_framebuffer_multisample
extension = "GL_EXT_framebuffer_multisample";
glRefConfig.framebufferMultisample = qfalse;
if (GLimp_HaveExtension(extension))
{
qglRenderbufferStorageMultisampleEXT = (void *)SDL_GL_GetProcAddress("glRenderbufferStorageMultisampleEXT");
glRefConfig.framebufferMultisample = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.framebufferMultisample], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_EXT_texture_sRGB
extension = "GL_EXT_texture_sRGB";
glRefConfig.texture_srgb = qfalse;
if (GLimp_HaveExtension(extension))
{
if (r_srgb->integer)
glRefConfig.texture_srgb = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.texture_srgb], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
glRefConfig.textureCompression = TCR_NONE;
// GL_EXT_texture_compression_latc
extension = "GL_EXT_texture_compression_latc";
if (GLimp_HaveExtension(extension))
{
if (r_ext_compressed_textures->integer)
glRefConfig.textureCompression |= TCR_LATC;
ri.Printf(PRINT_ALL, result[r_ext_compressed_textures->integer ? 1 : 0], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_ARB_texture_compression_bptc
extension = "GL_ARB_texture_compression_bptc";
if (GLimp_HaveExtension(extension))
{
if (r_ext_compressed_textures->integer >= 2)
glRefConfig.textureCompression |= TCR_BPTC;
ri.Printf(PRINT_ALL, result[(r_ext_compressed_textures->integer >= 2) ? 1 : 0], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_ARB_draw_buffers
extension = "GL_ARB_draw_buffers";
qglDrawBuffersARB = NULL;
if( GLimp_HaveExtension( extension ) )
{
qglDrawBuffersARB = (void *) SDL_GL_GetProcAddress("glDrawBuffersARB");
ri.Printf(PRINT_ALL, result[1], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_ARB_depth_clamp
extension = "GL_ARB_depth_clamp";
glRefConfig.depthClamp = qfalse;
if( GLimp_HaveExtension( extension ) )
{
glRefConfig.depthClamp = qtrue;
ri.Printf(PRINT_ALL, result[1], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
}

View file

@ -0,0 +1,240 @@
/*
===========================================================================
Copyright (C) 2010 James Canete (use.less01@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_extramath.c - extra math needed by the renderer not in qmath.c
#include "tr_local.h"
// Some matrix helper functions
// FIXME: do these already exist in ioq3 and I don't know about them?
void Matrix16Zero( matrix_t out )
{
out[ 0] = 0.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = 0.0f;
out[ 1] = 0.0f; out[ 5] = 0.0f; out[ 9] = 0.0f; out[13] = 0.0f;
out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 0.0f; out[14] = 0.0f;
out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 0.0f;
}
void Matrix16Identity( matrix_t out )
{
out[ 0] = 1.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = 0.0f;
out[ 1] = 0.0f; out[ 5] = 1.0f; out[ 9] = 0.0f; out[13] = 0.0f;
out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 1.0f; out[14] = 0.0f;
out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f;
}
void Matrix16Copy( const matrix_t in, matrix_t out )
{
out[ 0] = in[ 0]; out[ 4] = in[ 4]; out[ 8] = in[ 8]; out[12] = in[12];
out[ 1] = in[ 1]; out[ 5] = in[ 5]; out[ 9] = in[ 9]; out[13] = in[13];
out[ 2] = in[ 2]; out[ 6] = in[ 6]; out[10] = in[10]; out[14] = in[14];
out[ 3] = in[ 3]; out[ 7] = in[ 7]; out[11] = in[11]; out[15] = in[15];
}
void Matrix16Multiply( const matrix_t in1, const matrix_t in2, matrix_t out )
{
out[ 0] = in1[ 0] * in2[ 0] + in1[ 4] * in2[ 1] + in1[ 8] * in2[ 2] + in1[12] * in2[ 3];
out[ 1] = in1[ 1] * in2[ 0] + in1[ 5] * in2[ 1] + in1[ 9] * in2[ 2] + in1[13] * in2[ 3];
out[ 2] = in1[ 2] * in2[ 0] + in1[ 6] * in2[ 1] + in1[10] * in2[ 2] + in1[14] * in2[ 3];
out[ 3] = in1[ 3] * in2[ 0] + in1[ 7] * in2[ 1] + in1[11] * in2[ 2] + in1[15] * in2[ 3];
out[ 4] = in1[ 0] * in2[ 4] + in1[ 4] * in2[ 5] + in1[ 8] * in2[ 6] + in1[12] * in2[ 7];
out[ 5] = in1[ 1] * in2[ 4] + in1[ 5] * in2[ 5] + in1[ 9] * in2[ 6] + in1[13] * in2[ 7];
out[ 6] = in1[ 2] * in2[ 4] + in1[ 6] * in2[ 5] + in1[10] * in2[ 6] + in1[14] * in2[ 7];
out[ 7] = in1[ 3] * in2[ 4] + in1[ 7] * in2[ 5] + in1[11] * in2[ 6] + in1[15] * in2[ 7];
out[ 8] = in1[ 0] * in2[ 8] + in1[ 4] * in2[ 9] + in1[ 8] * in2[10] + in1[12] * in2[11];
out[ 9] = in1[ 1] * in2[ 8] + in1[ 5] * in2[ 9] + in1[ 9] * in2[10] + in1[13] * in2[11];
out[10] = in1[ 2] * in2[ 8] + in1[ 6] * in2[ 9] + in1[10] * in2[10] + in1[14] * in2[11];
out[11] = in1[ 3] * in2[ 8] + in1[ 7] * in2[ 9] + in1[11] * in2[10] + in1[15] * in2[11];
out[12] = in1[ 0] * in2[12] + in1[ 4] * in2[13] + in1[ 8] * in2[14] + in1[12] * in2[15];
out[13] = in1[ 1] * in2[12] + in1[ 5] * in2[13] + in1[ 9] * in2[14] + in1[13] * in2[15];
out[14] = in1[ 2] * in2[12] + in1[ 6] * in2[13] + in1[10] * in2[14] + in1[14] * in2[15];
out[15] = in1[ 3] * in2[12] + in1[ 7] * in2[13] + in1[11] * in2[14] + in1[15] * in2[15];
}
void Matrix16Transform( const matrix_t in1, const vec4_t in2, vec4_t out )
{
out[ 0] = in1[ 0] * in2[ 0] + in1[ 4] * in2[ 1] + in1[ 8] * in2[ 2] + in1[12] * in2[ 3];
out[ 1] = in1[ 1] * in2[ 0] + in1[ 5] * in2[ 1] + in1[ 9] * in2[ 2] + in1[13] * in2[ 3];
out[ 2] = in1[ 2] * in2[ 0] + in1[ 6] * in2[ 1] + in1[10] * in2[ 2] + in1[14] * in2[ 3];
out[ 3] = in1[ 3] * in2[ 0] + in1[ 7] * in2[ 1] + in1[11] * in2[ 2] + in1[15] * in2[ 3];
}
qboolean Matrix16Compare( const matrix_t a, const matrix_t b )
{
return !(a[ 0] != b[ 0] || a[ 4] != b[ 4] || a[ 8] != b[ 8] || a[12] != b[12] ||
a[ 1] != b[ 1] || a[ 5] != b[ 5] || a[ 9] != b[ 9] || a[13] != b[13] ||
a[ 2] != b[ 2] || a[ 6] != b[ 6] || a[10] != b[10] || a[14] != b[14] ||
a[ 3] != b[ 3] || a[ 7] != b[ 7] || a[11] != b[11] || a[15] != b[15]);
}
void Matrix16Dump( const matrix_t in )
{
ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 0], in[ 4], in[ 8], in[12]);
ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 1], in[ 5], in[ 9], in[13]);
ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 2], in[ 6], in[10], in[14]);
ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 3], in[ 7], in[11], in[15]);
}
void Matrix16Translation( vec3_t vec, matrix_t out )
{
out[ 0] = 1.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = vec[0];
out[ 1] = 0.0f; out[ 5] = 1.0f; out[ 9] = 0.0f; out[13] = vec[1];
out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 1.0f; out[14] = vec[2];
out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f;
}
void Matrix16Ortho( float left, float right, float bottom, float top, float znear, float zfar, matrix_t out )
{
out[ 0] = 2.0f / (right - left); out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = -(right + left) / (right - left);
out[ 1] = 0.0f; out[ 5] = 2.0f / (top - bottom); out[ 9] = 0.0f; out[13] = -(top + bottom) / (top - bottom);
out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 2.0f / (zfar - znear); out[14] = -(zfar + znear) / (zfar - znear);
out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f;
}
void Matrix16View(vec3_t axes[3], vec3_t origin, matrix_t out)
{
out[0] = axes[0][0];
out[1] = axes[1][0];
out[2] = axes[2][0];
out[3] = 0;
out[4] = axes[0][1];
out[5] = axes[1][1];
out[6] = axes[2][1];
out[7] = 0;
out[8] = axes[0][2];
out[9] = axes[1][2];
out[10] = axes[2][2];
out[11] = 0;
out[12] = -DotProduct(origin, axes[0]);
out[13] = -DotProduct(origin, axes[1]);
out[14] = -DotProduct(origin, axes[2]);
out[15] = 1;
}
void Matrix16SimpleInverse( const matrix_t in, matrix_t out)
{
vec3_t v;
float invSqrLen;
VectorCopy(in + 0, v);
invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v);
out[ 0] = v[0]; out[ 4] = v[1]; out[ 8] = v[2]; out[12] = -DotProduct(v, &in[12]);
VectorCopy(in + 4, v);
invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v);
out[ 1] = v[0]; out[ 5] = v[1]; out[ 9] = v[2]; out[13] = -DotProduct(v, &in[12]);
VectorCopy(in + 8, v);
invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v);
out[ 2] = v[0]; out[ 6] = v[1]; out[10] = v[2]; out[14] = -DotProduct(v, &in[12]);
out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f;
}
void VectorLerp( vec3_t a, vec3_t b, float lerp, vec3_t c)
{
c[0] = a[0] * (1.0f - lerp) + b[0] * lerp;
c[1] = a[1] * (1.0f - lerp) + b[1] * lerp;
c[2] = a[2] * (1.0f - lerp) + b[2] * lerp;
}
qboolean SpheresIntersect(vec3_t origin1, float radius1, vec3_t origin2, float radius2)
{
float radiusSum = radius1 + radius2;
vec3_t diff;
VectorSubtract(origin1, origin2, diff);
if (DotProduct(diff, diff) <= radiusSum * radiusSum)
{
return qtrue;
}
return qfalse;
}
void BoundingSphereOfSpheres(vec3_t origin1, float radius1, vec3_t origin2, float radius2, vec3_t origin3, float *radius3)
{
vec3_t diff;
VectorScale(origin1, 0.5f, origin3);
VectorMA(origin3, 0.5f, origin2, origin3);
VectorSubtract(origin1, origin2, diff);
*radius3 = VectorLength(diff) * 0.5f + MAX(radius1, radius2);
}
int NextPowerOfTwo(int in)
{
int out;
for (out = 1; out < in; out <<= 1)
;
return out;
}
unsigned short FloatToHalf(float in)
{
unsigned short out;
union
{
float f;
unsigned int i;
} f32;
int sign, inExponent, inFraction;
int outExponent, outFraction;
f32.f = in;
sign = (f32.i & 0x80000000) >> 31;
inExponent = (f32.i & 0x7F800000) >> 23;
inFraction = f32.i & 0x007FFFFF;
outExponent = CLAMP(inExponent - 127, -15, 16) + 15;
outFraction = 0;
if (outExponent == 0x1F)
{
if (inExponent == 0xFF && inFraction != 0)
outFraction = 0x3FF;
}
else if (outExponent == 0x00)
{
if (inExponent == 0x00 && inFraction != 0)
outFraction = 0x3FF;
}
else
outFraction = inFraction >> 13;
out = (sign << 15) | (outExponent << 10) | outFraction;
return out;
}

View file

@ -0,0 +1,105 @@
/*
===========================================================================
Copyright (C) 2010 James Canete (use.less01@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_extramath.h
#ifndef __TR_EXTRAMATH_H__
#define __TR_EXTRAMATH_H__
typedef vec_t matrix_t[16];
typedef int vec2i_t[2];
typedef int vec3i_t[3];
typedef int vec4i_t[4];
void Matrix16Zero( matrix_t out );
void Matrix16Identity( matrix_t out );
void Matrix16Copy( const matrix_t in, matrix_t out );
void Matrix16Multiply( const matrix_t in1, const matrix_t in2, matrix_t out );
void Matrix16Transform( const matrix_t in1, const vec4_t in2, vec4_t out );
qboolean Matrix16Compare(const matrix_t a, const matrix_t b);
void Matrix16Dump( const matrix_t in );
void Matrix16Translation( vec3_t vec, matrix_t out );
void Matrix16Ortho( float left, float right, float bottom, float top, float znear, float zfar, matrix_t out );
void Matrix16View(vec3_t axes[3], vec3_t origin, matrix_t out);
void Matrix16SimpleInverse( const matrix_t in, matrix_t out);
#define VectorCopy2(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1])
#define VectorSet2(v,x,y) ((v)[0]=(x),(v)[1]=(y));
#define VectorCopy4(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3])
#define VectorSet4(v,x,y,z,w) ((v)[0]=(x),(v)[1]=(y),(v)[2]=(z),(v)[3]=(w))
#define DotProduct4(a,b) ((a)[0]*(b)[0] + (a)[1]*(b)[1] + (a)[2]*(b)[2] + (a)[3]*(b)[3])
#define VectorScale4(a,b,c) ((c)[0]=(a)[0]*(b),(c)[1]=(a)[1]*(b),(c)[2]=(a)[2]*(b),(c)[3]=(a)[3]*(b))
#define VectorCopy5(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3],(b)[4]=(a)[4])
#define OffsetByteToFloat(a) ((float)(a) * 1.0f/127.5f - 1.0f)
#define FloatToOffsetByte(a) (byte)(((a) + 1.0f) * 127.5f)
#define ByteToFloat(a) ((float)(a) * 1.0f/255.0f)
#define FloatToByte(a) (byte)((a) * 255.0f)
#define RGBtosRGB(a) (((a) < 0.0031308f) ? (12.92f * (a)) : (1.055f * pow((a), 0.41666f) - 0.055f))
#define sRGBtoRGB(a) (((a) <= 0.04045f) ? ((a) / 12.92f) : (pow((((a) + 0.055f) / 1.055f), 2.4)) )
static ID_INLINE int VectorCompare4(const vec4_t v1, const vec4_t v2)
{
if(v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2] || v1[3] != v2[3])
{
return 0;
}
return 1;
}
static ID_INLINE int VectorCompare5(const vec5_t v1, const vec5_t v2)
{
if(v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2] || v1[3] != v2[3] || v1[4] != v2[4])
{
return 0;
}
return 1;
}
void VectorLerp( vec3_t a, vec3_t b, float lerp, vec3_t c);
qboolean SpheresIntersect(vec3_t origin1, float radius1, vec3_t origin2, float radius2);
void BoundingSphereOfSpheres(vec3_t origin1, float radius1, vec3_t origin2, float radius2, vec3_t origin3, float *radius3);
#ifndef SGN
#define SGN(x) (((x) >= 0) ? !!(x) : -1)
#endif
#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifndef CLAMP
#define CLAMP(a,b,c) MIN(MAX((a),(b)),(c))
#endif
int NextPowerOfTwo(int in);
unsigned short FloatToHalf(float in);
#endif

View file

@ -0,0 +1,43 @@
/*
===========================================================================
Copyright (C) 2009-2011 Andrei Drexler, Richard Allen, James Canete
This file is part of Reaction source code.
Reaction source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Reaction source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Reaction source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#ifndef __TR_EXTRATYPES_H__
#define __TR_EXTRATYPES_H__
// tr_extratypes.h, for mods that want to extend tr_types.h without losing compatibility with original VMs
// extra renderfx flags start at 0x0400
#define RF_SUNFLARE 0x0400
// extra refdef flags start at 0x0008
#define RDF_NOFOG 0x0008 // don't apply fog
#define RDF_EXTRA 0x0010 // Makro - refdefex_t to follow after refdef_t
#define RDF_SUNLIGHT 0x0020 // SmileTheory - render sunlight and shadows
typedef struct {
float blurFactor;
float sunDir[3];
float sunCol[3];
float sunAmbCol[3];
} refdefex_t;
#endif

861
code/renderergl2/tr_fbo.c Normal file
View file

@ -0,0 +1,861 @@
/*
===========================================================================
Copyright (C) 2006 Kirk Barnes
Copyright (C) 2006-2008 Robert Beckebans <trebor_7@users.sourceforge.net>
This file is part of XreaL source code.
XreaL source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
XreaL source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with XreaL source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_fbo.c
#include "tr_local.h"
/*
=============
R_CheckFBO
=============
*/
qboolean R_CheckFBO(const FBO_t * fbo)
{
int code;
int id;
qglGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &id);
qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->frameBuffer);
code = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if(code == GL_FRAMEBUFFER_COMPLETE_EXT)
{
qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id);
return qtrue;
}
// an error occured
switch (code)
{
case GL_FRAMEBUFFER_COMPLETE_EXT:
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Unsupported framebuffer format\n", fbo->name);
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete attachment\n", fbo->name);
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing attachment\n", fbo->name);
break;
//case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
// ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, duplicate attachment\n", fbo->name);
// break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, attached images must have same dimensions\n",
fbo->name);
break;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, attached images must have same format\n",
fbo->name);
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing draw buffer\n", fbo->name);
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing read buffer\n", fbo->name);
break;
default:
ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) unknown error 0x%X\n", fbo->name, code);
//ri.Error(ERR_FATAL, "R_CheckFBO: (%s) unknown error 0x%X", fbo->name, code);
//assert(0);
break;
}
qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id);
return qfalse;
}
/*
============
FBO_Create
============
*/
FBO_t *FBO_Create(const char *name, int width, int height)
{
FBO_t *fbo;
if(strlen(name) >= MAX_QPATH)
{
ri.Error(ERR_DROP, "FBO_Create: \"%s\" is too long\n", name);
}
if(width <= 0 || width > glRefConfig.maxRenderbufferSize)
{
ri.Error(ERR_DROP, "FBO_Create: bad width %i", width);
}
if(height <= 0 || height > glRefConfig.maxRenderbufferSize)
{
ri.Error(ERR_DROP, "FBO_Create: bad height %i", height);
}
if(tr.numFBOs == MAX_FBOS)
{
ri.Error(ERR_DROP, "FBO_Create: MAX_FBOS hit");
}
fbo = tr.fbos[tr.numFBOs] = ri.Hunk_Alloc(sizeof(*fbo), h_low);
Q_strncpyz(fbo->name, name, sizeof(fbo->name));
fbo->index = tr.numFBOs++;
fbo->width = width;
fbo->height = height;
qglGenFramebuffersEXT(1, &fbo->frameBuffer);
return fbo;
}
void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample)
{
uint32_t *pRenderBuffer;
GLenum attachment;
qboolean absent;
switch(format)
{
case GL_RGB:
case GL_RGBA:
case GL_RGB8:
case GL_RGBA8:
case GL_RGB16F_ARB:
case GL_RGBA16F_ARB:
case GL_RGB32F_ARB:
case GL_RGBA32F_ARB:
fbo->colorFormat = format;
pRenderBuffer = &fbo->colorBuffers[index];
attachment = GL_COLOR_ATTACHMENT0_EXT + index;
break;
case GL_DEPTH_COMPONENT:
case GL_DEPTH_COMPONENT16_ARB:
case GL_DEPTH_COMPONENT24_ARB:
case GL_DEPTH_COMPONENT32_ARB:
fbo->depthFormat = format;
pRenderBuffer = &fbo->depthBuffer;
attachment = GL_DEPTH_ATTACHMENT_EXT;
break;
case GL_STENCIL_INDEX:
case GL_STENCIL_INDEX1_EXT:
case GL_STENCIL_INDEX4_EXT:
case GL_STENCIL_INDEX8_EXT:
case GL_STENCIL_INDEX16_EXT:
fbo->stencilFormat = format;
pRenderBuffer = &fbo->stencilBuffer;
attachment = GL_STENCIL_ATTACHMENT_EXT;
break;
case GL_DEPTH_STENCIL_EXT:
case GL_DEPTH24_STENCIL8_EXT:
fbo->packedDepthStencilFormat = format;
pRenderBuffer = &fbo->packedDepthStencilBuffer;
attachment = 0; // special for stencil and depth
break;
default:
ri.Printf(PRINT_WARNING, "FBO_CreateBuffer: invalid format %d\n", format);
return;
}
absent = *pRenderBuffer == 0;
if (absent)
qglGenRenderbuffersEXT(1, pRenderBuffer);
qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, *pRenderBuffer);
if (multisample && glRefConfig.framebufferMultisample)
{
qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, multisample, format, fbo->width, fbo->height);
}
else
{
qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, fbo->width, fbo->height);
}
if(absent)
{
if (attachment == 0)
{
qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer);
qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer);
}
else
qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, GL_RENDERBUFFER_EXT, *pRenderBuffer);
}
}
/*
=================
R_AttachFBOTexture1D
=================
*/
void R_AttachFBOTexture1D(int texId, int index)
{
if(index < 0 || index >= glRefConfig.maxColorAttachments)
{
ri.Printf(PRINT_WARNING, "R_AttachFBOTexture1D: invalid attachment index %i\n", index);
return;
}
qglFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, GL_TEXTURE_1D, texId, 0);
}
/*
=================
R_AttachFBOTexture2D
=================
*/
void R_AttachFBOTexture2D(int target, int texId, int index)
{
if(target != GL_TEXTURE_2D && (target < GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB || target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB))
{
ri.Printf(PRINT_WARNING, "R_AttachFBOTexture2D: invalid target %i\n", target);
return;
}
if(index < 0 || index >= glRefConfig.maxColorAttachments)
{
ri.Printf(PRINT_WARNING, "R_AttachFBOTexture2D: invalid attachment index %i\n", index);
return;
}
qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, target, texId, 0);
}
/*
=================
R_AttachFBOTexture3D
=================
*/
void R_AttachFBOTexture3D(int texId, int index, int zOffset)
{
if(index < 0 || index >= glRefConfig.maxColorAttachments)
{
ri.Printf(PRINT_WARNING, "R_AttachFBOTexture3D: invalid attachment index %i\n", index);
return;
}
qglFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, GL_TEXTURE_3D_EXT, texId, 0, zOffset);
}
/*
=================
R_AttachFBOTextureDepth
=================
*/
void R_AttachFBOTextureDepth(int texId)
{
qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0);
}
/*
=================
R_AttachFBOTexturePackedDepthStencil
=================
*/
void R_AttachFBOTexturePackedDepthStencil(int texId)
{
qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0);
qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0);
}
void FBO_AttachTextureImage(image_t *img, int index)
{
if (!glState.currentFBO)
{
ri.Printf(PRINT_WARNING, "FBO: attempted to attach a texture image with no FBO bound!\n");
return;
}
R_AttachFBOTexture2D(GL_TEXTURE_2D, img->texnum, index);
glState.currentFBO->colorImage[index] = img;
}
/*
============
FBO_Bind
============
*/
void FBO_Bind(FBO_t * fbo)
{
if (glState.currentFBO == fbo)
return;
if (r_logFile->integer)
{
// don't just call LogComment, or we will get a call to va() every frame!
if (fbo)
GLimp_LogComment(va("--- FBO_Bind( %s ) ---\n", fbo->name));
else
GLimp_LogComment("--- FBO_Bind ( NULL ) ---\n");
}
if (!fbo)
{
qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
//qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
glState.currentFBO = NULL;
return;
}
qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->frameBuffer);
/*
if(fbo->colorBuffers[0])
{
qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->colorBuffers[0]);
}
*/
/*
if(fbo->depthBuffer)
{
qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->depthBuffer);
qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->depthBuffer);
}
*/
glState.currentFBO = fbo;
}
/*
============
FBO_Init
============
*/
void FBO_Init(void)
{
int i;
// int width, height, hdrFormat, multisample;
int hdrFormat, multisample;
ri.Printf(PRINT_ALL, "------- FBO_Init -------\n");
if(!glRefConfig.framebufferObject)
return;
tr.numFBOs = 0;
GL_CheckErrors();
R_IssuePendingRenderCommands();
/* if(glRefConfig.textureNonPowerOfTwo)
{
width = glConfig.vidWidth;
height = glConfig.vidHeight;
}
else
{
width = NextPowerOfTwo(glConfig.vidWidth);
height = NextPowerOfTwo(glConfig.vidHeight);
} */
hdrFormat = GL_RGBA8;
if (r_hdr->integer && glRefConfig.framebufferObject && glRefConfig.textureFloat)
{
hdrFormat = GL_RGB16F_ARB;
}
qglGetIntegerv(GL_MAX_SAMPLES_EXT, &multisample);
if (r_ext_framebuffer_multisample->integer < multisample)
{
multisample = r_ext_framebuffer_multisample->integer;
}
if (multisample < 2 || !glRefConfig.framebufferBlit)
multisample = 0;
if (multisample != r_ext_framebuffer_multisample->integer)
{
ri.Cvar_SetValue("r_ext_framebuffer_multisample", (float)multisample);
}
// only create a render FBO if we need to resolve MSAA or do HDR
// otherwise just render straight to the screen (tr.renderFbo = NULL)
if (multisample && glRefConfig.framebufferMultisample)
{
tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height);
FBO_Bind(tr.renderFbo);
FBO_CreateBuffer(tr.renderFbo, hdrFormat, 0, multisample);
FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24_ARB, 0, multisample);
R_CheckFBO(tr.renderFbo);
tr.msaaResolveFbo = FBO_Create("_msaaResolve", tr.renderDepthImage->width, tr.renderDepthImage->height);
FBO_Bind(tr.msaaResolveFbo);
//FBO_CreateBuffer(tr.msaaResolveFbo, hdrFormat, 0, 0);
FBO_AttachTextureImage(tr.renderImage, 0);
//FBO_CreateBuffer(tr.msaaResolveFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0);
R_AttachFBOTextureDepth(tr.renderDepthImage->texnum);
R_CheckFBO(tr.msaaResolveFbo);
}
else if (r_hdr->integer)
{
tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height);
FBO_Bind(tr.renderFbo);
//FBO_CreateBuffer(tr.renderFbo, hdrFormat, 0, 0);
FBO_AttachTextureImage(tr.renderImage, 0);
//FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0);
R_AttachFBOTextureDepth(tr.renderDepthImage->texnum);
R_CheckFBO(tr.renderFbo);
}
// clear render buffer
// this fixes the corrupt screen bug with r_hdr 1 on older hardware
if (tr.renderFbo)
{
FBO_Bind(tr.renderFbo);
qglClearColor( 1, 0, 0.5, 1 );
qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
FBO_Bind(NULL);
}
if (r_drawSunRays->integer)
{
tr.sunRaysFbo = FBO_Create("_sunRays", tr.renderDepthImage->width, tr.renderDepthImage->height);
FBO_Bind(tr.sunRaysFbo);
FBO_AttachTextureImage(tr.sunRaysImage, 0);
R_AttachFBOTextureDepth(tr.renderDepthImage->texnum);
R_CheckFBO(tr.sunRaysFbo);
}
// FIXME: Don't use separate color/depth buffers for a shadow buffer
for( i = 0; i < MAX_DRAWN_PSHADOWS; i++)
{
tr.pshadowFbos[i] = FBO_Create(va("_shadowmap%d", i), tr.pshadowMaps[i]->width, tr.pshadowMaps[i]->height);
FBO_Bind(tr.pshadowFbos[i]);
//FBO_CreateBuffer(tr.pshadowFbos[i], GL_RGBA8, 0, 0);
FBO_AttachTextureImage(tr.pshadowMaps[i], 0);
FBO_CreateBuffer(tr.pshadowFbos[i], GL_DEPTH_COMPONENT24_ARB, 0, 0);
//R_AttachFBOTextureDepth(tr.textureDepthImage->texnum);
R_CheckFBO(tr.pshadowFbos[i]);
}
for ( i = 0; i < 3; i++)
{
tr.sunShadowFbo[i] = FBO_Create("_sunshadowmap", tr.sunShadowDepthImage[i]->width, tr.sunShadowDepthImage[i]->height);
FBO_Bind(tr.sunShadowFbo[i]);
//FBO_CreateBuffer(tr.sunShadowFbo[i], GL_RGBA8, 0, 0);
//FBO_AttachTextureImage(tr.sunShadowImage, 0);
qglDrawBuffer(GL_NONE);
qglReadBuffer(GL_NONE);
//FBO_CreateBuffer(tr.sunShadowFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0);
R_AttachFBOTextureDepth(tr.sunShadowDepthImage[i]->texnum);
R_CheckFBO(tr.sunShadowFbo[i]);
}
for (i = 0; i < 2; i++)
{
tr.textureScratchFbo[i] = FBO_Create(va("_texturescratch%d", i), tr.textureScratchImage[i]->width, tr.textureScratchImage[i]->height);
FBO_Bind(tr.textureScratchFbo[i]);
//FBO_CreateBuffer(tr.textureScratchFbo[i], GL_RGBA8, 0, 0);
FBO_AttachTextureImage(tr.textureScratchImage[i], 0);
R_CheckFBO(tr.textureScratchFbo[i]);
}
{
tr.calcLevelsFbo = FBO_Create("_calclevels", tr.calcLevelsImage->width, tr.calcLevelsImage->height);
FBO_Bind(tr.calcLevelsFbo);
//FBO_CreateBuffer(tr.calcLevelsFbo, hdrFormat, 0, 0);
FBO_AttachTextureImage(tr.calcLevelsImage, 0);
R_CheckFBO(tr.calcLevelsFbo);
}
{
tr.targetLevelsFbo = FBO_Create("_targetlevels", tr.targetLevelsImage->width, tr.targetLevelsImage->height);
FBO_Bind(tr.targetLevelsFbo);
//FBO_CreateBuffer(tr.targetLevelsFbo, hdrFormat, 0, 0);
FBO_AttachTextureImage(tr.targetLevelsImage, 0);
R_CheckFBO(tr.targetLevelsFbo);
}
if (r_softOverbright->integer)
{
//tr.screenScratchFbo = FBO_Create("_screenscratch", width, height);
tr.screenScratchFbo = FBO_Create("_screenscratch", tr.screenScratchImage->width, tr.screenScratchImage->height);
FBO_Bind(tr.screenScratchFbo);
//FBO_CreateBuffer(tr.screenScratchFbo, format, 0, 0);
FBO_AttachTextureImage(tr.screenScratchImage, 0);
// FIXME: hack: share zbuffer between render fbo and pre-screen fbo
//FBO_CreateBuffer(tr.screenScratchFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0);
R_AttachFBOTextureDepth(tr.renderDepthImage->texnum);
R_CheckFBO(tr.screenScratchFbo);
}
for (i = 0; i < 2; i++)
{
tr.quarterFbo[i] = FBO_Create(va("_quarter%d", i), tr.quarterImage[i]->width, tr.quarterImage[i]->height);
FBO_Bind(tr.quarterFbo[i]);
//FBO_CreateBuffer(tr.quarterFbo[i], hdrFormat, 0, 0);
FBO_AttachTextureImage(tr.quarterImage[i], 0);
R_CheckFBO(tr.quarterFbo[i]);
}
{
tr.screenShadowFbo = FBO_Create("_screenshadow", tr.screenShadowImage->width, tr.screenShadowImage->height);
FBO_Bind(tr.screenShadowFbo);
FBO_AttachTextureImage(tr.screenShadowImage, 0);
R_CheckFBO(tr.screenShadowFbo);
}
if (r_ssao->integer)
{
tr.hdrDepthFbo = FBO_Create("_hdrDepth", tr.hdrDepthImage->width, tr.hdrDepthImage->height);
FBO_Bind(tr.hdrDepthFbo);
FBO_AttachTextureImage(tr.hdrDepthImage, 0);
R_CheckFBO(tr.hdrDepthFbo);
tr.screenSsaoFbo = FBO_Create("_screenssao", tr.screenSsaoImage->width, tr.screenSsaoImage->height);
FBO_Bind(tr.screenSsaoFbo);
FBO_AttachTextureImage(tr.screenSsaoImage, 0);
R_CheckFBO(tr.screenSsaoFbo);
}
GL_CheckErrors();
FBO_Bind(NULL);
}
/*
============
FBO_Shutdown
============
*/
void FBO_Shutdown(void)
{
int i, j;
FBO_t *fbo;
ri.Printf(PRINT_ALL, "------- FBO_Shutdown -------\n");
if(!glRefConfig.framebufferObject)
return;
FBO_Bind(NULL);
for(i = 0; i < tr.numFBOs; i++)
{
fbo = tr.fbos[i];
for(j = 0; j < glRefConfig.maxColorAttachments; j++)
{
if(fbo->colorBuffers[j])
qglDeleteRenderbuffersEXT(1, &fbo->colorBuffers[j]);
}
if(fbo->depthBuffer)
qglDeleteRenderbuffersEXT(1, &fbo->depthBuffer);
if(fbo->stencilBuffer)
qglDeleteRenderbuffersEXT(1, &fbo->stencilBuffer);
if(fbo->frameBuffer)
qglDeleteFramebuffersEXT(1, &fbo->frameBuffer);
}
}
/*
============
R_FBOList_f
============
*/
void R_FBOList_f(void)
{
int i;
FBO_t *fbo;
if(!glRefConfig.framebufferObject)
{
ri.Printf(PRINT_ALL, "GL_EXT_framebuffer_object is not available.\n");
return;
}
ri.Printf(PRINT_ALL, " size name\n");
ri.Printf(PRINT_ALL, "----------------------------------------------------------\n");
for(i = 0; i < tr.numFBOs; i++)
{
fbo = tr.fbos[i];
ri.Printf(PRINT_ALL, " %4i: %4i %4i %s\n", i, fbo->width, fbo->height, fbo->name);
}
ri.Printf(PRINT_ALL, " %i FBOs\n", tr.numFBOs);
}
// FIXME
extern void RB_SetGL2D (void);
void FBO_BlitFromTexture(struct image_s *src, vec4i_t inSrcBox, vec2_t inSrcTexScale, FBO_t *dst, vec4i_t inDstBox, struct shaderProgram_s *shaderProgram, vec4_t inColor, int blend)
{
vec4i_t dstBox, srcBox;
vec2_t srcTexScale;
vec4_t color;
vec4_t quadVerts[4];
vec2_t texCoords[4];
vec2_t invTexRes;
FBO_t *oldFbo = glState.currentFBO;
matrix_t projection;
int width, height;
if (!src)
return;
if (inSrcBox)
{
VectorSet4(srcBox, inSrcBox[0], inSrcBox[1], inSrcBox[0] + inSrcBox[2], inSrcBox[1] + inSrcBox[3]);
}
else
{
VectorSet4(srcBox, 0, 0, src->width, src->height);
}
// framebuffers are 0 bottom, Y up.
if (inDstBox)
{
if (dst)
{
dstBox[0] = inDstBox[0];
dstBox[1] = dst->height - inDstBox[1] - inDstBox[3];
dstBox[2] = inDstBox[0] + inDstBox[2];
dstBox[3] = dst->height - inDstBox[1];
}
else
{
dstBox[0] = inDstBox[0];
dstBox[1] = glConfig.vidHeight - inDstBox[1] - inDstBox[3];
dstBox[2] = inDstBox[0] + inDstBox[2];
dstBox[3] = glConfig.vidHeight - inDstBox[1];
}
}
else if (dst)
{
VectorSet4(dstBox, 0, dst->height, dst->width, 0);
}
else
{
VectorSet4(dstBox, 0, glConfig.vidHeight, glConfig.vidWidth, 0);
}
if (inSrcTexScale)
{
VectorCopy2(inSrcTexScale, srcTexScale);
}
else
{
srcTexScale[0] = srcTexScale[1] = 1.0f;
}
if (inColor)
{
VectorCopy4(inColor, color);
}
else
{
VectorCopy4(colorWhite, color);
}
if (!shaderProgram)
{
shaderProgram = &tr.textureColorShader;
}
FBO_Bind(dst);
if (glState.currentFBO)
{
width = glState.currentFBO->width;
height = glState.currentFBO->height;
}
else
{
width = glConfig.vidWidth;
height = glConfig.vidHeight;
}
qglViewport( 0, 0, width, height );
qglScissor( 0, 0, width, height );
Matrix16Ortho(0, width, height, 0, 0, 1, projection);
qglDisable( GL_CULL_FACE );
GL_BindToTMU(src, TB_COLORMAP);
VectorSet4(quadVerts[0], dstBox[0], dstBox[1], 0, 1);
VectorSet4(quadVerts[1], dstBox[2], dstBox[1], 0, 1);
VectorSet4(quadVerts[2], dstBox[2], dstBox[3], 0, 1);
VectorSet4(quadVerts[3], dstBox[0], dstBox[3], 0, 1);
texCoords[0][0] = srcBox[0] / (float)src->width; texCoords[0][1] = 1.0f - srcBox[1] / (float)src->height;
texCoords[1][0] = srcBox[2] / (float)src->width; texCoords[1][1] = 1.0f - srcBox[1] / (float)src->height;
texCoords[2][0] = srcBox[2] / (float)src->width; texCoords[2][1] = 1.0f - srcBox[3] / (float)src->height;
texCoords[3][0] = srcBox[0] / (float)src->width; texCoords[3][1] = 1.0f - srcBox[3] / (float)src->height;
invTexRes[0] = 1.0f / src->width * srcTexScale[0];
invTexRes[1] = 1.0f / src->height * srcTexScale[1];
GL_State( blend );
GLSL_BindProgram(shaderProgram);
GLSL_SetUniformMatrix16(shaderProgram, UNIFORM_MODELVIEWPROJECTIONMATRIX, projection);
GLSL_SetUniformVec4(shaderProgram, UNIFORM_COLOR, color);
GLSL_SetUniformVec2(shaderProgram, UNIFORM_INVTEXRES, invTexRes);
GLSL_SetUniformVec2(shaderProgram, UNIFORM_AUTOEXPOSUREMINMAX, tr.refdef.autoExposureMinMax);
GLSL_SetUniformVec3(shaderProgram, UNIFORM_TONEMINAVGMAXLINEAR, tr.refdef.toneMinAvgMaxLinear);
RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes);
FBO_Bind(oldFbo);
}
void FBO_Blit(FBO_t *src, vec4i_t inSrcBox, vec2_t srcTexScale, FBO_t *dst, vec4i_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend)
{
vec4i_t srcBox;
if (!src)
return;
// framebuffers are 0 bottom, Y up.
if (inSrcBox)
{
srcBox[0] = inSrcBox[0];
srcBox[1] = src->height - inSrcBox[1] - inSrcBox[3];
srcBox[2] = inSrcBox[2];
srcBox[3] = inSrcBox[3];
}
else
{
VectorSet4(srcBox, 0, src->height, src->width, -src->height);
}
FBO_BlitFromTexture(src->colorImage[0], srcBox, srcTexScale, dst, dstBox, shaderProgram, color, blend | GLS_DEPTHTEST_DISABLE);
}
void FBO_FastBlit(FBO_t *src, vec4i_t srcBox, FBO_t *dst, vec4i_t dstBox, int buffers, int filter)
{
vec4i_t srcBoxFinal, dstBoxFinal;
GLuint srcFb, dstFb;
if (!glRefConfig.framebufferBlit)
{
FBO_Blit(src, srcBox, NULL, dst, dstBox, NULL, NULL, 0);
return;
}
// get to a neutral state first
//FBO_Bind(NULL);
srcFb = src ? src->frameBuffer : 0;
dstFb = dst ? dst->frameBuffer : 0;
if (!srcBox)
{
if (src)
{
VectorSet4(srcBoxFinal, 0, 0, src->width, src->height);
}
else
{
VectorSet4(srcBoxFinal, 0, 0, glConfig.vidWidth, glConfig.vidHeight);
}
}
else
{
VectorSet4(srcBoxFinal, srcBox[0], srcBox[1], srcBox[0] + srcBox[2], srcBox[1] + srcBox[3]);
}
if (!dstBox)
{
if (dst)
{
VectorSet4(dstBoxFinal, 0, 0, dst->width, dst->height);
}
else
{
VectorSet4(dstBoxFinal, 0, 0, glConfig.vidWidth, glConfig.vidHeight);
}
}
else
{
VectorSet4(dstBoxFinal, dstBox[0], dstBox[1], dstBox[0] + dstBox[2], dstBox[1] + dstBox[3]);
}
qglBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, srcFb);
qglBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dstFb);
qglBlitFramebufferEXT(srcBoxFinal[0], srcBoxFinal[1], srcBoxFinal[2], srcBoxFinal[3],
dstBoxFinal[0], dstBoxFinal[1], dstBoxFinal[2], dstBoxFinal[3],
buffers, filter);
qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glState.currentFBO = NULL;
}

64
code/renderergl2/tr_fbo.h Normal file
View file

@ -0,0 +1,64 @@
/*
===========================================================================
Copyright (C) 2010 James Canete (use.less01@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_fbo.h
#ifndef __TR_FBO_H__
#define __TR_FBO_H__
struct image_s;
struct shaderProgram_s;
typedef struct FBO_s
{
char name[MAX_QPATH];
int index;
uint32_t frameBuffer;
uint32_t colorBuffers[16];
int colorFormat;
struct image_s *colorImage[16];
uint32_t depthBuffer;
int depthFormat;
uint32_t stencilBuffer;
int stencilFormat;
uint32_t packedDepthStencilBuffer;
int packedDepthStencilFormat;
int width;
int height;
} FBO_t;
void FBO_Bind(FBO_t *fbo);
void FBO_Init(void);
void FBO_Shutdown(void);
void FBO_BlitFromTexture(struct image_s *src, vec4i_t srcBox, vec2_t srcTexScale, FBO_t *dst, vec4i_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend);
void FBO_Blit(FBO_t *src, vec4i_t srcBox, vec2_t srcTexScale, FBO_t *dst, vec4i_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend);
void FBO_FastBlit(FBO_t *src, vec4i_t srcBox, FBO_t *dst, vec4i_t dstBox, int buffers, int filter);
#endif

View file

@ -0,0 +1,532 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_flares.c
#include "tr_local.h"
/*
=============================================================================
LIGHT FLARES
A light flare is an effect that takes place inside the eye when bright light
sources are visible. The size of the flare reletive to the screen is nearly
constant, irrespective of distance, but the intensity should be proportional to the
projected area of the light source.
A surface that has been flagged as having a light flare will calculate the depth
buffer value that its midpoint should have when the surface is added.
After all opaque surfaces have been rendered, the depth buffer is read back for
each flare in view. If the point has not been obscured by a closer surface, the
flare should be drawn.
Surfaces that have a repeated texture should never be flagged as flaring, because
there will only be a single flare added at the midpoint of the polygon.
To prevent abrupt popping, the intensity of the flare is interpolated up and
down as it changes visibility. This involves scene to scene state, unlike almost
all other aspects of the renderer, and is complicated by the fact that a single
frame may have multiple scenes.
RB_RenderFlares() will be called once per view (twice in a mirrored scene, potentially
up to five or more times in a frame with 3D status bar icons).
=============================================================================
*/
// flare states maintain visibility over multiple frames for fading
// layers: view, mirror, menu
typedef struct flare_s {
struct flare_s *next; // for active chain
int addedFrame;
qboolean inPortal; // true if in a portal view of the scene
int frameSceneNum;
void *surface;
int fogNum;
int fadeTime;
qboolean visible; // state of last test
float drawIntensity; // may be non 0 even if !visible due to fading
int windowX, windowY;
float eyeZ;
vec3_t origin;
vec3_t color;
} flare_t;
#define MAX_FLARES 128
flare_t r_flareStructs[MAX_FLARES];
flare_t *r_activeFlares, *r_inactiveFlares;
int flareCoeff;
/*
==================
R_ClearFlares
==================
*/
void R_ClearFlares( void ) {
int i;
Com_Memset( r_flareStructs, 0, sizeof( r_flareStructs ) );
r_activeFlares = NULL;
r_inactiveFlares = NULL;
for ( i = 0 ; i < MAX_FLARES ; i++ ) {
r_flareStructs[i].next = r_inactiveFlares;
r_inactiveFlares = &r_flareStructs[i];
}
}
/*
==================
RB_AddFlare
This is called at surface tesselation time
==================
*/
void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ) {
int i;
flare_t *f;
vec3_t local;
float d = 1;
vec4_t eye, clip, normalized, window;
backEnd.pc.c_flareAdds++;
if(normal && (normal[0] || normal[1] || normal[2]))
{
VectorSubtract( backEnd.viewParms.or.origin, point, local );
VectorNormalizeFast(local);
d = DotProduct(local, normal);
// If the viewer is behind the flare don't add it.
if(d < 0)
return;
}
// if the point is off the screen, don't bother adding it
// calculate screen coordinates and depth
R_TransformModelToClip( point, backEnd.or.modelMatrix,
backEnd.viewParms.projectionMatrix, eye, clip );
// check to see if the point is completely off screen
for ( i = 0 ; i < 3 ; i++ ) {
if ( clip[i] >= clip[3] || clip[i] <= -clip[3] ) {
return;
}
}
R_TransformClipToWindow( clip, &backEnd.viewParms, normalized, window );
if ( window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth
|| window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight ) {
return; // shouldn't happen, since we check the clip[] above, except for FP rounding
}
// see if a flare with a matching surface, scene, and view exists
for ( f = r_activeFlares ; f ; f = f->next ) {
if ( f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum
&& f->inPortal == backEnd.viewParms.isPortal ) {
break;
}
}
// allocate a new one
if (!f ) {
if ( !r_inactiveFlares ) {
// the list is completely full
return;
}
f = r_inactiveFlares;
r_inactiveFlares = r_inactiveFlares->next;
f->next = r_activeFlares;
r_activeFlares = f;
f->surface = surface;
f->frameSceneNum = backEnd.viewParms.frameSceneNum;
f->inPortal = backEnd.viewParms.isPortal;
f->addedFrame = -1;
}
if ( f->addedFrame != backEnd.viewParms.frameCount - 1 ) {
f->visible = qfalse;
f->fadeTime = backEnd.refdef.time - 2000;
}
f->addedFrame = backEnd.viewParms.frameCount;
f->fogNum = fogNum;
VectorCopy(point, f->origin);
VectorCopy( color, f->color );
// fade the intensity of the flare down as the
// light surface turns away from the viewer
VectorScale( f->color, d, f->color );
// save info needed to test
f->windowX = backEnd.viewParms.viewportX + window[0];
f->windowY = backEnd.viewParms.viewportY + window[1];
f->eyeZ = eye[2];
}
/*
==================
RB_AddDlightFlares
==================
*/
void RB_AddDlightFlares( void ) {
dlight_t *l;
int i, j, k;
fog_t *fog = NULL;
if ( !r_flares->integer ) {
return;
}
l = backEnd.refdef.dlights;
if(tr.world)
fog = tr.world->fogs;
for (i=0 ; i<backEnd.refdef.num_dlights ; i++, l++) {
if(fog)
{
// find which fog volume the light is in
for ( j = 1 ; j < tr.world->numfogs ; j++ ) {
fog = &tr.world->fogs[j];
for ( k = 0 ; k < 3 ; k++ ) {
if ( l->origin[k] < fog->bounds[0][k] || l->origin[k] > fog->bounds[1][k] ) {
break;
}
}
if ( k == 3 ) {
break;
}
}
if ( j == tr.world->numfogs ) {
j = 0;
}
}
else
j = 0;
RB_AddFlare( (void *)l, j, l->origin, l->color, NULL );
}
}
/*
===============================================================================
FLARE BACK END
===============================================================================
*/
/*
==================
RB_TestFlare
==================
*/
void RB_TestFlare( flare_t *f ) {
float depth;
qboolean visible;
float fade;
float screenZ;
backEnd.pc.c_flareTests++;
// doing a readpixels is as good as doing a glFinish(), so
// don't bother with another sync
glState.finishCalled = qfalse;
// read back the z buffer contents
qglReadPixels( f->windowX, f->windowY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth );
screenZ = backEnd.viewParms.projectionMatrix[14] /
( ( 2*depth - 1 ) * backEnd.viewParms.projectionMatrix[11] - backEnd.viewParms.projectionMatrix[10] );
visible = ( -f->eyeZ - -screenZ ) < 24;
if ( visible ) {
if ( !f->visible ) {
f->visible = qtrue;
f->fadeTime = backEnd.refdef.time - 1;
}
fade = ( ( backEnd.refdef.time - f->fadeTime ) /1000.0f ) * r_flareFade->value;
} else {
if ( f->visible ) {
f->visible = qfalse;
f->fadeTime = backEnd.refdef.time - 1;
}
fade = 1.0f - ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value;
}
if ( fade < 0 ) {
fade = 0;
}
if ( fade > 1 ) {
fade = 1;
}
f->drawIntensity = fade;
}
/*
==================
RB_RenderFlare
==================
*/
void RB_RenderFlare( flare_t *f ) {
float size;
vec3_t color;
int iColor[3];
float distance, intensity, factor;
byte fogFactors[3] = {255, 255, 255};
backEnd.pc.c_flareRenders++;
// We don't want too big values anyways when dividing by distance.
if(f->eyeZ > -1.0f)
distance = 1.0f;
else
distance = -f->eyeZ;
// calculate the flare size..
size = backEnd.viewParms.viewportWidth * ( r_flareSize->value/640.0f + 8 / distance );
/*
* This is an alternative to intensity scaling. It changes the size of the flare on screen instead
* with growing distance. See in the description at the top why this is not the way to go.
// size will change ~ 1/r.
size = backEnd.viewParms.viewportWidth * (r_flareSize->value / (distance * -2.0f));
*/
/*
* As flare sizes stay nearly constant with increasing distance we must decrease the intensity
* to achieve a reasonable visual result. The intensity is ~ (size^2 / distance^2) which can be
* got by considering the ratio of
* (flaresurface on screen) : (Surface of sphere defined by flare origin and distance from flare)
* An important requirement is:
* intensity <= 1 for all distances.
*
* The formula used here to compute the intensity is as follows:
* intensity = flareCoeff * size^2 / (distance + size*sqrt(flareCoeff))^2
* As you can see, the intensity will have a max. of 1 when the distance is 0.
* The coefficient flareCoeff will determine the falloff speed with increasing distance.
*/
factor = distance + size * sqrt(flareCoeff);
intensity = flareCoeff * size * size / (factor * factor);
VectorScale(f->color, f->drawIntensity * intensity, color);
// Calculations for fogging
if(tr.world && f->fogNum < tr.world->numfogs)
{
tess.numVertexes = 1;
VectorCopy(f->origin, tess.xyz[0]);
tess.fogNum = f->fogNum;
RB_CalcModulateColorsByFog(fogFactors);
// We don't need to render the flare if colors are 0 anyways.
if(!(fogFactors[0] || fogFactors[1] || fogFactors[2]))
return;
}
iColor[0] = color[0] * fogFactors[0];
iColor[1] = color[1] * fogFactors[1];
iColor[2] = color[2] * fogFactors[2];
RB_BeginSurface( tr.flareShader, f->fogNum );
// FIXME: use quadstamp?
tess.xyz[tess.numVertexes][0] = f->windowX - size;
tess.xyz[tess.numVertexes][1] = f->windowY - size;
tess.texCoords[tess.numVertexes][0][0] = 0;
tess.texCoords[tess.numVertexes][0][1] = 0;
tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f;
tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f;
tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f;
tess.vertexColors[tess.numVertexes][3] = 1.0f;
tess.numVertexes++;
tess.xyz[tess.numVertexes][0] = f->windowX - size;
tess.xyz[tess.numVertexes][1] = f->windowY + size;
tess.texCoords[tess.numVertexes][0][0] = 0;
tess.texCoords[tess.numVertexes][0][1] = 1;
tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f;
tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f;
tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f;
tess.vertexColors[tess.numVertexes][3] = 1.0f;
tess.numVertexes++;
tess.xyz[tess.numVertexes][0] = f->windowX + size;
tess.xyz[tess.numVertexes][1] = f->windowY + size;
tess.texCoords[tess.numVertexes][0][0] = 1;
tess.texCoords[tess.numVertexes][0][1] = 1;
tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f;
tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f;
tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f;
tess.vertexColors[tess.numVertexes][3] = 1.0f;
tess.numVertexes++;
tess.xyz[tess.numVertexes][0] = f->windowX + size;
tess.xyz[tess.numVertexes][1] = f->windowY - size;
tess.texCoords[tess.numVertexes][0][0] = 1;
tess.texCoords[tess.numVertexes][0][1] = 0;
tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f;
tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f;
tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f;
tess.vertexColors[tess.numVertexes][3] = 1.0f;
tess.numVertexes++;
tess.indexes[tess.numIndexes++] = 0;
tess.indexes[tess.numIndexes++] = 1;
tess.indexes[tess.numIndexes++] = 2;
tess.indexes[tess.numIndexes++] = 0;
tess.indexes[tess.numIndexes++] = 2;
tess.indexes[tess.numIndexes++] = 3;
RB_EndSurface();
}
/*
==================
RB_RenderFlares
Because flares are simulating an occular effect, they should be drawn after
everything (all views) in the entire frame has been drawn.
Because of the way portals use the depth buffer to mark off areas, the
needed information would be lost after each view, so we are forced to draw
flares after each view.
The resulting artifact is that flares in mirrors or portals don't dim properly
when occluded by something in the main view, and portal flares that should
extend past the portal edge will be overwritten.
==================
*/
void RB_RenderFlares (void) {
flare_t *f;
flare_t **prev;
qboolean draw;
matrix_t oldmodelview, oldprojection, matrix;
if ( !r_flares->integer ) {
return;
}
if(r_flareCoeff->modified)
{
if(r_flareCoeff->value == 0.0f)
flareCoeff = atof(FLARE_STDCOEFF);
else
flareCoeff = r_flareCoeff->value;
r_flareCoeff->modified = qfalse;
}
// Reset currentEntity to world so that any previously referenced entities
// don't have influence on the rendering of these flares (i.e. RF_ renderer flags).
backEnd.currentEntity = &tr.worldEntity;
backEnd.or = backEnd.viewParms.world;
// RB_AddDlightFlares();
// perform z buffer readback on each flare in this view
draw = qfalse;
prev = &r_activeFlares;
while ( ( f = *prev ) != NULL ) {
// throw out any flares that weren't added last frame
if ( f->addedFrame < backEnd.viewParms.frameCount - 1 ) {
*prev = f->next;
f->next = r_inactiveFlares;
r_inactiveFlares = f;
continue;
}
// don't draw any here that aren't from this scene / portal
f->drawIntensity = 0;
if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum
&& f->inPortal == backEnd.viewParms.isPortal ) {
RB_TestFlare( f );
if ( f->drawIntensity ) {
draw = qtrue;
} else {
// this flare has completely faded out, so remove it from the chain
*prev = f->next;
f->next = r_inactiveFlares;
r_inactiveFlares = f;
continue;
}
}
prev = &f->next;
}
if ( !draw ) {
return; // none visible
}
if ( backEnd.viewParms.isPortal ) {
qglDisable (GL_CLIP_PLANE0);
}
Matrix16Copy(glState.projection, oldprojection);
Matrix16Copy(glState.modelview, oldmodelview);
Matrix16Identity(matrix);
GL_SetModelviewMatrix(matrix);
Matrix16Ortho( backEnd.viewParms.viewportX, backEnd.viewParms.viewportX + backEnd.viewParms.viewportWidth,
backEnd.viewParms.viewportY, backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight,
-99999, 99999, matrix );
GL_SetProjectionMatrix(matrix);
for ( f = r_activeFlares ; f ; f = f->next ) {
if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum
&& f->inPortal == backEnd.viewParms.isPortal
&& f->drawIntensity ) {
RB_RenderFlare( f );
}
}
GL_SetProjectionMatrix(oldprojection);
GL_SetModelviewMatrix(oldmodelview);
}

1831
code/renderergl2/tr_glsl.c Normal file

File diff suppressed because it is too large Load diff

3358
code/renderergl2/tr_image.c Normal file

File diff suppressed because it is too large Load diff

1574
code/renderergl2/tr_init.c Normal file

File diff suppressed because it is too large Load diff

451
code/renderergl2/tr_light.c Normal file
View file

@ -0,0 +1,451 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_light.c
#include "tr_local.h"
#define DLIGHT_AT_RADIUS 16
// at the edge of a dlight's influence, this amount of light will be added
#define DLIGHT_MINIMUM_RADIUS 16
// never calculate a range less than this to prevent huge light numbers
/*
===============
R_TransformDlights
Transforms the origins of an array of dlights.
Used by both the front end (for DlightBmodel) and
the back end (before doing the lighting calculation)
===============
*/
void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or) {
int i;
vec3_t temp;
for ( i = 0 ; i < count ; i++, dl++ ) {
VectorSubtract( dl->origin, or->origin, temp );
dl->transformed[0] = DotProduct( temp, or->axis[0] );
dl->transformed[1] = DotProduct( temp, or->axis[1] );
dl->transformed[2] = DotProduct( temp, or->axis[2] );
}
}
/*
=============
R_DlightBmodel
Determine which dynamic lights may effect this bmodel
=============
*/
void R_DlightBmodel( bmodel_t *bmodel ) {
int i, j;
dlight_t *dl;
int mask;
msurface_t *surf;
// transform all the lights
R_TransformDlights( tr.refdef.num_dlights, tr.refdef.dlights, &tr.or );
mask = 0;
for ( i=0 ; i<tr.refdef.num_dlights ; i++ ) {
dl = &tr.refdef.dlights[i];
// see if the point is close enough to the bounds to matter
for ( j = 0 ; j < 3 ; j++ ) {
if ( dl->transformed[j] - bmodel->bounds[1][j] > dl->radius ) {
break;
}
if ( bmodel->bounds[0][j] - dl->transformed[j] > dl->radius ) {
break;
}
}
if ( j < 3 ) {
continue;
}
// we need to check this light
mask |= 1 << i;
}
tr.currentEntity->needDlights = (mask != 0);
// set the dlight bits in all the surfaces
for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) {
surf = tr.world->surfaces + bmodel->firstSurface + i;
if ( *surf->data == SF_FACE ) {
((srfSurfaceFace_t *)surf->data)->dlightBits = mask;
} else if ( *surf->data == SF_GRID ) {
((srfGridMesh_t *)surf->data)->dlightBits = mask;
} else if ( *surf->data == SF_TRIANGLES ) {
((srfTriangles_t *)surf->data)->dlightBits = mask;
}
}
}
/*
=============================================================================
LIGHT SAMPLING
=============================================================================
*/
extern cvar_t *r_ambientScale;
extern cvar_t *r_directedScale;
extern cvar_t *r_debugLight;
/*
=================
R_SetupEntityLightingGrid
=================
*/
static void R_SetupEntityLightingGrid( trRefEntity_t *ent, world_t *world ) {
vec3_t lightOrigin;
int pos[3];
int i, j;
byte *gridData;
float frac[3];
int gridStep[3];
vec3_t direction;
float totalFactor;
if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) {
// seperate lightOrigins are needed so an object that is
// sinking into the ground can still be lit, and so
// multi-part models can be lit identically
VectorCopy( ent->e.lightingOrigin, lightOrigin );
} else {
VectorCopy( ent->e.origin, lightOrigin );
}
VectorSubtract( lightOrigin, world->lightGridOrigin, lightOrigin );
for ( i = 0 ; i < 3 ; i++ ) {
float v;
v = lightOrigin[i]*world->lightGridInverseSize[i];
pos[i] = floor( v );
frac[i] = v - pos[i];
if ( pos[i] < 0 ) {
pos[i] = 0;
} else if ( pos[i] >= world->lightGridBounds[i] - 1 ) {
pos[i] = world->lightGridBounds[i] - 1;
}
}
VectorClear( ent->ambientLight );
VectorClear( ent->directedLight );
VectorClear( direction );
assert( world->lightGridData ); // NULL with -nolight maps
// trilerp the light value
gridStep[0] = 8;
gridStep[1] = 8 * world->lightGridBounds[0];
gridStep[2] = 8 * world->lightGridBounds[0] * world->lightGridBounds[1];
gridData = world->lightGridData + pos[0] * gridStep[0]
+ pos[1] * gridStep[1] + pos[2] * gridStep[2];
totalFactor = 0;
for ( i = 0 ; i < 8 ; i++ ) {
float factor;
byte *data;
int lat, lng;
vec3_t normal;
qboolean ignore;
#if idppc
float d0, d1, d2, d3, d4, d5;
#endif
factor = 1.0;
data = gridData;
ignore = qfalse;
for ( j = 0 ; j < 3 ; j++ ) {
if ( i & (1<<j) ) {
if ((pos[j] + 1) >= world->lightGridBounds[j] - 1)
{
ignore = qtrue; // ignore values outside lightgrid
}
factor *= frac[j];
data += gridStep[j];
} else {
factor *= (1.0f - frac[j]);
}
}
if ( ignore )
continue;
if (world->hdrLightGrid)
{
float *hdrData = world->hdrLightGrid + (int)(data - world->lightGridData) / 8 * 6;
if (!(hdrData[0]+hdrData[1]+hdrData[2]+hdrData[3]+hdrData[4]+hdrData[5]) ) {
continue; // ignore samples in walls
}
}
else
{
if (!(data[0]+data[1]+data[2]+data[3]+data[4]+data[5]) ) {
continue; // ignore samples in walls
}
}
totalFactor += factor;
#if idppc
d0 = data[0]; d1 = data[1]; d2 = data[2];
d3 = data[3]; d4 = data[4]; d5 = data[5];
ent->ambientLight[0] += factor * d0;
ent->ambientLight[1] += factor * d1;
ent->ambientLight[2] += factor * d2;
ent->directedLight[0] += factor * d3;
ent->directedLight[1] += factor * d4;
ent->directedLight[2] += factor * d5;
#else
if (world->hdrLightGrid)
{
// FIXME: this is hideous
float *hdrData = world->hdrLightGrid + (int)(data - world->lightGridData) / 8 * 6;
ent->ambientLight[0] += factor * hdrData[0];
ent->ambientLight[1] += factor * hdrData[1];
ent->ambientLight[2] += factor * hdrData[2];
ent->directedLight[0] += factor * hdrData[3];
ent->directedLight[1] += factor * hdrData[4];
ent->directedLight[2] += factor * hdrData[5];
}
else
{
ent->ambientLight[0] += factor * data[0];
ent->ambientLight[1] += factor * data[1];
ent->ambientLight[2] += factor * data[2];
ent->directedLight[0] += factor * data[3];
ent->directedLight[1] += factor * data[4];
ent->directedLight[2] += factor * data[5];
}
#endif
lat = data[7];
lng = data[6];
lat *= (FUNCTABLE_SIZE/256);
lng *= (FUNCTABLE_SIZE/256);
// decode X as cos( lat ) * sin( long )
// decode Y as sin( lat ) * sin( long )
// decode Z as cos( long )
normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng];
normal[1] = tr.sinTable[lat] * tr.sinTable[lng];
normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK];
VectorMA( direction, factor, normal, direction );
}
if ( totalFactor > 0 && totalFactor < 0.99 ) {
totalFactor = 1.0f / totalFactor;
VectorScale( ent->ambientLight, totalFactor, ent->ambientLight );
VectorScale( ent->directedLight, totalFactor, ent->directedLight );
}
VectorScale( ent->ambientLight, r_ambientScale->value, ent->ambientLight );
VectorScale( ent->directedLight, r_directedScale->value, ent->directedLight );
VectorNormalize2( direction, ent->lightDir );
}
/*
===============
LogLight
===============
*/
static void LogLight( trRefEntity_t *ent ) {
int max1, max2;
if ( !(ent->e.renderfx & RF_FIRST_PERSON ) ) {
return;
}
max1 = ent->ambientLight[0];
if ( ent->ambientLight[1] > max1 ) {
max1 = ent->ambientLight[1];
} else if ( ent->ambientLight[2] > max1 ) {
max1 = ent->ambientLight[2];
}
max2 = ent->directedLight[0];
if ( ent->directedLight[1] > max2 ) {
max2 = ent->directedLight[1];
} else if ( ent->directedLight[2] > max2 ) {
max2 = ent->directedLight[2];
}
ri.Printf( PRINT_ALL, "amb:%i dir:%i\n", max1, max2 );
}
/*
=================
R_SetupEntityLighting
Calculates all the lighting values that will be used
by the Calc_* functions
=================
*/
void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) {
int i;
dlight_t *dl;
float power;
vec3_t dir;
float d;
vec3_t lightDir;
vec3_t lightOrigin;
// lighting calculations
if ( ent->lightingCalculated ) {
return;
}
ent->lightingCalculated = qtrue;
//
// trace a sample point down to find ambient light
//
if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) {
// seperate lightOrigins are needed so an object that is
// sinking into the ground can still be lit, and so
// multi-part models can be lit identically
VectorCopy( ent->e.lightingOrigin, lightOrigin );
} else {
VectorCopy( ent->e.origin, lightOrigin );
}
// if NOWORLDMODEL, only use dynamic lights (menu system, etc)
if ( !(refdef->rdflags & RDF_NOWORLDMODEL )
&& tr.world->lightGridData ) {
R_SetupEntityLightingGrid( ent, tr.world );
} else {
ent->ambientLight[0] = ent->ambientLight[1] =
ent->ambientLight[2] = tr.identityLight * 150;
ent->directedLight[0] = ent->directedLight[1] =
ent->directedLight[2] = tr.identityLight * 150;
VectorCopy( tr.sunDirection, ent->lightDir );
}
// bonus items and view weapons have a fixed minimum add
if ( !r_hdr->integer /* ent->e.renderfx & RF_MINLIGHT */ ) {
// give everything a minimum light add
ent->ambientLight[0] += tr.identityLight * 32;
ent->ambientLight[1] += tr.identityLight * 32;
ent->ambientLight[2] += tr.identityLight * 32;
}
//
// modify the light by dynamic lights
//
d = VectorLength( ent->directedLight );
VectorScale( ent->lightDir, d, lightDir );
for ( i = 0 ; i < refdef->num_dlights ; i++ ) {
dl = &refdef->dlights[i];
VectorSubtract( dl->origin, lightOrigin, dir );
d = VectorNormalize( dir );
power = DLIGHT_AT_RADIUS * ( dl->radius * dl->radius );
if ( d < DLIGHT_MINIMUM_RADIUS ) {
d = DLIGHT_MINIMUM_RADIUS;
}
d = power / ( d * d );
VectorMA( ent->directedLight, d, dl->color, ent->directedLight );
VectorMA( lightDir, d, dir, lightDir );
}
// clamp ambient
if ( !r_hdr->integer )
{
for ( i = 0 ; i < 3 ; i++ ) {
if ( ent->ambientLight[i] > tr.identityLightByte ) {
ent->ambientLight[i] = tr.identityLightByte;
}
}
}
if ( r_debugLight->integer ) {
LogLight( ent );
}
// save out the byte packet version
((byte *)&ent->ambientLightInt)[0] = ri.ftol(ent->ambientLight[0]);
((byte *)&ent->ambientLightInt)[1] = ri.ftol(ent->ambientLight[1]);
((byte *)&ent->ambientLightInt)[2] = ri.ftol(ent->ambientLight[2]);
((byte *)&ent->ambientLightInt)[3] = 0xff;
// transform the direction to local space
// no need to do this if using lightentity glsl shader
VectorNormalize( lightDir );
VectorCopy(lightDir, ent->lightDir);
}
/*
=================
R_LightForPoint
=================
*/
int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir )
{
trRefEntity_t ent;
if ( tr.world->lightGridData == NULL )
return qfalse;
Com_Memset(&ent, 0, sizeof(ent));
VectorCopy( point, ent.e.origin );
R_SetupEntityLightingGrid( &ent, tr.world );
VectorCopy(ent.ambientLight, ambientLight);
VectorCopy(ent.directedLight, directedLight);
VectorCopy(ent.lightDir, lightDir);
return qtrue;
}
int R_LightDirForPoint( vec3_t point, vec3_t lightDir, vec3_t normal, world_t *world )
{
trRefEntity_t ent;
if ( world->lightGridData == NULL )
return qfalse;
Com_Memset(&ent, 0, sizeof(ent));
VectorCopy( point, ent.e.origin );
R_SetupEntityLightingGrid( &ent, world );
if (DotProduct(ent.lightDir, normal) > 0.2f)
VectorCopy(ent.lightDir, lightDir);
else
VectorCopy(normal, lightDir);
return qtrue;
}

2680
code/renderergl2/tr_local.h Normal file

File diff suppressed because it is too large Load diff

2876
code/renderergl2/tr_main.c Normal file

File diff suppressed because it is too large Load diff

466
code/renderergl2/tr_marks.c Normal file
View file

@ -0,0 +1,466 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_marks.c -- polygon projection on the world polygons
#include "tr_local.h"
//#include "assert.h"
#define MAX_VERTS_ON_POLY 64
#define MARKER_OFFSET 0 // 1
/*
=============
R_ChopPolyBehindPlane
Out must have space for two more vertexes than in
=============
*/
#define SIDE_FRONT 0
#define SIDE_BACK 1
#define SIDE_ON 2
static void R_ChopPolyBehindPlane( int numInPoints, vec3_t inPoints[MAX_VERTS_ON_POLY],
int *numOutPoints, vec3_t outPoints[MAX_VERTS_ON_POLY],
vec3_t normal, vec_t dist, vec_t epsilon) {
float dists[MAX_VERTS_ON_POLY+4] = { 0 };
int sides[MAX_VERTS_ON_POLY+4] = { 0 };
int counts[3];
float dot;
int i, j;
float *p1, *p2, *clip;
float d;
// don't clip if it might overflow
if ( numInPoints >= MAX_VERTS_ON_POLY - 2 ) {
*numOutPoints = 0;
return;
}
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for ( i = 0 ; i < numInPoints ; i++ ) {
dot = DotProduct( inPoints[i], normal );
dot -= dist;
dists[i] = dot;
if ( dot > epsilon ) {
sides[i] = SIDE_FRONT;
} else if ( dot < -epsilon ) {
sides[i] = SIDE_BACK;
} else {
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
dists[i] = dists[0];
*numOutPoints = 0;
if ( !counts[0] ) {
return;
}
if ( !counts[1] ) {
*numOutPoints = numInPoints;
Com_Memcpy( outPoints, inPoints, numInPoints * sizeof(vec3_t) );
return;
}
for ( i = 0 ; i < numInPoints ; i++ ) {
p1 = inPoints[i];
clip = outPoints[ *numOutPoints ];
if ( sides[i] == SIDE_ON ) {
VectorCopy( p1, clip );
(*numOutPoints)++;
continue;
}
if ( sides[i] == SIDE_FRONT ) {
VectorCopy( p1, clip );
(*numOutPoints)++;
clip = outPoints[ *numOutPoints ];
}
if ( sides[i+1] == SIDE_ON || sides[i+1] == sides[i] ) {
continue;
}
// generate a split point
p2 = inPoints[ (i+1) % numInPoints ];
d = dists[i] - dists[i+1];
if ( d == 0 ) {
dot = 0;
} else {
dot = dists[i] / d;
}
// clip xyz
for (j=0 ; j<3 ; j++) {
clip[j] = p1[j] + dot * ( p2[j] - p1[j] );
}
(*numOutPoints)++;
}
}
/*
=================
R_BoxSurfaces_r
=================
*/
void R_BoxSurfaces_r(mnode_t *node, vec3_t mins, vec3_t maxs, surfaceType_t **list, int listsize, int *listlength, vec3_t dir) {
int s, c;
msurface_t *surf;
int *mark;
// do the tail recursion in a loop
while ( node->contents == -1 ) {
s = BoxOnPlaneSide( mins, maxs, node->plane );
if (s == 1) {
node = node->children[0];
} else if (s == 2) {
node = node->children[1];
} else {
R_BoxSurfaces_r(node->children[0], mins, maxs, list, listsize, listlength, dir);
node = node->children[1];
}
}
// add the individual surfaces
mark = tr.world->marksurfaces + node->firstmarksurface;
c = node->nummarksurfaces;
while (c--) {
int *surfViewCount;
//
if (*listlength >= listsize) break;
//
surfViewCount = &tr.world->surfacesViewCount[*mark];
surf = tr.world->surfaces + *mark;
// check if the surface has NOIMPACT or NOMARKS set
if ( ( surf->shader->surfaceFlags & ( SURF_NOIMPACT | SURF_NOMARKS ) )
|| ( surf->shader->contentFlags & CONTENTS_FOG ) ) {
*surfViewCount = tr.viewCount;
}
// extra check for surfaces to avoid list overflows
else if (*(surf->data) == SF_FACE) {
// the face plane should go through the box
s = BoxOnPlaneSide( mins, maxs, &surf->cullinfo.plane );
if (s == 1 || s == 2) {
*surfViewCount = tr.viewCount;
} else if (DotProduct(surf->cullinfo.plane.normal, dir) > -0.5) {
// don't add faces that make sharp angles with the projection direction
*surfViewCount = tr.viewCount;
}
}
else if (*(surf->data) != SF_GRID &&
*(surf->data) != SF_TRIANGLES)
*surfViewCount = tr.viewCount;
// check the viewCount because the surface may have
// already been added if it spans multiple leafs
if (*surfViewCount != tr.viewCount) {
*surfViewCount = tr.viewCount;
list[*listlength] = surf->data;
(*listlength)++;
}
mark++;
}
}
/*
=================
R_AddMarkFragments
=================
*/
void R_AddMarkFragments(int numClipPoints, vec3_t clipPoints[2][MAX_VERTS_ON_POLY],
int numPlanes, vec3_t *normals, float *dists,
int maxPoints, vec3_t pointBuffer,
int maxFragments, markFragment_t *fragmentBuffer,
int *returnedPoints, int *returnedFragments,
vec3_t mins, vec3_t maxs) {
int pingPong, i;
markFragment_t *mf;
// chop the surface by all the bounding planes of the to be projected polygon
pingPong = 0;
for ( i = 0 ; i < numPlanes ; i++ ) {
R_ChopPolyBehindPlane( numClipPoints, clipPoints[pingPong],
&numClipPoints, clipPoints[!pingPong],
normals[i], dists[i], 0.5 );
pingPong ^= 1;
if ( numClipPoints == 0 ) {
break;
}
}
// completely clipped away?
if ( numClipPoints == 0 ) {
return;
}
// add this fragment to the returned list
if ( numClipPoints + (*returnedPoints) > maxPoints ) {
return; // not enough space for this polygon
}
/*
// all the clip points should be within the bounding box
for ( i = 0 ; i < numClipPoints ; i++ ) {
int j;
for ( j = 0 ; j < 3 ; j++ ) {
if (clipPoints[pingPong][i][j] < mins[j] - 0.5) break;
if (clipPoints[pingPong][i][j] > maxs[j] + 0.5) break;
}
if (j < 3) break;
}
if (i < numClipPoints) return;
*/
mf = fragmentBuffer + (*returnedFragments);
mf->firstPoint = (*returnedPoints);
mf->numPoints = numClipPoints;
Com_Memcpy( pointBuffer + (*returnedPoints) * 3, clipPoints[pingPong], numClipPoints * sizeof(vec3_t) );
(*returnedPoints) += numClipPoints;
(*returnedFragments)++;
}
/*
=================
R_MarkFragments
=================
*/
int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection,
int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) {
int numsurfaces, numPlanes;
int i, j, k, m, n;
surfaceType_t *surfaces[64];
vec3_t mins, maxs;
int returnedFragments;
int returnedPoints;
vec3_t normals[MAX_VERTS_ON_POLY+2];
float dists[MAX_VERTS_ON_POLY+2];
vec3_t clipPoints[2][MAX_VERTS_ON_POLY];
int numClipPoints;
float *v;
srfGridMesh_t *cv;
srfTriangle_t *tri;
srfVert_t *dv;
vec3_t normal;
vec3_t projectionDir;
vec3_t v1, v2;
if (numPoints <= 0) {
return 0;
}
//increment view count for double check prevention
tr.viewCount++;
//
VectorNormalize2( projection, projectionDir );
// find all the brushes that are to be considered
ClearBounds( mins, maxs );
for ( i = 0 ; i < numPoints ; i++ ) {
vec3_t temp;
AddPointToBounds( points[i], mins, maxs );
VectorAdd( points[i], projection, temp );
AddPointToBounds( temp, mins, maxs );
// make sure we get all the leafs (also the one(s) in front of the hit surface)
VectorMA( points[i], -20, projectionDir, temp );
AddPointToBounds( temp, mins, maxs );
}
if (numPoints > MAX_VERTS_ON_POLY) numPoints = MAX_VERTS_ON_POLY;
// create the bounding planes for the to be projected polygon
for ( i = 0 ; i < numPoints ; i++ ) {
VectorSubtract(points[(i+1)%numPoints], points[i], v1);
VectorAdd(points[i], projection, v2);
VectorSubtract(points[i], v2, v2);
CrossProduct(v1, v2, normals[i]);
VectorNormalizeFast(normals[i]);
dists[i] = DotProduct(normals[i], points[i]);
}
// add near and far clipping planes for projection
VectorCopy(projectionDir, normals[numPoints]);
dists[numPoints] = DotProduct(normals[numPoints], points[0]) - 32;
VectorCopy(projectionDir, normals[numPoints+1]);
VectorInverse(normals[numPoints+1]);
dists[numPoints+1] = DotProduct(normals[numPoints+1], points[0]) - 20;
numPlanes = numPoints + 2;
numsurfaces = 0;
R_BoxSurfaces_r(tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir);
//assert(numsurfaces <= 64);
//assert(numsurfaces != 64);
returnedPoints = 0;
returnedFragments = 0;
for ( i = 0 ; i < numsurfaces ; i++ ) {
if (*surfaces[i] == SF_GRID) {
cv = (srfGridMesh_t *) surfaces[i];
for ( m = 0 ; m < cv->height - 1 ; m++ ) {
for ( n = 0 ; n < cv->width - 1 ; n++ ) {
// We triangulate the grid and chop all triangles within
// the bounding planes of the to be projected polygon.
// LOD is not taken into account, not such a big deal though.
//
// It's probably much nicer to chop the grid itself and deal
// with this grid as a normal SF_GRID surface so LOD will
// be applied. However the LOD of that chopped grid must
// be synced with the LOD of the original curve.
// One way to do this; the chopped grid shares vertices with
// the original curve. When LOD is applied to the original
// curve the unused vertices are flagged. Now the chopped curve
// should skip the flagged vertices. This still leaves the
// problems with the vertices at the chopped grid edges.
//
// To avoid issues when LOD applied to "hollow curves" (like
// the ones around many jump pads) we now just add a 2 unit
// offset to the triangle vertices.
// The offset is added in the vertex normal vector direction
// so all triangles will still fit together.
// The 2 unit offset should avoid pretty much all LOD problems.
numClipPoints = 3;
dv = cv->verts + m * cv->width + n;
VectorCopy(dv[0].xyz, clipPoints[0][0]);
VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0]);
VectorCopy(dv[cv->width].xyz, clipPoints[0][1]);
VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]);
VectorCopy(dv[1].xyz, clipPoints[0][2]);
VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2]);
// check the normal of this triangle
VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
CrossProduct(v1, v2, normal);
VectorNormalizeFast(normal);
if (DotProduct(normal, projectionDir) < -0.1) {
// add the fragments of this triangle
R_AddMarkFragments(numClipPoints, clipPoints,
numPlanes, normals, dists,
maxPoints, pointBuffer,
maxFragments, fragmentBuffer,
&returnedPoints, &returnedFragments, mins, maxs);
if ( returnedFragments == maxFragments ) {
return returnedFragments; // not enough space for more fragments
}
}
VectorCopy(dv[1].xyz, clipPoints[0][0]);
VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0]);
VectorCopy(dv[cv->width].xyz, clipPoints[0][1]);
VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]);
VectorCopy(dv[cv->width+1].xyz, clipPoints[0][2]);
VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[cv->width+1].normal, clipPoints[0][2]);
// check the normal of this triangle
VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
CrossProduct(v1, v2, normal);
VectorNormalizeFast(normal);
if (DotProduct(normal, projectionDir) < -0.05) {
// add the fragments of this triangle
R_AddMarkFragments(numClipPoints, clipPoints,
numPlanes, normals, dists,
maxPoints, pointBuffer,
maxFragments, fragmentBuffer,
&returnedPoints, &returnedFragments, mins, maxs);
if ( returnedFragments == maxFragments ) {
return returnedFragments; // not enough space for more fragments
}
}
}
}
}
else if (*surfaces[i] == SF_FACE) {
srfSurfaceFace_t *surf = ( srfSurfaceFace_t * ) surfaces[i];
// check the normal of this face
if (DotProduct(surf->plane.normal, projectionDir) > -0.5) {
continue;
}
for(k = 0, tri = surf->triangles; k < surf->numTriangles; k++, tri++)
{
for(j = 0; j < 3; j++)
{
v = surf->verts[tri->indexes[j]].xyz;
VectorMA(v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j]);
}
// add the fragments of this face
R_AddMarkFragments( 3 , clipPoints,
numPlanes, normals, dists,
maxPoints, pointBuffer,
maxFragments, fragmentBuffer,
&returnedPoints, &returnedFragments, mins, maxs);
if ( returnedFragments == maxFragments ) {
return returnedFragments; // not enough space for more fragments
}
}
}
else if(*surfaces[i] == SF_TRIANGLES && r_marksOnTriangleMeshes->integer) {
srfTriangles_t *surf = (srfTriangles_t *) surfaces[i];
for(k = 0, tri = surf->triangles; k < surf->numTriangles; k++, tri++)
{
for(j = 0; j < 3; j++)
{
v = surf->verts[tri->indexes[j]].xyz;
VectorMA(v, MARKER_OFFSET, surf->verts[tri->indexes[j]].normal, clipPoints[0][j]);
}
// add the fragments of this face
R_AddMarkFragments(3, clipPoints,
numPlanes, normals, dists,
maxPoints, pointBuffer,
maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs);
if(returnedFragments == maxFragments)
{
return returnedFragments; // not enough space for more fragments
}
}
}
}
return returnedFragments;
}

401
code/renderergl2/tr_mesh.c Normal file
View file

@ -0,0 +1,401 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_mesh.c: triangle model functions
#include "tr_local.h"
static float ProjectRadius( float r, vec3_t location )
{
float pr;
float dist;
float c;
vec3_t p;
float projected[4];
c = DotProduct( tr.viewParms.or.axis[0], tr.viewParms.or.origin );
dist = DotProduct( tr.viewParms.or.axis[0], location ) - c;
if ( dist <= 0 )
return 0;
p[0] = 0;
p[1] = fabs( r );
p[2] = -dist;
projected[0] = p[0] * tr.viewParms.projectionMatrix[0] +
p[1] * tr.viewParms.projectionMatrix[4] +
p[2] * tr.viewParms.projectionMatrix[8] +
tr.viewParms.projectionMatrix[12];
projected[1] = p[0] * tr.viewParms.projectionMatrix[1] +
p[1] * tr.viewParms.projectionMatrix[5] +
p[2] * tr.viewParms.projectionMatrix[9] +
tr.viewParms.projectionMatrix[13];
projected[2] = p[0] * tr.viewParms.projectionMatrix[2] +
p[1] * tr.viewParms.projectionMatrix[6] +
p[2] * tr.viewParms.projectionMatrix[10] +
tr.viewParms.projectionMatrix[14];
projected[3] = p[0] * tr.viewParms.projectionMatrix[3] +
p[1] * tr.viewParms.projectionMatrix[7] +
p[2] * tr.viewParms.projectionMatrix[11] +
tr.viewParms.projectionMatrix[15];
pr = projected[1] / projected[3];
if ( pr > 1.0f )
pr = 1.0f;
return pr;
}
/*
=============
R_CullModel
=============
*/
static int R_CullModel( mdvModel_t *model, trRefEntity_t *ent ) {
vec3_t bounds[2];
mdvFrame_t *oldFrame, *newFrame;
int i;
// compute frame pointers
newFrame = model->frames + ent->e.frame;
oldFrame = model->frames + ent->e.oldframe;
// cull bounding sphere ONLY if this is not an upscaled entity
if ( !ent->e.nonNormalizedAxes )
{
if ( ent->e.frame == ent->e.oldframe )
{
switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) )
{
case CULL_OUT:
tr.pc.c_sphere_cull_md3_out++;
return CULL_OUT;
case CULL_IN:
tr.pc.c_sphere_cull_md3_in++;
return CULL_IN;
case CULL_CLIP:
tr.pc.c_sphere_cull_md3_clip++;
break;
}
}
else
{
int sphereCull, sphereCullB;
sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius );
if ( newFrame == oldFrame ) {
sphereCullB = sphereCull;
} else {
sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius );
}
if ( sphereCull == sphereCullB )
{
if ( sphereCull == CULL_OUT )
{
tr.pc.c_sphere_cull_md3_out++;
return CULL_OUT;
}
else if ( sphereCull == CULL_IN )
{
tr.pc.c_sphere_cull_md3_in++;
return CULL_IN;
}
else
{
tr.pc.c_sphere_cull_md3_clip++;
}
}
}
}
// calculate a bounding box in the current coordinate system
for (i = 0 ; i < 3 ; i++) {
bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i];
bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i];
}
switch ( R_CullLocalBox( bounds ) )
{
case CULL_IN:
tr.pc.c_box_cull_md3_in++;
return CULL_IN;
case CULL_CLIP:
tr.pc.c_box_cull_md3_clip++;
return CULL_CLIP;
case CULL_OUT:
default:
tr.pc.c_box_cull_md3_out++;
return CULL_OUT;
}
}
/*
=================
R_ComputeLOD
=================
*/
int R_ComputeLOD( trRefEntity_t *ent ) {
float radius;
float flod, lodscale;
float projectedRadius;
mdvFrame_t *frame;
mdrHeader_t *mdr;
mdrFrame_t *mdrframe;
int lod;
if ( tr.currentModel->numLods < 2 )
{
// model has only 1 LOD level, skip computations and bias
lod = 0;
}
else
{
// multiple LODs exist, so compute projected bounding sphere
// and use that as a criteria for selecting LOD
if(tr.currentModel->type == MOD_MDR)
{
int frameSize;
mdr = (mdrHeader_t *) tr.currentModel->modelData;
frameSize = (size_t) (&((mdrFrame_t *)0)->bones[mdr->numBones]);
mdrframe = (mdrFrame_t *) ((byte *) mdr + mdr->ofsFrames + frameSize * ent->e.frame);
radius = RadiusFromBounds(mdrframe->bounds[0], mdrframe->bounds[1]);
}
else
{
//frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames );
frame = tr.currentModel->mdv[0]->frames;
frame += ent->e.frame;
radius = RadiusFromBounds( frame->bounds[0], frame->bounds[1] );
}
if ( ( projectedRadius = ProjectRadius( radius, ent->e.origin ) ) != 0 )
{
lodscale = r_lodscale->value;
if (lodscale > 20) lodscale = 20;
flod = 1.0f - projectedRadius * lodscale;
}
else
{
// object intersects near view plane, e.g. view weapon
flod = 0;
}
flod *= tr.currentModel->numLods;
lod = ri.ftol(flod);
if ( lod < 0 )
{
lod = 0;
}
else if ( lod >= tr.currentModel->numLods )
{
lod = tr.currentModel->numLods - 1;
}
}
lod += r_lodbias->integer;
if ( lod >= tr.currentModel->numLods )
lod = tr.currentModel->numLods - 1;
if ( lod < 0 )
lod = 0;
return lod;
}
/*
=================
R_ComputeFogNum
=================
*/
int R_ComputeFogNum( mdvModel_t *model, trRefEntity_t *ent ) {
int i, j;
fog_t *fog;
mdvFrame_t *mdvFrame;
vec3_t localOrigin;
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
return 0;
}
// FIXME: non-normalized axis issues
mdvFrame = model->frames + ent->e.frame;
VectorAdd( ent->e.origin, mdvFrame->localOrigin, localOrigin );
for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
fog = &tr.world->fogs[i];
for ( j = 0 ; j < 3 ; j++ ) {
if ( localOrigin[j] - mdvFrame->radius >= fog->bounds[1][j] ) {
break;
}
if ( localOrigin[j] + mdvFrame->radius <= fog->bounds[0][j] ) {
break;
}
}
if ( j == 3 ) {
return i;
}
}
return 0;
}
/*
=================
R_AddMD3Surfaces
=================
*/
void R_AddMD3Surfaces( trRefEntity_t *ent ) {
int i;
mdvModel_t *model = NULL;
mdvSurface_t *surface = NULL;
shader_t *shader = NULL;
int cull;
int lod;
int fogNum;
qboolean personalModel;
// don't add third_person objects if not in a portal
personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !(tr.viewParms.isPortal
|| (tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW)));
if ( ent->e.renderfx & RF_WRAP_FRAMES ) {
ent->e.frame %= tr.currentModel->mdv[0]->numFrames;
ent->e.oldframe %= tr.currentModel->mdv[0]->numFrames;
}
//
// Validate the frames so there is no chance of a crash.
// This will write directly into the entity structure, so
// when the surfaces are rendered, they don't need to be
// range checked again.
//
if ( (ent->e.frame >= tr.currentModel->mdv[0]->numFrames)
|| (ent->e.frame < 0)
|| (ent->e.oldframe >= tr.currentModel->mdv[0]->numFrames)
|| (ent->e.oldframe < 0) ) {
ri.Printf( PRINT_DEVELOPER, "R_AddMD3Surfaces: no such frame %d to %d for '%s'\n",
ent->e.oldframe, ent->e.frame,
tr.currentModel->name );
ent->e.frame = 0;
ent->e.oldframe = 0;
}
//
// compute LOD
//
lod = R_ComputeLOD( ent );
model = tr.currentModel->mdv[lod];
//
// cull the entire model if merged bounding box of both frames
// is outside the view frustum.
//
cull = R_CullModel ( model, ent );
if ( cull == CULL_OUT ) {
return;
}
//
// set up lighting now that we know we aren't culled
//
if ( !personalModel || r_shadows->integer > 1 ) {
R_SetupEntityLighting( &tr.refdef, ent );
}
//
// see if we are in a fog volume
//
fogNum = R_ComputeFogNum( model, ent );
//
// draw all surfaces
//
surface = model->surfaces;
for ( i = 0 ; i < model->numSurfaces ; i++ ) {
if ( ent->e.customShader ) {
shader = R_GetShaderByHandle( ent->e.customShader );
} else if ( ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins ) {
skin_t *skin;
int j;
skin = R_GetSkinByHandle( ent->e.customSkin );
// match the surface name to something in the skin file
shader = tr.defaultShader;
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
// the names have both been lowercased
if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) {
shader = skin->surfaces[j]->shader;
break;
}
}
if (shader == tr.defaultShader) {
ri.Printf( PRINT_DEVELOPER, "WARNING: no shader for surface %s in skin %s\n", surface->name, skin->name);
}
else if (shader->defaultShader) {
ri.Printf( PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name);
}
//} else if ( surface->numShaders <= 0 ) {
//shader = tr.defaultShader;
} else {
//md3Shader = (md3Shader_t *) ( (byte *)surface + surface->ofsShaders );
//md3Shader += ent->e.skinNum % surface->numShaders;
//shader = tr.shaders[ md3Shader->shaderIndex ];
shader = tr.shaders[ surface->shaderIndexes[ ent->e.skinNum % surface->numShaderIndexes ] ];
}
// don't add third_person objects if not viewing through a portal
if(!personalModel)
{
srfVBOMDVMesh_t *vboSurface = &model->vboSurfaces[i];
R_AddDrawSurf((void *)vboSurface, shader, fogNum, qfalse, qfalse );
}
surface++;
}
}

1569
code/renderergl2/tr_model.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,503 @@
/*
===========================================================================
Copyright (C) 2011 Andrei Drexler, Richard Allen, James Canete
This file is part of Reaction source code.
Reaction source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Reaction source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Reaction source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "tr_local.h"
void RB_ToneMap(FBO_t *hdrFbo, vec4i_t hdrBox, FBO_t *ldrFbo, vec4i_t ldrBox, int autoExposure)
{
vec4i_t srcBox, dstBox;
vec4_t color;
static int lastFrameCount = 0;
if (autoExposure)
{
if (lastFrameCount == 0 || tr.frameCount < lastFrameCount || tr.frameCount - lastFrameCount > 5)
{
// determine average log luminance
FBO_t *srcFbo, *dstFbo, *tmp;
int size = 256;
lastFrameCount = tr.frameCount;
VectorSet4(dstBox, 0, 0, size, size);
FBO_Blit(hdrFbo, hdrBox, NULL, tr.textureScratchFbo[0], dstBox, &tr.calclevels4xShader[0], NULL, 0);
srcFbo = tr.textureScratchFbo[0];
dstFbo = tr.textureScratchFbo[1];
// downscale to 1x1 texture
while (size > 1)
{
VectorSet4(srcBox, 0, 0, size, size);
//size >>= 2;
size >>= 1;
VectorSet4(dstBox, 0, 0, size, size);
if (size == 1)
dstFbo = tr.targetLevelsFbo;
//FBO_Blit(targetFbo, srcBox, NULL, tr.textureScratchFbo[nextScratch], dstBox, &tr.calclevels4xShader[1], NULL, 0);
FBO_FastBlit(srcFbo, srcBox, dstFbo, dstBox, GL_COLOR_BUFFER_BIT, GL_LINEAR);
tmp = srcFbo;
srcFbo = dstFbo;
dstFbo = tmp;
}
}
// blend with old log luminance for gradual change
VectorSet4(srcBox, 0, 0, 0, 0);
color[0] =
color[1] =
color[2] = 1.0f;
if (glRefConfig.textureFloat)
color[3] = 0.03f;
else
color[3] = 0.1f;
FBO_Blit(tr.targetLevelsFbo, srcBox, NULL, tr.calcLevelsFbo, NULL, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
}
// tonemap
color[0] =
color[1] =
color[2] = pow(2, r_cameraExposure->value); //exp2(r_cameraExposure->value);
color[3] = 1.0f;
if (autoExposure)
GL_BindToTMU(tr.calcLevelsImage, TB_LEVELSMAP);
else
GL_BindToTMU(tr.fixedLevelsImage, TB_LEVELSMAP);
FBO_Blit(hdrFbo, hdrBox, NULL, ldrFbo, ldrBox, &tr.tonemapShader, color, 0);
}
/*
=============
RB_BokehBlur
Blurs a part of one framebuffer to another.
Framebuffers can be identical.
=============
*/
void RB_BokehBlur(FBO_t *src, vec4i_t srcBox, FBO_t *dst, vec4i_t dstBox, float blur)
{
// vec4i_t srcBox, dstBox;
vec4_t color;
blur *= 10.0f;
if (blur < 0.004f)
return;
if (glRefConfig.framebufferObject)
{
// bokeh blur
if (blur > 0.0f)
{
vec4i_t quarterBox;
quarterBox[0] = 0;
quarterBox[1] = tr.quarterFbo[0]->height;
quarterBox[2] = tr.quarterFbo[0]->width;
quarterBox[3] = -tr.quarterFbo[0]->height;
// create a quarter texture
//FBO_Blit(NULL, NULL, NULL, tr.quarterFbo[0], NULL, NULL, NULL, 0);
FBO_FastBlit(src, srcBox, tr.quarterFbo[0], quarterBox, GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
#ifndef HQ_BLUR
if (blur > 1.0f)
{
// create a 1/16th texture
//FBO_Blit(tr.quarterFbo[0], NULL, NULL, tr.textureScratchFbo[0], NULL, NULL, NULL, 0);
FBO_FastBlit(tr.quarterFbo[0], NULL, tr.textureScratchFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
#endif
if (blur > 0.0f && blur <= 1.0f)
{
// Crossfade original with quarter texture
VectorSet4(color, 1, 1, 1, blur);
FBO_Blit(tr.quarterFbo[0], NULL, NULL, dst, dstBox, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
}
#ifndef HQ_BLUR
// ok blur, but can see some pixelization
else if (blur > 1.0f && blur <= 2.0f)
{
// crossfade quarter texture with 1/16th texture
FBO_Blit(tr.quarterFbo[0], NULL, NULL, dst, dstBox, NULL, NULL, 0);
VectorSet4(color, 1, 1, 1, blur - 1.0f);
FBO_Blit(tr.textureScratchFbo[0], NULL, NULL, dst, dstBox, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
}
else if (blur > 2.0f)
{
// blur 1/16th texture then replace
int i;
for (i = 0; i < 2; i++)
{
vec2_t blurTexScale;
float subblur;
subblur = ((blur - 2.0f) / 2.0f) / 3.0f * (float)(i + 1);
blurTexScale[0] =
blurTexScale[1] = subblur;
color[0] =
color[1] =
color[2] = 0.5f;
color[3] = 1.0f;
if (i != 0)
FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE);
else
FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, 0);
}
FBO_Blit(tr.textureScratchFbo[1], NULL, NULL, dst, dstBox, &tr.textureColorShader, NULL, 0);
}
#else // higher quality blur, but slower
else if (blur > 1.0f)
{
// blur quarter texture then replace
int i;
src = tr.quarterFbo[0];
dst = tr.quarterFbo[1];
VectorSet4(color, 0.5f, 0.5f, 0.5f, 1);
for (i = 0; i < 2; i++)
{
vec2_t blurTexScale;
float subblur;
subblur = (blur - 1.0f) / 2.0f * (float)(i + 1);
blurTexScale[0] =
blurTexScale[1] = subblur;
color[0] =
color[1] =
color[2] = 1.0f;
if (i != 0)
color[3] = 1.0f;
else
color[3] = 0.5f;
FBO_Blit(tr.quarterFbo[0], NULL, blurTexScale, tr.quarterFbo[1], NULL, &tr.bokehShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
}
FBO_Blit(tr.quarterFbo[1], NULL, NULL, dst, dstBox, &tr.textureColorShader, NULL, 0);
}
#endif
}
}
static void RB_RadialBlur(FBO_t *srcFbo, FBO_t *dstFbo, int passes, float stretch, float x, float y, float w, float h, float xcenter, float ycenter, float alpha)
{
vec4i_t srcBox, dstBox;
vec4_t color;
const float inc = 1.f / passes;
const float mul = powf(stretch, inc);
float scale;
{
vec2_t texScale;
texScale[0] =
texScale[1] = 1.0f;
alpha *= inc;
VectorSet4(color, alpha, alpha, alpha, 1.0f);
VectorSet4(srcBox, 0, 0, srcFbo->width, srcFbo->height);
VectorSet4(dstBox, x, y, w, h);
FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, 0);
--passes;
scale = mul;
while (passes > 0)
{
float iscale = 1.f / scale;
float s0 = xcenter * (1.f - iscale);
float t0 = (1.0f - ycenter) * (1.f - iscale);
float s1 = iscale + s0;
float t1 = iscale + t0;
srcBox[0] = s0 * srcFbo->width;
srcBox[1] = t0 * srcFbo->height;
srcBox[2] = (s1 - s0) * srcFbo->width;
srcBox[3] = (t1 - t0) * srcFbo->height;
FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE );
scale *= mul;
--passes;
}
}
}
static qboolean RB_UpdateSunFlareVis(void)
{
GLuint sampleCount = 0;
if (!glRefConfig.occlusionQuery)
return qtrue;
tr.sunFlareQueryIndex ^= 1;
if (!tr.sunFlareQueryActive[tr.sunFlareQueryIndex])
return qtrue;
/* debug code */
if (0)
{
int iter;
for (iter=0 ; ; ++iter)
{
GLint available = 0;
qglGetQueryObjectivARB(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
if (available)
break;
}
ri.Printf(PRINT_DEVELOPER, "Waited %d iterations\n", iter);
}
qglGetQueryObjectuivARB(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT_ARB, &sampleCount);
return sampleCount > 0;
}
void RB_SunRays(FBO_t *srcFbo, vec4i_t srcBox, FBO_t *dstFbo, vec4i_t dstBox)
{
vec4_t color;
float dot;
const float cutoff = 0.25f;
qboolean colorize = qtrue;
// float w, h, w2, h2;
matrix_t mvp;
vec4_t pos, hpos;
dot = DotProduct(tr.sunDirection, backEnd.viewParms.or.axis[0]);
if (dot < cutoff)
return;
if (!RB_UpdateSunFlareVis())
return;
// From RB_DrawSun()
{
float dist;
matrix_t trans, model, mvp;
Matrix16Translation( backEnd.viewParms.or.origin, trans );
Matrix16Multiply( backEnd.viewParms.world.modelMatrix, trans, model );
Matrix16Multiply(backEnd.viewParms.projectionMatrix, model, mvp);
dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3)
VectorScale( tr.sunDirection, dist, pos );
}
// project sun point
//Matrix16Multiply(backEnd.viewParms.projectionMatrix, backEnd.viewParms.world.modelMatrix, mvp);
Matrix16Transform(mvp, pos, hpos);
// transform to UV coords
hpos[3] = 0.5f / hpos[3];
pos[0] = 0.5f + hpos[0] * hpos[3];
pos[1] = 0.5f + hpos[1] * hpos[3];
// initialize quarter buffers
{
float mul = 1.f;
vec2_t texScale;
vec4i_t rayBox, quarterBox;
texScale[0] =
texScale[1] = 1.0f;
VectorSet4(color, mul, mul, mul, 1);
rayBox[0] = srcBox[0] * tr.sunRaysFbo->width / srcFbo->width;
rayBox[1] = srcBox[1] * tr.sunRaysFbo->height / srcFbo->height;
rayBox[2] = srcBox[2] * tr.sunRaysFbo->width / srcFbo->width;
rayBox[3] = srcBox[3] * tr.sunRaysFbo->height / srcFbo->height;
quarterBox[0] = 0;
quarterBox[1] = tr.quarterFbo[0]->height;
quarterBox[2] = tr.quarterFbo[0]->width;
quarterBox[3] = -tr.quarterFbo[0]->height;
// first, downsample the framebuffer
if (colorize)
{
FBO_FastBlit(srcFbo, srcBox, tr.quarterFbo[0], quarterBox, GL_COLOR_BUFFER_BIT, GL_LINEAR);
FBO_Blit(tr.sunRaysFbo, rayBox, NULL, tr.quarterFbo[0], quarterBox, NULL, color, GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO);
}
else
{
FBO_FastBlit(tr.sunRaysFbo, rayBox, tr.quarterFbo[0], quarterBox, GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
}
// radial blur passes, ping-ponging between the two quarter-size buffers
{
const float stretch_add = 2.f/3.f;
float stretch = 1.f + stretch_add;
int i;
for (i=0; i<2; ++i)
{
RB_RadialBlur(tr.quarterFbo[i&1], tr.quarterFbo[(~i) & 1], 5, stretch, 0.f, 0.f, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height, pos[0], pos[1], 1.125f);
stretch += stretch_add;
}
}
// add result back on top of the main buffer
{
float mul = 1.f;
vec2_t texScale;
texScale[0] =
texScale[1] = 1.0f;
VectorSet4(color, mul, mul, mul, 1);
FBO_Blit(tr.quarterFbo[0], NULL, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE);
}
}
static void RB_BlurAxis(FBO_t *srcFbo, FBO_t *dstFbo, float strength, qboolean horizontal)
{
float dx, dy;
float xmul, ymul;
float weights[3] = {
0.227027027f,
0.316216216f,
0.070270270f,
};
float offsets[3] = {
0.f,
1.3846153846f,
3.2307692308f,
};
xmul = horizontal;
ymul = 1.f - xmul;
xmul *= strength;
ymul *= strength;
{
vec4i_t srcBox, dstBox;
vec4_t color;
vec2_t texScale;
texScale[0] =
texScale[1] = 1.0f;
VectorSet4(color, weights[0], weights[0], weights[0], 1.0f);
VectorSet4(srcBox, 0, 0, srcFbo->width, srcFbo->height);
VectorSet4(dstBox, 0, 0, dstFbo->width, dstFbo->height);
FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, 0 );
VectorSet4(color, weights[1], weights[1], weights[1], 1.0f);
dx = offsets[1] * xmul;
dy = offsets[1] * ymul;
VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height);
FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE );
VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height);
FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE );
VectorSet4(color, weights[2], weights[2], weights[2], 1.0f);
dx = offsets[2] * xmul;
dy = offsets[2] * ymul;
VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height);
FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE );
VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height);
FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE );
}
}
static void RB_HBlur(FBO_t *srcFbo, FBO_t *dstFbo, float strength)
{
RB_BlurAxis(srcFbo, dstFbo, strength, qtrue);
}
static void RB_VBlur(FBO_t *srcFbo, FBO_t *dstFbo, float strength)
{
RB_BlurAxis(srcFbo, dstFbo, strength, qfalse);
}
void RB_GaussianBlur(float blur)
{
//float mul = 1.f;
float factor = Com_Clamp(0.f, 1.f, blur);
if (factor <= 0.f)
return;
{
vec4i_t srcBox, dstBox;
vec4_t color;
vec2_t texScale;
texScale[0] =
texScale[1] = 1.0f;
VectorSet4(color, 1, 1, 1, 1);
// first, downsample the framebuffer
FBO_FastBlit(tr.screenScratchFbo, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
FBO_FastBlit(tr.quarterFbo[0], NULL, tr.textureScratchFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
// set the alpha channel
VectorSet4(srcBox, 0, 0, tr.whiteImage->width, tr.whiteImage->height);
VectorSet4(dstBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height);
qglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
FBO_BlitFromTexture(tr.whiteImage, srcBox, texScale, tr.textureScratchFbo[0], dstBox, &tr.textureColorShader, color, GLS_DEPTHTEST_DISABLE);
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
// blur the tiny buffer horizontally and vertically
RB_HBlur(tr.textureScratchFbo[0], tr.textureScratchFbo[1], factor);
RB_VBlur(tr.textureScratchFbo[1], tr.textureScratchFbo[0], factor);
// finally, merge back to framebuffer
VectorSet4(srcBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height);
VectorSet4(dstBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight);
color[3] = factor;
FBO_Blit(tr.textureScratchFbo[0], srcBox, texScale, tr.screenScratchFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
}
}

View file

@ -0,0 +1,33 @@
/*
===========================================================================
Copyright (C) 2011 Andrei Drexler, Richard Allen, James Canete
This file is part of Reaction source code.
Reaction source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Reaction source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Reaction source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#ifndef TR_POSTPROCESS_H
#define TR_POSTPROCESS_H
#include "tr_fbo.h"
void RB_ToneMap(FBO_t *hdrFbo, vec4i_t hdrBox, FBO_t *ldrFbo, vec4i_t ldrBox, int autoExposure);
void RB_BokehBlur(FBO_t *src, vec4i_t srcBox, FBO_t *dst, vec4i_t dstBox, float blur);
void RB_SunRays(FBO_t *srcFbo, vec4i_t srcBox, FBO_t *dstFbo, vec4i_t dstBox);
void RB_GaussianBlur(float blur);
#endif

532
code/renderergl2/tr_scene.c Normal file
View file

@ -0,0 +1,532 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "tr_local.h"
int r_firstSceneDrawSurf;
int r_numdlights;
int r_firstSceneDlight;
int r_numentities;
int r_firstSceneEntity;
int r_numpolys;
int r_firstScenePoly;
int r_numpolyverts;
/*
====================
R_InitNextFrame
====================
*/
void R_InitNextFrame( void ) {
backEndData->commands.used = 0;
r_firstSceneDrawSurf = 0;
r_numdlights = 0;
r_firstSceneDlight = 0;
r_numentities = 0;
r_firstSceneEntity = 0;
r_numpolys = 0;
r_firstScenePoly = 0;
r_numpolyverts = 0;
}
/*
====================
RE_ClearScene
====================
*/
void RE_ClearScene( void ) {
r_firstSceneDlight = r_numdlights;
r_firstSceneEntity = r_numentities;
r_firstScenePoly = r_numpolys;
}
/*
===========================================================================
DISCRETE POLYS
===========================================================================
*/
/*
=====================
R_AddPolygonSurfaces
Adds all the scene's polys into this view's drawsurf list
=====================
*/
void R_AddPolygonSurfaces( void ) {
int i;
shader_t *sh;
srfPoly_t *poly;
int fogMask;
tr.currentEntityNum = REFENTITYNUM_WORLD;
tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT;
fogMask = -((tr.refdef.rdflags & RDF_NOFOG) == 0);
for ( i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys ; i++, poly++ ) {
sh = R_GetShaderByHandle( poly->hShader );
R_AddDrawSurf( ( void * )poly, sh, poly->fogIndex & fogMask, qfalse, qfalse );
}
}
/*
=====================
RE_AddPolyToScene
=====================
*/
void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) {
srfPoly_t *poly;
int i, j;
int fogIndex;
fog_t *fog;
vec3_t bounds[2];
if ( !tr.registered ) {
return;
}
if ( !hShader ) {
// This isn't a useful warning, and an hShader of zero isn't a null shader, it's
// the default shader.
//ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n");
//return;
}
for ( j = 0; j < numPolys; j++ ) {
if ( r_numpolyverts + numVerts > max_polyverts || r_numpolys >= max_polys ) {
/*
NOTE TTimo this was initially a PRINT_WARNING
but it happens a lot with high fighting scenes and particles
since we don't plan on changing the const and making for room for those effects
simply cut this message to developer only
*/
ri.Printf( PRINT_DEVELOPER, "WARNING: RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n");
return;
}
poly = &backEndData->polys[r_numpolys];
poly->surfaceType = SF_POLY;
poly->hShader = hShader;
poly->numVerts = numVerts;
poly->verts = &backEndData->polyVerts[r_numpolyverts];
Com_Memcpy( poly->verts, &verts[numVerts*j], numVerts * sizeof( *verts ) );
if ( glConfig.hardwareType == GLHW_RAGEPRO ) {
poly->verts->modulate[0] = 255;
poly->verts->modulate[1] = 255;
poly->verts->modulate[2] = 255;
poly->verts->modulate[3] = 255;
}
// done.
r_numpolys++;
r_numpolyverts += numVerts;
// if no world is loaded
if ( tr.world == NULL ) {
fogIndex = 0;
}
// see if it is in a fog volume
else if ( tr.world->numfogs == 1 ) {
fogIndex = 0;
} else {
// find which fog volume the poly is in
VectorCopy( poly->verts[0].xyz, bounds[0] );
VectorCopy( poly->verts[0].xyz, bounds[1] );
for ( i = 1 ; i < poly->numVerts ; i++ ) {
AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] );
}
for ( fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++ ) {
fog = &tr.world->fogs[fogIndex];
if ( bounds[1][0] >= fog->bounds[0][0]
&& bounds[1][1] >= fog->bounds[0][1]
&& bounds[1][2] >= fog->bounds[0][2]
&& bounds[0][0] <= fog->bounds[1][0]
&& bounds[0][1] <= fog->bounds[1][1]
&& bounds[0][2] <= fog->bounds[1][2] ) {
break;
}
}
if ( fogIndex == tr.world->numfogs ) {
fogIndex = 0;
}
}
poly->fogIndex = fogIndex;
}
}
//=================================================================================
/*
=====================
RE_AddRefEntityToScene
=====================
*/
void RE_AddRefEntityToScene( const refEntity_t *ent ) {
vec3_t cross;
if ( !tr.registered ) {
return;
}
if ( r_numentities >= MAX_REFENTITIES ) {
ri.Printf(PRINT_DEVELOPER, "RE_AddRefEntityToScene: Dropping refEntity, reached MAX_REFENTITIES\n");
return;
}
if ( Q_isnan(ent->origin[0]) || Q_isnan(ent->origin[1]) || Q_isnan(ent->origin[2]) ) {
static qboolean firstTime = qtrue;
if (firstTime) {
firstTime = qfalse;
ri.Printf( PRINT_WARNING, "RE_AddRefEntityToScene passed a refEntity which has an origin with a NaN component\n");
}
return;
}
if ( (int)ent->reType < 0 || ent->reType >= RT_MAX_REF_ENTITY_TYPE ) {
ri.Error( ERR_DROP, "RE_AddRefEntityToScene: bad reType %i", ent->reType );
}
backEndData->entities[r_numentities].e = *ent;
backEndData->entities[r_numentities].lightingCalculated = qfalse;
CrossProduct(ent->axis[0], ent->axis[1], cross);
backEndData->entities[r_numentities].mirrored = (DotProduct(ent->axis[2], cross) < 0.f);
r_numentities++;
}
/*
=====================
RE_AddDynamicLightToScene
=====================
*/
void RE_AddDynamicLightToScene( const vec3_t org, float intensity, float r, float g, float b, int additive ) {
dlight_t *dl;
if ( !tr.registered ) {
return;
}
if ( r_numdlights >= MAX_DLIGHTS ) {
return;
}
if ( intensity <= 0 ) {
return;
}
// these cards don't have the correct blend mode
if ( glConfig.hardwareType == GLHW_RIVA128 || glConfig.hardwareType == GLHW_PERMEDIA2 ) {
return;
}
dl = &backEndData->dlights[r_numdlights++];
VectorCopy (org, dl->origin);
dl->radius = intensity;
dl->color[0] = r;
dl->color[1] = g;
dl->color[2] = b;
dl->additive = additive;
}
/*
=====================
RE_AddLightToScene
=====================
*/
void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) {
RE_AddDynamicLightToScene( org, intensity, r, g, b, qfalse );
}
/*
=====================
RE_AddAdditiveLightToScene
=====================
*/
void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ) {
RE_AddDynamicLightToScene( org, intensity, r, g, b, qtrue );
}
/*
@@@@@@@@@@@@@@@@@@@@@
RE_RenderScene
Draw a 3D view into a part of the window, then return
to 2D drawing.
Rendering a scene may require multiple views to be rendered
to handle mirrors,
@@@@@@@@@@@@@@@@@@@@@
*/
void RE_RenderScene( const refdef_t *fd ) {
viewParms_t parms;
int startTime;
if ( !tr.registered ) {
return;
}
GLimp_LogComment( "====== RE_RenderScene =====\n" );
if ( r_norefresh->integer ) {
return;
}
startTime = ri.Milliseconds();
if (!tr.world && !( fd->rdflags & RDF_NOWORLDMODEL ) ) {
ri.Error (ERR_DROP, "R_RenderScene: NULL worldmodel");
}
Com_Memcpy( tr.refdef.text, fd->text, sizeof( tr.refdef.text ) );
tr.refdef.x = fd->x;
tr.refdef.y = fd->y;
tr.refdef.width = fd->width;
tr.refdef.height = fd->height;
tr.refdef.fov_x = fd->fov_x;
tr.refdef.fov_y = fd->fov_y;
VectorCopy( fd->vieworg, tr.refdef.vieworg );
VectorCopy( fd->viewaxis[0], tr.refdef.viewaxis[0] );
VectorCopy( fd->viewaxis[1], tr.refdef.viewaxis[1] );
VectorCopy( fd->viewaxis[2], tr.refdef.viewaxis[2] );
tr.refdef.time = fd->time;
tr.refdef.rdflags = fd->rdflags;
// copy the areamask data over and note if it has changed, which
// will force a reset of the visible leafs even if the view hasn't moved
tr.refdef.areamaskModified = qfalse;
if ( ! (tr.refdef.rdflags & RDF_NOWORLDMODEL) ) {
int areaDiff;
int i;
// compare the area bits
areaDiff = 0;
for (i = 0 ; i < MAX_MAP_AREA_BYTES/4 ; i++) {
areaDiff |= ((int *)tr.refdef.areamask)[i] ^ ((int *)fd->areamask)[i];
((int *)tr.refdef.areamask)[i] = ((int *)fd->areamask)[i];
}
if ( areaDiff ) {
// a door just opened or something
tr.refdef.areamaskModified = qtrue;
}
}
tr.refdef.sunDir[3] = 0.0f;
tr.refdef.sunCol[3] = 1.0f;
tr.refdef.sunAmbCol[3] = 1.0f;
VectorCopy(tr.sunDirection, tr.refdef.sunDir);
if ( (tr.refdef.rdflags & RDF_NOWORLDMODEL) || !(r_depthPrepass->value) ){
tr.refdef.colorScale = 1.0f;
VectorSet(tr.refdef.sunCol, 0, 0, 0);
VectorSet(tr.refdef.sunAmbCol, 0, 0, 0);
}
else
{
tr.refdef.colorScale = r_forceSun->integer ? r_forceSunMapLightScale->value : tr.mapLightScale;
if (r_sunlightMode->integer == 1)
{
tr.refdef.sunCol[0] =
tr.refdef.sunCol[1] =
tr.refdef.sunCol[2] = 1.0f;
tr.refdef.sunAmbCol[0] =
tr.refdef.sunAmbCol[1] =
tr.refdef.sunAmbCol[2] = r_forceSun->integer ? r_forceSunAmbientScale->value : tr.sunShadowScale;
}
else
{
float scale = pow(2, r_mapOverBrightBits->integer - tr.overbrightBits - 8);
if (r_forceSun->integer)
{
VectorScale(tr.sunLight, scale * r_forceSunLightScale->value, tr.refdef.sunCol);
VectorScale(tr.sunLight, scale * r_forceSunAmbientScale->value, tr.refdef.sunAmbCol);
}
else
{
VectorScale(tr.sunLight, scale, tr.refdef.sunCol);
VectorScale(tr.sunLight, scale * tr.sunShadowScale, tr.refdef.sunAmbCol);
}
}
}
if (r_forceAutoExposure->integer)
{
tr.refdef.autoExposureMinMax[0] = r_forceAutoExposureMin->value;
tr.refdef.autoExposureMinMax[1] = r_forceAutoExposureMax->value;
}
else
{
tr.refdef.autoExposureMinMax[0] = tr.autoExposureMinMax[0];
tr.refdef.autoExposureMinMax[1] = tr.autoExposureMinMax[1];
}
if (r_forceToneMap->integer)
{
tr.refdef.toneMinAvgMaxLinear[0] = pow(2, r_forceToneMapMin->value);
tr.refdef.toneMinAvgMaxLinear[1] = pow(2, r_forceToneMapAvg->value);
tr.refdef.toneMinAvgMaxLinear[2] = pow(2, r_forceToneMapMax->value);
}
else
{
tr.refdef.toneMinAvgMaxLinear[0] = pow(2, tr.toneMinAvgMaxLevel[0]);
tr.refdef.toneMinAvgMaxLinear[1] = pow(2, tr.toneMinAvgMaxLevel[1]);
tr.refdef.toneMinAvgMaxLinear[2] = pow(2, tr.toneMinAvgMaxLevel[2]);
}
// Makro - copy exta info if present
if (fd->rdflags & RDF_EXTRA) {
const refdefex_t* extra = (const refdefex_t*) (fd+1);
tr.refdef.blurFactor = extra->blurFactor;
if (fd->rdflags & RDF_SUNLIGHT)
{
VectorCopy(extra->sunDir, tr.refdef.sunDir);
VectorCopy(extra->sunCol, tr.refdef.sunCol);
VectorCopy(extra->sunAmbCol, tr.refdef.sunAmbCol);
}
}
else
{
tr.refdef.blurFactor = 0.0f;
}
// derived info
tr.refdef.floatTime = tr.refdef.time * 0.001f;
tr.refdef.numDrawSurfs = r_firstSceneDrawSurf;
tr.refdef.drawSurfs = backEndData->drawSurfs;
tr.refdef.num_entities = r_numentities - r_firstSceneEntity;
tr.refdef.entities = &backEndData->entities[r_firstSceneEntity];
tr.refdef.num_dlights = r_numdlights - r_firstSceneDlight;
tr.refdef.dlights = &backEndData->dlights[r_firstSceneDlight];
tr.refdef.numPolys = r_numpolys - r_firstScenePoly;
tr.refdef.polys = &backEndData->polys[r_firstScenePoly];
tr.refdef.num_pshadows = 0;
tr.refdef.pshadows = &backEndData->pshadows[0];
// turn off dynamic lighting globally by clearing all the
// dlights if it needs to be disabled or if vertex lighting is enabled
if ( r_dynamiclight->integer == 0 ||
r_vertexLight->integer == 1 ||
glConfig.hardwareType == GLHW_PERMEDIA2 ) {
tr.refdef.num_dlights = 0;
}
// a single frame may have multiple scenes draw inside it --
// a 3D game view, 3D status bar renderings, 3D menus, etc.
// They need to be distinguished by the light flare code, because
// the visibility state for a given surface may be different in
// each scene / view.
tr.frameSceneNum++;
tr.sceneCount++;
// SmileTheory: playing with shadow mapping
if (!( fd->rdflags & RDF_NOWORLDMODEL ) && tr.refdef.num_dlights && r_dlightMode->integer >= 2)
{
R_RenderDlightCubemaps(fd);
}
/* playing with more shadows */
if(glRefConfig.framebufferObject && !( fd->rdflags & RDF_NOWORLDMODEL ) && r_shadows->integer == 4)
{
R_RenderPshadowMaps(fd);
}
// playing with even more shadows
if(glRefConfig.framebufferObject && !( fd->rdflags & RDF_NOWORLDMODEL ) && (r_forceSun->integer || tr.sunShadows))
{
R_RenderSunShadowMaps(fd, 0);
R_RenderSunShadowMaps(fd, 1);
R_RenderSunShadowMaps(fd, 2);
}
// setup view parms for the initial view
//
// set up viewport
// The refdef takes 0-at-the-top y coordinates, so
// convert to GL's 0-at-the-bottom space
//
Com_Memset( &parms, 0, sizeof( parms ) );
parms.viewportX = tr.refdef.x;
parms.viewportY = glConfig.vidHeight - ( tr.refdef.y + tr.refdef.height );
parms.viewportWidth = tr.refdef.width;
parms.viewportHeight = tr.refdef.height;
parms.isPortal = qfalse;
parms.fovX = tr.refdef.fov_x;
parms.fovY = tr.refdef.fov_y;
parms.stereoFrame = tr.refdef.stereoFrame;
VectorCopy( fd->vieworg, parms.or.origin );
VectorCopy( fd->viewaxis[0], parms.or.axis[0] );
VectorCopy( fd->viewaxis[1], parms.or.axis[1] );
VectorCopy( fd->viewaxis[2], parms.or.axis[2] );
VectorCopy( fd->vieworg, parms.pvsOrigin );
if(!( fd->rdflags & RDF_NOWORLDMODEL ) && r_depthPrepass->value && ((r_forceSun->integer) || tr.sunShadows))
{
parms.flags = VPF_USESUNLIGHT;
}
R_RenderView( &parms );
if(!( fd->rdflags & RDF_NOWORLDMODEL ))
R_AddPostProcessCmd();
// the next scene rendered in this frame will tack on after this one
r_firstSceneDrawSurf = tr.refdef.numDrawSurfs;
r_firstSceneEntity = r_numentities;
r_firstSceneDlight = r_numdlights;
r_firstScenePoly = r_numpolys;
tr.frontEndMsec += ri.Milliseconds() - startTime;
}

1670
code/renderergl2/tr_shade.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

3771
code/renderergl2/tr_shader.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,343 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "tr_local.h"
/*
for a projection shadow:
point[x] += light vector * ( z - shadow plane )
point[y] +=
point[z] = shadow plane
1 0 light[x] / light[z]
*/
typedef struct {
int i2;
int facing;
} edgeDef_t;
#define MAX_EDGE_DEFS 32
static edgeDef_t edgeDefs[SHADER_MAX_VERTEXES][MAX_EDGE_DEFS];
static int numEdgeDefs[SHADER_MAX_VERTEXES];
static int facing[SHADER_MAX_INDEXES/3];
void R_AddEdgeDef( int i1, int i2, int facing ) {
int c;
c = numEdgeDefs[ i1 ];
if ( c == MAX_EDGE_DEFS ) {
return; // overflow
}
edgeDefs[ i1 ][ c ].i2 = i2;
edgeDefs[ i1 ][ c ].facing = facing;
numEdgeDefs[ i1 ]++;
}
void R_RenderShadowEdges( void ) {
int i;
#if 0
int numTris;
// dumb way -- render every triangle's edges
numTris = tess.numIndexes / 3;
for ( i = 0 ; i < numTris ; i++ ) {
int i1, i2, i3;
if ( !facing[i] ) {
continue;
}
i1 = tess.indexes[ i*3 + 0 ];
i2 = tess.indexes[ i*3 + 1 ];
i3 = tess.indexes[ i*3 + 2 ];
qglBegin( GL_TRIANGLE_STRIP );
qglVertex3fv( tess.xyz[ i1 ] );
qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] );
qglVertex3fv( tess.xyz[ i2 ] );
qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] );
qglVertex3fv( tess.xyz[ i3 ] );
qglVertex3fv( tess.xyz[ i3 + tess.numVertexes ] );
qglVertex3fv( tess.xyz[ i1 ] );
qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] );
qglEnd();
}
#else
int c, c2;
int j, k;
int i2;
int c_edges, c_rejected;
int hit[2];
// an edge is NOT a silhouette edge if its face doesn't face the light,
// or if it has a reverse paired edge that also faces the light.
// A well behaved polyhedron would have exactly two faces for each edge,
// but lots of models have dangling edges or overfanned edges
c_edges = 0;
c_rejected = 0;
for ( i = 0 ; i < tess.numVertexes ; i++ ) {
c = numEdgeDefs[ i ];
for ( j = 0 ; j < c ; j++ ) {
if ( !edgeDefs[ i ][ j ].facing ) {
continue;
}
hit[0] = 0;
hit[1] = 0;
i2 = edgeDefs[ i ][ j ].i2;
c2 = numEdgeDefs[ i2 ];
for ( k = 0 ; k < c2 ; k++ ) {
if ( edgeDefs[ i2 ][ k ].i2 == i ) {
hit[ edgeDefs[ i2 ][ k ].facing ]++;
}
}
// if it doesn't share the edge with another front facing
// triangle, it is a sil edge
if ( hit[ 1 ] == 0 ) {
qglBegin( GL_TRIANGLE_STRIP );
qglVertex3fv( tess.xyz[ i ] );
qglVertex3fv( tess.xyz[ i + tess.numVertexes ] );
qglVertex3fv( tess.xyz[ i2 ] );
qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] );
qglEnd();
c_edges++;
} else {
c_rejected++;
}
}
}
#endif
}
/*
=================
RB_ShadowTessEnd
triangleFromEdge[ v1 ][ v2 ]
set triangle from edge( v1, v2, tri )
if ( facing[ triangleFromEdge[ v1 ][ v2 ] ] && !facing[ triangleFromEdge[ v2 ][ v1 ] ) {
}
=================
*/
void RB_ShadowTessEnd( void ) {
int i;
int numTris;
vec3_t lightDir;
GLboolean rgba[4];
// we can only do this if we have enough space in the vertex buffers
if ( tess.numVertexes >= SHADER_MAX_VERTEXES / 2 ) {
return;
}
if ( glConfig.stencilBits < 4 ) {
return;
}
VectorCopy( backEnd.currentEntity->lightDir, lightDir );
// project vertexes away from light direction
for ( i = 0 ; i < tess.numVertexes ; i++ ) {
VectorMA( tess.xyz[i], -512, lightDir, tess.xyz[i+tess.numVertexes] );
}
// decide which triangles face the light
Com_Memset( numEdgeDefs, 0, 4 * tess.numVertexes );
numTris = tess.numIndexes / 3;
for ( i = 0 ; i < numTris ; i++ ) {
int i1, i2, i3;
vec3_t d1, d2, normal;
float *v1, *v2, *v3;
float d;
i1 = tess.indexes[ i*3 + 0 ];
i2 = tess.indexes[ i*3 + 1 ];
i3 = tess.indexes[ i*3 + 2 ];
v1 = tess.xyz[ i1 ];
v2 = tess.xyz[ i2 ];
v3 = tess.xyz[ i3 ];
VectorSubtract( v2, v1, d1 );
VectorSubtract( v3, v1, d2 );
CrossProduct( d1, d2, normal );
d = DotProduct( normal, lightDir );
if ( d > 0 ) {
facing[ i ] = 1;
} else {
facing[ i ] = 0;
}
// create the edges
R_AddEdgeDef( i1, i2, facing[ i ] );
R_AddEdgeDef( i2, i3, facing[ i ] );
R_AddEdgeDef( i3, i1, facing[ i ] );
}
// draw the silhouette edges
GL_Bind( tr.whiteImage );
qglEnable( GL_CULL_FACE );
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO );
qglColor3f( 0.2f, 0.2f, 0.2f );
// don't write to the color buffer
qglGetBooleanv(GL_COLOR_WRITEMASK, rgba);
qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
qglEnable( GL_STENCIL_TEST );
qglStencilFunc( GL_ALWAYS, 1, 255 );
// mirrors have the culling order reversed
if ( backEnd.viewParms.isMirror ) {
qglCullFace( GL_FRONT );
qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
R_RenderShadowEdges();
qglCullFace( GL_BACK );
qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR );
R_RenderShadowEdges();
} else {
qglCullFace( GL_BACK );
qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
R_RenderShadowEdges();
qglCullFace( GL_FRONT );
qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR );
R_RenderShadowEdges();
}
// reenable writing to the color buffer
qglColorMask(rgba[0], rgba[1], rgba[2], rgba[3]);
}
/*
=================
RB_ShadowFinish
Darken everything that is is a shadow volume.
We have to delay this until everything has been shadowed,
because otherwise shadows from different body parts would
overlap and double darken.
=================
*/
void RB_ShadowFinish( void ) {
if ( r_shadows->integer != 2 ) {
return;
}
if ( glConfig.stencilBits < 4 ) {
return;
}
qglEnable( GL_STENCIL_TEST );
qglStencilFunc( GL_NOTEQUAL, 0, 255 );
qglDisable (GL_CLIP_PLANE0);
qglDisable (GL_CULL_FACE);
GL_Bind( tr.whiteImage );
qglLoadIdentity ();
qglColor3f( 0.6f, 0.6f, 0.6f );
GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO );
// qglColor3f( 1, 0, 0 );
// GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO );
qglBegin( GL_QUADS );
qglVertex3f( -100, 100, -10 );
qglVertex3f( 100, 100, -10 );
qglVertex3f( 100, -100, -10 );
qglVertex3f( -100, -100, -10 );
qglEnd ();
qglColor4f(1,1,1,1);
qglDisable( GL_STENCIL_TEST );
}
/*
=================
RB_ProjectionShadowDeform
=================
*/
void RB_ProjectionShadowDeform( void ) {
float *xyz;
int i;
float h;
vec3_t ground;
vec3_t light;
float groundDist;
float d;
vec3_t lightDir;
xyz = ( float * ) tess.xyz;
ground[0] = backEnd.or.axis[0][2];
ground[1] = backEnd.or.axis[1][2];
ground[2] = backEnd.or.axis[2][2];
groundDist = backEnd.or.origin[2] - backEnd.currentEntity->e.shadowPlane;
VectorCopy( backEnd.currentEntity->lightDir, lightDir );
d = DotProduct( lightDir, ground );
// don't let the shadows get too long or go negative
if ( d < 0.5 ) {
VectorMA( lightDir, (0.5 - d), ground, lightDir );
d = DotProduct( lightDir, ground );
}
d = 1.0 / d;
light[0] = lightDir[0] * d;
light[1] = lightDir[1] * d;
light[2] = lightDir[2] * d;
for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) {
h = DotProduct( xyz, ground ) + groundDist;
xyz[0] -= light[0] * h;
xyz[1] -= light[1] * h;
xyz[2] -= light[2] * h;
}
}

912
code/renderergl2/tr_sky.c Normal file
View file

@ -0,0 +1,912 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_sky.c
#include "tr_local.h"
#define SKY_SUBDIVISIONS 8
#define HALF_SKY_SUBDIVISIONS (SKY_SUBDIVISIONS/2)
static float s_cloudTexCoords[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2];
static float s_cloudTexP[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1];
/*
===================================================================================
POLYGON TO BOX SIDE PROJECTION
===================================================================================
*/
static vec3_t sky_clip[6] =
{
{1,1,0},
{1,-1,0},
{0,-1,1},
{0,1,1},
{1,0,1},
{-1,0,1}
};
static float sky_mins[2][6], sky_maxs[2][6];
static float sky_min, sky_max;
/*
================
AddSkyPolygon
================
*/
static void AddSkyPolygon (int nump, vec3_t vecs)
{
int i,j;
vec3_t v, av;
float s, t, dv;
int axis;
float *vp;
// s = [0]/[2], t = [1]/[2]
static int vec_to_st[6][3] =
{
{-2,3,1},
{2,3,-1},
{1,3,2},
{-1,3,-2},
{-2,-1,3},
{-2,1,-3}
// {-1,2,3},
// {1,2,-3}
};
// decide which face it maps to
VectorCopy (vec3_origin, v);
for (i=0, vp=vecs ; i<nump ; i++, vp+=3)
{
VectorAdd (vp, v, v);
}
av[0] = fabs(v[0]);
av[1] = fabs(v[1]);
av[2] = fabs(v[2]);
if (av[0] > av[1] && av[0] > av[2])
{
if (v[0] < 0)
axis = 1;
else
axis = 0;
}
else if (av[1] > av[2] && av[1] > av[0])
{
if (v[1] < 0)
axis = 3;
else
axis = 2;
}
else
{
if (v[2] < 0)
axis = 5;
else
axis = 4;
}
// project new texture coords
for (i=0 ; i<nump ; i++, vecs+=3)
{
j = vec_to_st[axis][2];
if (j > 0)
dv = vecs[j - 1];
else
dv = -vecs[-j - 1];
if (dv < 0.001)
continue; // don't divide by zero
j = vec_to_st[axis][0];
if (j < 0)
s = -vecs[-j -1] / dv;
else
s = vecs[j-1] / dv;
j = vec_to_st[axis][1];
if (j < 0)
t = -vecs[-j -1] / dv;
else
t = vecs[j-1] / dv;
if (s < sky_mins[0][axis])
sky_mins[0][axis] = s;
if (t < sky_mins[1][axis])
sky_mins[1][axis] = t;
if (s > sky_maxs[0][axis])
sky_maxs[0][axis] = s;
if (t > sky_maxs[1][axis])
sky_maxs[1][axis] = t;
}
}
#define ON_EPSILON 0.1f // point on plane side epsilon
#define MAX_CLIP_VERTS 64
/*
================
ClipSkyPolygon
================
*/
static void ClipSkyPolygon (int nump, vec3_t vecs, int stage)
{
float *norm;
float *v;
qboolean front, back;
float d, e;
float dists[MAX_CLIP_VERTS];
int sides[MAX_CLIP_VERTS];
vec3_t newv[2][MAX_CLIP_VERTS];
int newc[2];
int i, j;
if (nump > MAX_CLIP_VERTS-2)
ri.Error (ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS");
if (stage == 6)
{ // fully clipped, so draw it
AddSkyPolygon (nump, vecs);
return;
}
front = back = qfalse;
norm = sky_clip[stage];
for (i=0, v = vecs ; i<nump ; i++, v+=3)
{
d = DotProduct (v, norm);
if (d > ON_EPSILON)
{
front = qtrue;
sides[i] = SIDE_FRONT;
}
else if (d < -ON_EPSILON)
{
back = qtrue;
sides[i] = SIDE_BACK;
}
else
sides[i] = SIDE_ON;
dists[i] = d;
}
if (!front || !back)
{ // not clipped
ClipSkyPolygon (nump, vecs, stage+1);
return;
}
// clip it
sides[i] = sides[0];
dists[i] = dists[0];
VectorCopy (vecs, (vecs+(i*3)) );
newc[0] = newc[1] = 0;
for (i=0, v = vecs ; i<nump ; i++, v+=3)
{
switch (sides[i])
{
case SIDE_FRONT:
VectorCopy (v, newv[0][newc[0]]);
newc[0]++;
break;
case SIDE_BACK:
VectorCopy (v, newv[1][newc[1]]);
newc[1]++;
break;
case SIDE_ON:
VectorCopy (v, newv[0][newc[0]]);
newc[0]++;
VectorCopy (v, newv[1][newc[1]]);
newc[1]++;
break;
}
if (sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
continue;
d = dists[i] / (dists[i] - dists[i+1]);
for (j=0 ; j<3 ; j++)
{
e = v[j] + d*(v[j+3] - v[j]);
newv[0][newc[0]][j] = e;
newv[1][newc[1]][j] = e;
}
newc[0]++;
newc[1]++;
}
// continue
ClipSkyPolygon (newc[0], newv[0][0], stage+1);
ClipSkyPolygon (newc[1], newv[1][0], stage+1);
}
/*
==============
ClearSkyBox
==============
*/
static void ClearSkyBox (void) {
int i;
for (i=0 ; i<6 ; i++) {
sky_mins[0][i] = sky_mins[1][i] = 9999;
sky_maxs[0][i] = sky_maxs[1][i] = -9999;
}
}
/*
================
RB_ClipSkyPolygons
================
*/
void RB_ClipSkyPolygons( shaderCommands_t *input )
{
vec3_t p[5]; // need one extra point for clipping
int i, j;
ClearSkyBox();
for ( i = 0; i < input->numIndexes; i += 3 )
{
for (j = 0 ; j < 3 ; j++)
{
VectorSubtract( input->xyz[input->indexes[i+j]],
backEnd.viewParms.or.origin,
p[j] );
}
ClipSkyPolygon( 3, p[0], 0 );
}
}
/*
===================================================================================
CLOUD VERTEX GENERATION
===================================================================================
*/
/*
** MakeSkyVec
**
** Parms: s, t range from -1 to 1
*/
static void MakeSkyVec( float s, float t, int axis, float outSt[2], vec3_t outXYZ )
{
// 1 = s, 2 = t, 3 = 2048
static int st_to_vec[6][3] =
{
{3,-1,2},
{-3,1,2},
{1,3,2},
{-1,-3,2},
{-2,-1,3}, // 0 degrees yaw, look straight up
{2,-1,-3} // look straight down
};
vec3_t b;
int j, k;
float boxSize;
boxSize = backEnd.viewParms.zFar / 1.75; // div sqrt(3)
b[0] = s*boxSize;
b[1] = t*boxSize;
b[2] = boxSize;
for (j=0 ; j<3 ; j++)
{
k = st_to_vec[axis][j];
if (k < 0)
{
outXYZ[j] = -b[-k - 1];
}
else
{
outXYZ[j] = b[k - 1];
}
}
// avoid bilerp seam
s = (s+1)*0.5;
t = (t+1)*0.5;
if (s < sky_min)
{
s = sky_min;
}
else if (s > sky_max)
{
s = sky_max;
}
if (t < sky_min)
{
t = sky_min;
}
else if (t > sky_max)
{
t = sky_max;
}
t = 1.0 - t;
if ( outSt )
{
outSt[0] = s;
outSt[1] = t;
}
}
static int sky_texorder[6] = {0,2,1,3,4,5};
static vec3_t s_skyPoints[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1];
static float s_skyTexCoords[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2];
static void DrawSkySide( struct image_s *image, const int mins[2], const int maxs[2] )
{
int s, t;
int firstVertex = tess.numVertexes;
//int firstIndex = tess.numIndexes;
int minIndex = tess.minIndex;
int maxIndex = tess.maxIndex;
vec4_t color;
//tess.numVertexes = 0;
//tess.numIndexes = 0;
tess.firstIndex = tess.numIndexes;
GL_Bind( image );
GL_Cull( CT_TWO_SIDED );
for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ )
{
for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ )
{
tess.xyz[tess.numVertexes][0] = s_skyPoints[t][s][0];
tess.xyz[tess.numVertexes][1] = s_skyPoints[t][s][1];
tess.xyz[tess.numVertexes][2] = s_skyPoints[t][s][2];
tess.xyz[tess.numVertexes][3] = 1.0;
tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t][s][0];
tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t][s][1];
tess.numVertexes++;
if(tess.numVertexes >= SHADER_MAX_VERTEXES)
{
ri.Error(ERR_DROP, "SHADER_MAX_VERTEXES hit in DrawSkySideVBO()");
}
}
}
for ( t = 0; t < maxs[1] - mins[1]; t++ )
{
for ( s = 0; s < maxs[0] - mins[0]; s++ )
{
if (tess.numIndexes + 6 >= SHADER_MAX_INDEXES)
{
ri.Error(ERR_DROP, "SHADER_MAX_INDEXES hit in DrawSkySideVBO()");
}
tess.indexes[tess.numIndexes++] = s + t * (maxs[0] - mins[0] + 1) + firstVertex;
tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex;
tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex;
tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex;
tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex;
tess.indexes[tess.numIndexes++] = (s + 1) + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex;
}
}
tess.minIndex = firstVertex;
tess.maxIndex = tess.numVertexes;
// FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function
RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD);
/*
{
shaderProgram_t *sp = &tr.textureColorShader;
GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD);
GLSL_BindProgram(sp);
GLSL_SetUniformMatrix16(sp, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection);
color[0] =
color[1] =
color[2] = tr.identityLight;
color[3] = 1.0f;
GLSL_SetUniformVec4(sp, UNIFORM_COLOR, color);
}
*/
{
shaderProgram_t *sp = &tr.lightallShader[0];
vec4_t vector;
GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD);
GLSL_BindProgram(sp);
GLSL_SetUniformMatrix16(sp, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection);
color[0] =
color[1] =
color[2] = tr.identityLight;
color[3] = 1.0f;
GLSL_SetUniformVec4(sp, UNIFORM_BASECOLOR, color);
color[0] =
color[1] =
color[2] =
color[3] = 0.0f;
GLSL_SetUniformVec4(sp, UNIFORM_VERTCOLOR, color);
VectorSet4(vector, 1.0, 0.0, 0.0, 1.0);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX, vector);
VectorSet4(vector, 0.0, 0.0, 0.0, 0.0);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXOFFTURB, vector);
}
R_DrawElementsVBO(tess.numIndexes - tess.firstIndex, tess.firstIndex, tess.minIndex, tess.maxIndex);
//qglDrawElements(GL_TRIANGLES, tess.numIndexes - tess.firstIndex, GL_INDEX_TYPE, BUFFER_OFFSET(tess.firstIndex * sizeof(GL_INDEX_TYPE)));
//R_BindNullVBO();
//R_BindNullIBO();
tess.numIndexes = tess.firstIndex;
tess.numVertexes = firstVertex;
tess.firstIndex = 0;
tess.minIndex = minIndex;
tess.maxIndex = maxIndex;
}
static void DrawSkyBox( shader_t *shader )
{
int i;
sky_min = 0;
sky_max = 1;
Com_Memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) );
for (i=0 ; i<6 ; i++)
{
int sky_mins_subd[2], sky_maxs_subd[2];
int s, t;
sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) ||
( sky_mins[1][i] >= sky_maxs[1][i] ) )
{
continue;
}
sky_mins_subd[0] = sky_mins[0][i] * HALF_SKY_SUBDIVISIONS;
sky_mins_subd[1] = sky_mins[1][i] * HALF_SKY_SUBDIVISIONS;
sky_maxs_subd[0] = sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS;
sky_maxs_subd[1] = sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS;
if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS )
sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS;
else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS )
sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS;
if ( sky_mins_subd[1] < -HALF_SKY_SUBDIVISIONS )
sky_mins_subd[1] = -HALF_SKY_SUBDIVISIONS;
else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS )
sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS;
if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS )
sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS;
else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS )
sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS;
if ( sky_maxs_subd[1] < -HALF_SKY_SUBDIVISIONS )
sky_maxs_subd[1] = -HALF_SKY_SUBDIVISIONS;
else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS )
sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS;
//
// iterate through the subdivisions
//
for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ )
{
for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ )
{
MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
i,
s_skyTexCoords[t][s],
s_skyPoints[t][s] );
}
}
DrawSkySide( shader->sky.outerbox[sky_texorder[i]],
sky_mins_subd,
sky_maxs_subd );
}
}
static void FillCloudySkySide( const int mins[2], const int maxs[2], qboolean addIndexes )
{
int s, t;
int vertexStart = tess.numVertexes;
int tHeight, sWidth;
tHeight = maxs[1] - mins[1] + 1;
sWidth = maxs[0] - mins[0] + 1;
for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ )
{
for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ )
{
VectorAdd( s_skyPoints[t][s], backEnd.viewParms.or.origin, tess.xyz[tess.numVertexes] );
tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t][s][0];
tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t][s][1];
tess.numVertexes++;
if ( tess.numVertexes >= SHADER_MAX_VERTEXES )
{
ri.Error( ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()" );
}
}
}
// only add indexes for one pass, otherwise it would draw multiple times for each pass
if ( addIndexes ) {
for ( t = 0; t < tHeight-1; t++ )
{
for ( s = 0; s < sWidth-1; s++ )
{
tess.indexes[tess.numIndexes] = vertexStart + s + t * ( sWidth );
tess.numIndexes++;
tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth );
tess.numIndexes++;
tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth );
tess.numIndexes++;
tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth );
tess.numIndexes++;
tess.indexes[tess.numIndexes] = vertexStart + s + 1 + ( t + 1 ) * ( sWidth );
tess.numIndexes++;
tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth );
tess.numIndexes++;
}
}
}
}
static void FillCloudBox( const shader_t *shader, int stage )
{
int i;
for ( i =0; i < 6; i++ )
{
int sky_mins_subd[2], sky_maxs_subd[2];
int s, t;
float MIN_T;
if ( 1 ) // FIXME? shader->sky.fullClouds )
{
MIN_T = -HALF_SKY_SUBDIVISIONS;
// still don't want to draw the bottom, even if fullClouds
if ( i == 5 )
continue;
}
else
{
switch( i )
{
case 0:
case 1:
case 2:
case 3:
MIN_T = -1;
break;
case 5:
// don't draw clouds beneath you
continue;
case 4: // top
default:
MIN_T = -HALF_SKY_SUBDIVISIONS;
break;
}
}
sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) ||
( sky_mins[1][i] >= sky_maxs[1][i] ) )
{
continue;
}
sky_mins_subd[0] = ri.ftol(sky_mins[0][i] * HALF_SKY_SUBDIVISIONS);
sky_mins_subd[1] = ri.ftol(sky_mins[1][i] * HALF_SKY_SUBDIVISIONS);
sky_maxs_subd[0] = ri.ftol(sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS);
sky_maxs_subd[1] = ri.ftol(sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS);
if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS )
sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS;
else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS )
sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS;
if ( sky_mins_subd[1] < MIN_T )
sky_mins_subd[1] = MIN_T;
else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS )
sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS;
if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS )
sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS;
else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS )
sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS;
if ( sky_maxs_subd[1] < MIN_T )
sky_maxs_subd[1] = MIN_T;
else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS )
sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS;
//
// iterate through the subdivisions
//
for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ )
{
for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ )
{
MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
i,
NULL,
s_skyPoints[t][s] );
s_skyTexCoords[t][s][0] = s_cloudTexCoords[i][t][s][0];
s_skyTexCoords[t][s][1] = s_cloudTexCoords[i][t][s][1];
}
}
// only add indexes for first stage
FillCloudySkySide( sky_mins_subd, sky_maxs_subd, ( stage == 0 ) );
}
}
/*
** R_BuildCloudData
*/
void R_BuildCloudData( shaderCommands_t *input )
{
int i;
shader_t *shader;
shader = input->shader;
assert( shader->isSky );
sky_min = 1.0 / 256.0f; // FIXME: not correct?
sky_max = 255.0 / 256.0f;
// set up for drawing
tess.numIndexes = 0;
tess.numVertexes = 0;
tess.firstIndex = 0;
if ( shader->sky.cloudHeight )
{
for ( i = 0; i < MAX_SHADER_STAGES; i++ )
{
if ( !tess.xstages[i] ) {
break;
}
FillCloudBox( shader, i );
}
}
}
/*
** R_InitSkyTexCoords
** Called when a sky shader is parsed
*/
#define SQR( a ) ((a)*(a))
void R_InitSkyTexCoords( float heightCloud )
{
int i, s, t;
float radiusWorld = 4096;
float p;
float sRad, tRad;
vec3_t skyVec;
vec3_t v;
// init zfar so MakeSkyVec works even though
// a world hasn't been bounded
backEnd.viewParms.zFar = 1024;
for ( i = 0; i < 6; i++ )
{
for ( t = 0; t <= SKY_SUBDIVISIONS; t++ )
{
for ( s = 0; s <= SKY_SUBDIVISIONS; s++ )
{
// compute vector from view origin to sky side integral point
MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
i,
NULL,
skyVec );
// compute parametric value 'p' that intersects with cloud layer
p = ( 1.0f / ( 2 * DotProduct( skyVec, skyVec ) ) ) *
( -2 * skyVec[2] * radiusWorld +
2 * sqrt( SQR( skyVec[2] ) * SQR( radiusWorld ) +
2 * SQR( skyVec[0] ) * radiusWorld * heightCloud +
SQR( skyVec[0] ) * SQR( heightCloud ) +
2 * SQR( skyVec[1] ) * radiusWorld * heightCloud +
SQR( skyVec[1] ) * SQR( heightCloud ) +
2 * SQR( skyVec[2] ) * radiusWorld * heightCloud +
SQR( skyVec[2] ) * SQR( heightCloud ) ) );
s_cloudTexP[i][t][s] = p;
// compute intersection point based on p
VectorScale( skyVec, p, v );
v[2] += radiusWorld;
// compute vector from world origin to intersection point 'v'
VectorNormalize( v );
sRad = Q_acos( v[0] );
tRad = Q_acos( v[1] );
s_cloudTexCoords[i][t][s][0] = sRad;
s_cloudTexCoords[i][t][s][1] = tRad;
}
}
}
}
//======================================================================================
/*
** RB_DrawSun
*/
void RB_DrawSun( float scale, shader_t *shader ) {
float size;
float dist;
vec3_t origin, vec1, vec2;
if ( !backEnd.skyRenderedThisView ) {
return;
}
//qglLoadMatrixf( backEnd.viewParms.world.modelMatrix );
//qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]);
{
// FIXME: this could be a lot cleaner
matrix_t translation, modelview;
Matrix16Translation( backEnd.viewParms.or.origin, translation );
Matrix16Multiply( backEnd.viewParms.world.modelMatrix, translation, modelview );
GL_SetModelviewMatrix( modelview );
}
dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3)
size = dist * scale;
VectorScale( tr.sunDirection, dist, origin );
PerpendicularVector( vec1, tr.sunDirection );
CrossProduct( tr.sunDirection, vec1, vec2 );
VectorScale( vec1, size, vec1 );
VectorScale( vec2, size, vec2 );
// farthest depth range
qglDepthRange( 1.0, 1.0 );
RB_BeginSurface( shader, 0 );
RB_AddQuadStamp(origin, vec1, vec2, colorWhite);
RB_EndSurface();
// back to normal depth range
qglDepthRange( 0.0, 1.0 );
}
/*
================
RB_StageIteratorSky
All of the visible sky triangles are in tess
Other things could be stuck in here, like birds in the sky, etc
================
*/
void RB_StageIteratorSky( void ) {
if ( r_fastsky->integer ) {
return;
}
// go through all the polygons and project them onto
// the sky box to see which blocks on each side need
// to be drawn
RB_ClipSkyPolygons( &tess );
// r_showsky will let all the sky blocks be drawn in
// front of everything to allow developers to see how
// much sky is getting sucked in
if ( r_showsky->integer ) {
qglDepthRange( 0.0, 0.0 );
} else {
qglDepthRange( 1.0, 1.0 );
}
// draw the outer skybox
if ( tess.shader->sky.outerbox[0] && tess.shader->sky.outerbox[0] != tr.defaultImage ) {
matrix_t oldmodelview;
GL_State( 0 );
//qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]);
{
// FIXME: this could be a lot cleaner
matrix_t trans, product;
Matrix16Copy( glState.modelview, oldmodelview );
Matrix16Translation( backEnd.viewParms.or.origin, trans );
Matrix16Multiply( glState.modelview, trans, product );
GL_SetModelviewMatrix( product );
}
DrawSkyBox( tess.shader );
GL_SetModelviewMatrix( oldmodelview );
}
// generate the vertexes for all the clouds, which will be drawn
// by the generic shader routine
R_BuildCloudData( &tess );
RB_StageIteratorGeneric();
// draw the inner skybox
// back to normal depth range
qglDepthRange( 0.0, 1.0 );
// note that sky was drawn so we will draw a sun later
backEnd.skyRenderedThisView = qtrue;
}

View file

@ -0,0 +1,48 @@
/*
===========================================================================
Copyright (C) 2010 James Canete (use.less01@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_subs.c - common function replacements for modular renderer
#include "tr_local.h"
void QDECL Com_Printf( const char *msg, ... )
{
va_list argptr;
char text[1024];
va_start(argptr, msg);
Q_vsnprintf(text, sizeof(text), msg, argptr);
va_end(argptr);
ri.Printf(PRINT_ALL, "%s", text);
}
void QDECL Com_Error( int level, const char *error, ... )
{
va_list argptr;
char text[1024];
va_start(argptr, error);
Q_vsnprintf(text, sizeof(text), error, argptr);
va_end(argptr);
ri.Error(level, "%s", text);
}

File diff suppressed because it is too large Load diff

928
code/renderergl2/tr_vbo.c Normal file
View file

@ -0,0 +1,928 @@
/*
===========================================================================
Copyright (C) 2007-2009 Robert Beckebans <trebor_7@users.sourceforge.net>
This file is part of XreaL source code.
XreaL source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
XreaL source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with XreaL source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_vbo.c
#include "tr_local.h"
/*
============
R_CreateVBO
============
*/
VBO_t *R_CreateVBO(const char *name, byte * vertexes, int vertexesSize, vboUsage_t usage)
{
VBO_t *vbo;
int glUsage;
switch (usage)
{
case VBO_USAGE_STATIC:
glUsage = GL_STATIC_DRAW_ARB;
break;
case VBO_USAGE_DYNAMIC:
glUsage = GL_DYNAMIC_DRAW_ARB;
break;
default:
Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage);
return NULL;
}
if(strlen(name) >= MAX_QPATH)
{
ri.Error(ERR_DROP, "R_CreateVBO: \"%s\" is too long", name);
}
if ( tr.numVBOs == MAX_VBOS ) {
ri.Error( ERR_DROP, "R_CreateVBO: MAX_VBOS hit");
}
R_IssuePendingRenderCommands();
vbo = tr.vbos[tr.numVBOs] = ri.Hunk_Alloc(sizeof(*vbo), h_low);
tr.numVBOs++;
memset(vbo, 0, sizeof(*vbo));
Q_strncpyz(vbo->name, name, sizeof(vbo->name));
vbo->vertexesSize = vertexesSize;
qglGenBuffersARB(1, &vbo->vertexesVBO);
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO);
qglBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexesSize, vertexes, glUsage);
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glState.currentVBO = NULL;
GL_CheckErrors();
return vbo;
}
/*
============
R_CreateVBO2
============
*/
VBO_t *R_CreateVBO2(const char *name, int numVertexes, srfVert_t * verts, unsigned int stateBits, vboUsage_t usage)
{
VBO_t *vbo;
int i;
byte *data;
int dataSize;
int dataOfs;
int glUsage;
switch (usage)
{
case VBO_USAGE_STATIC:
glUsage = GL_STATIC_DRAW_ARB;
break;
case VBO_USAGE_DYNAMIC:
glUsage = GL_DYNAMIC_DRAW_ARB;
break;
default:
Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage);
return NULL;
}
if(!numVertexes)
return NULL;
if(strlen(name) >= MAX_QPATH)
{
ri.Error(ERR_DROP, "R_CreateVBO2: \"%s\" is too long", name);
}
if ( tr.numVBOs == MAX_VBOS ) {
ri.Error( ERR_DROP, "R_CreateVBO2: MAX_VBOS hit");
}
R_IssuePendingRenderCommands();
vbo = tr.vbos[tr.numVBOs] = ri.Hunk_Alloc(sizeof(*vbo), h_low);
tr.numVBOs++;
memset(vbo, 0, sizeof(*vbo));
Q_strncpyz(vbo->name, name, sizeof(vbo->name));
if (usage == VBO_USAGE_STATIC)
{
// since these vertex attributes are never altered, interleave them
vbo->ofs_xyz = 0;
dataSize = sizeof(verts[0].xyz);
if(stateBits & ATTR_NORMAL)
{
vbo->ofs_normal = dataSize;
dataSize += sizeof(verts[0].normal);
}
#ifdef USE_VERT_TANGENT_SPACE
if(stateBits & ATTR_TANGENT)
{
vbo->ofs_tangent = dataSize;
dataSize += sizeof(verts[0].tangent);
}
if(stateBits & ATTR_BITANGENT)
{
vbo->ofs_bitangent = dataSize;
dataSize += sizeof(verts[0].bitangent);
}
#endif
if(stateBits & ATTR_TEXCOORD)
{
vbo->ofs_st = dataSize;
dataSize += sizeof(verts[0].st);
}
if(stateBits & ATTR_LIGHTCOORD)
{
vbo->ofs_lightmap = dataSize;
dataSize += sizeof(verts[0].lightmap);
}
if(stateBits & ATTR_COLOR)
{
vbo->ofs_vertexcolor = dataSize;
dataSize += sizeof(verts[0].vertexColors);
}
if(stateBits & ATTR_LIGHTDIRECTION)
{
vbo->ofs_lightdir = dataSize;
dataSize += sizeof(verts[0].lightdir);
}
vbo->stride_xyz = dataSize;
vbo->stride_normal = dataSize;
#ifdef USE_VERT_TANGENT_SPACE
vbo->stride_tangent = dataSize;
vbo->stride_bitangent = dataSize;
#endif
vbo->stride_st = dataSize;
vbo->stride_lightmap = dataSize;
vbo->stride_vertexcolor = dataSize;
vbo->stride_lightdir = dataSize;
// create VBO
dataSize *= numVertexes;
data = ri.Hunk_AllocateTempMemory(dataSize);
dataOfs = 0;
//ri.Printf(PRINT_ALL, "CreateVBO: %d, %d %d %d %d %d, %d %d %d %d %d\n", dataSize, vbo->ofs_xyz, vbo->ofs_normal, vbo->ofs_st, vbo->ofs_lightmap, vbo->ofs_vertexcolor,
//vbo->stride_xyz, vbo->stride_normal, vbo->stride_st, vbo->stride_lightmap, vbo->stride_vertexcolor);
for (i = 0; i < numVertexes; i++)
{
// xyz
memcpy(data + dataOfs, &verts[i].xyz, sizeof(verts[i].xyz));
dataOfs += sizeof(verts[i].xyz);
// normal
if(stateBits & ATTR_NORMAL)
{
memcpy(data + dataOfs, &verts[i].normal, sizeof(verts[i].normal));
dataOfs += sizeof(verts[i].normal);
}
#ifdef USE_VERT_TANGENT_SPACE
// tangent
if(stateBits & ATTR_TANGENT)
{
memcpy(data + dataOfs, &verts[i].tangent, sizeof(verts[i].tangent));
dataOfs += sizeof(verts[i].tangent);
}
// bitangent
if(stateBits & ATTR_BITANGENT)
{
memcpy(data + dataOfs, &verts[i].bitangent, sizeof(verts[i].bitangent));
dataOfs += sizeof(verts[i].bitangent);
}
#endif
// vertex texcoords
if(stateBits & ATTR_TEXCOORD)
{
memcpy(data + dataOfs, &verts[i].st, sizeof(verts[i].st));
dataOfs += sizeof(verts[i].st);
}
// feed vertex lightmap texcoords
if(stateBits & ATTR_LIGHTCOORD)
{
memcpy(data + dataOfs, &verts[i].lightmap, sizeof(verts[i].lightmap));
dataOfs += sizeof(verts[i].lightmap);
}
// feed vertex colors
if(stateBits & ATTR_COLOR)
{
memcpy(data + dataOfs, &verts[i].vertexColors, sizeof(verts[i].vertexColors));
dataOfs += sizeof(verts[i].vertexColors);
}
// feed vertex light directions
if(stateBits & ATTR_LIGHTDIRECTION)
{
memcpy(data + dataOfs, &verts[i].lightdir, sizeof(verts[i].lightdir));
dataOfs += sizeof(verts[i].lightdir);
}
}
}
else
{
// since these vertex attributes may be changed, put them in flat arrays
dataSize = sizeof(verts[0].xyz);
if(stateBits & ATTR_NORMAL)
{
dataSize += sizeof(verts[0].normal);
}
#ifdef USE_VERT_TANGENT_SPACE
if(stateBits & ATTR_TANGENT)
{
dataSize += sizeof(verts[0].tangent);
}
if(stateBits & ATTR_BITANGENT)
{
dataSize += sizeof(verts[0].bitangent);
}
#endif
if(stateBits & ATTR_TEXCOORD)
{
dataSize += sizeof(verts[0].st);
}
if(stateBits & ATTR_LIGHTCOORD)
{
dataSize += sizeof(verts[0].lightmap);
}
if(stateBits & ATTR_COLOR)
{
dataSize += sizeof(verts[0].vertexColors);
}
if(stateBits & ATTR_LIGHTDIRECTION)
{
dataSize += sizeof(verts[0].lightdir);
}
// create VBO
dataSize *= numVertexes;
data = ri.Hunk_AllocateTempMemory(dataSize);
dataOfs = 0;
vbo->ofs_xyz = 0;
vbo->ofs_normal = 0;
#ifdef USE_VERT_TANGENT_SPACE
vbo->ofs_tangent = 0;
vbo->ofs_bitangent = 0;
#endif
vbo->ofs_st = 0;
vbo->ofs_lightmap = 0;
vbo->ofs_vertexcolor = 0;
vbo->ofs_lightdir = 0;
vbo->stride_xyz = sizeof(verts[0].xyz);
vbo->stride_normal = sizeof(verts[0].normal);
#ifdef USE_VERT_TANGENT_SPACE
vbo->stride_tangent = sizeof(verts[0].tangent);
vbo->stride_bitangent = sizeof(verts[0].bitangent);
#endif
vbo->stride_vertexcolor = sizeof(verts[0].vertexColors);
vbo->stride_st = sizeof(verts[0].st);
vbo->stride_lightmap = sizeof(verts[0].lightmap);
vbo->stride_lightdir = sizeof(verts[0].lightdir);
//ri.Printf(PRINT_ALL, "2CreateVBO: %d, %d %d %d %d %d, %d %d %d %d %d\n", dataSize, vbo->ofs_xyz, vbo->ofs_normal, vbo->ofs_st, vbo->ofs_lightmap, vbo->ofs_vertexcolor,
//vbo->stride_xyz, vbo->stride_normal, vbo->stride_st, vbo->stride_lightmap, vbo->stride_vertexcolor);
// xyz
for (i = 0; i < numVertexes; i++)
{
memcpy(data + dataOfs, &verts[i].xyz, sizeof(verts[i].xyz));
dataOfs += sizeof(verts[i].xyz);
}
// normal
if(stateBits & ATTR_NORMAL)
{
vbo->ofs_normal = dataOfs;
for (i = 0; i < numVertexes; i++)
{
memcpy(data + dataOfs, &verts[i].normal, sizeof(verts[i].normal));
dataOfs += sizeof(verts[i].normal);
}
}
#ifdef USE_VERT_TANGENT_SPACE
// tangent
if(stateBits & ATTR_TANGENT)
{
vbo->ofs_tangent = dataOfs;
for (i = 0; i < numVertexes; i++)
{
memcpy(data + dataOfs, &verts[i].tangent, sizeof(verts[i].tangent));
dataOfs += sizeof(verts[i].tangent);
}
}
// bitangent
if(stateBits & ATTR_BITANGENT)
{
vbo->ofs_bitangent = dataOfs;
for (i = 0; i < numVertexes; i++)
{
memcpy(data + dataOfs, &verts[i].bitangent, sizeof(verts[i].bitangent));
dataOfs += sizeof(verts[i].bitangent);
}
}
#endif
// vertex texcoords
if(stateBits & ATTR_TEXCOORD)
{
vbo->ofs_st = dataOfs;
for (i = 0; i < numVertexes; i++)
{
memcpy(data + dataOfs, &verts[i].st, sizeof(verts[i].st));
dataOfs += sizeof(verts[i].st);
}
}
// feed vertex lightmap texcoords
if(stateBits & ATTR_LIGHTCOORD)
{
vbo->ofs_lightmap = dataOfs;
for (i = 0; i < numVertexes; i++)
{
memcpy(data + dataOfs, &verts[i].lightmap, sizeof(verts[i].lightmap));
dataOfs += sizeof(verts[i].lightmap);
}
}
// feed vertex colors
if(stateBits & ATTR_COLOR)
{
vbo->ofs_vertexcolor = dataOfs;
for (i = 0; i < numVertexes; i++)
{
memcpy(data + dataOfs, &verts[i].vertexColors, sizeof(verts[i].vertexColors));
dataOfs += sizeof(verts[i].vertexColors);
}
}
// feed vertex lightdirs
if(stateBits & ATTR_LIGHTDIRECTION)
{
vbo->ofs_lightdir = dataOfs;
for (i = 0; i < numVertexes; i++)
{
memcpy(data + dataOfs, &verts[i].lightdir, sizeof(verts[i].lightdir));
dataOfs += sizeof(verts[i].lightdir);
}
}
}
vbo->vertexesSize = dataSize;
qglGenBuffersARB(1, &vbo->vertexesVBO);
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO);
qglBufferDataARB(GL_ARRAY_BUFFER_ARB, dataSize, data, glUsage);
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glState.currentVBO = NULL;
GL_CheckErrors();
ri.Hunk_FreeTempMemory(data);
return vbo;
}
/*
============
R_CreateIBO
============
*/
IBO_t *R_CreateIBO(const char *name, byte * indexes, int indexesSize, vboUsage_t usage)
{
IBO_t *ibo;
int glUsage;
switch (usage)
{
case VBO_USAGE_STATIC:
glUsage = GL_STATIC_DRAW_ARB;
break;
case VBO_USAGE_DYNAMIC:
glUsage = GL_DYNAMIC_DRAW_ARB;
break;
default:
Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage);
return NULL;
}
if(strlen(name) >= MAX_QPATH)
{
ri.Error(ERR_DROP, "R_CreateIBO: \"%s\" is too long", name);
}
if ( tr.numIBOs == MAX_IBOS ) {
ri.Error( ERR_DROP, "R_CreateIBO: MAX_IBOS hit");
}
R_IssuePendingRenderCommands();
ibo = tr.ibos[tr.numIBOs] = ri.Hunk_Alloc(sizeof(*ibo), h_low);
tr.numIBOs++;
Q_strncpyz(ibo->name, name, sizeof(ibo->name));
ibo->indexesSize = indexesSize;
qglGenBuffersARB(1, &ibo->indexesVBO);
qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO);
qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexesSize, indexes, glUsage);
qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
glState.currentIBO = NULL;
GL_CheckErrors();
return ibo;
}
/*
============
R_CreateIBO2
============
*/
IBO_t *R_CreateIBO2(const char *name, int numTriangles, srfTriangle_t * triangles, vboUsage_t usage)
{
IBO_t *ibo;
int i, j;
byte *indexes;
int indexesSize;
int indexesOfs;
srfTriangle_t *tri;
glIndex_t index;
int glUsage;
switch (usage)
{
case VBO_USAGE_STATIC:
glUsage = GL_STATIC_DRAW_ARB;
break;
case VBO_USAGE_DYNAMIC:
glUsage = GL_DYNAMIC_DRAW_ARB;
break;
default:
Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage);
return NULL;
}
if(!numTriangles)
return NULL;
if(strlen(name) >= MAX_QPATH)
{
ri.Error(ERR_DROP, "R_CreateIBO2: \"%s\" is too long", name);
}
if ( tr.numIBOs == MAX_IBOS ) {
ri.Error( ERR_DROP, "R_CreateIBO2: MAX_IBOS hit");
}
R_IssuePendingRenderCommands();
ibo = tr.ibos[tr.numIBOs] = ri.Hunk_Alloc(sizeof(*ibo), h_low);
tr.numIBOs++;
Q_strncpyz(ibo->name, name, sizeof(ibo->name));
indexesSize = numTriangles * 3 * sizeof(int);
indexes = ri.Hunk_AllocateTempMemory(indexesSize);
indexesOfs = 0;
for(i = 0, tri = triangles; i < numTriangles; i++, tri++)
{
for(j = 0; j < 3; j++)
{
index = tri->indexes[j];
memcpy(indexes + indexesOfs, &index, sizeof(glIndex_t));
indexesOfs += sizeof(glIndex_t);
}
}
ibo->indexesSize = indexesSize;
qglGenBuffersARB(1, &ibo->indexesVBO);
qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO);
qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexesSize, indexes, glUsage);
qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
glState.currentIBO = NULL;
GL_CheckErrors();
ri.Hunk_FreeTempMemory(indexes);
return ibo;
}
/*
============
R_BindVBO
============
*/
void R_BindVBO(VBO_t * vbo)
{
if(!vbo)
{
//R_BindNullVBO();
ri.Error(ERR_DROP, "R_BindNullVBO: NULL vbo");
return;
}
if(r_logFile->integer)
{
// don't just call LogComment, or we will get a call to va() every frame!
GLimp_LogComment(va("--- R_BindVBO( %s ) ---\n", vbo->name));
}
if(glState.currentVBO != vbo)
{
glState.currentVBO = vbo;
glState.vertexAttribPointersSet = 0;
glState.vertexAttribsInterpolation = 0;
glState.vertexAttribsOldFrame = 0;
glState.vertexAttribsNewFrame = 0;
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO);
backEnd.pc.c_vboVertexBuffers++;
}
}
/*
============
R_BindNullVBO
============
*/
void R_BindNullVBO(void)
{
GLimp_LogComment("--- R_BindNullVBO ---\n");
if(glState.currentVBO)
{
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glState.currentVBO = NULL;
}
GL_CheckErrors();
}
/*
============
R_BindIBO
============
*/
void R_BindIBO(IBO_t * ibo)
{
if(!ibo)
{
//R_BindNullIBO();
ri.Error(ERR_DROP, "R_BindIBO: NULL ibo");
return;
}
if(r_logFile->integer)
{
// don't just call LogComment, or we will get a call to va() every frame!
GLimp_LogComment(va("--- R_BindIBO( %s ) ---\n", ibo->name));
}
if(glState.currentIBO != ibo)
{
qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO);
glState.currentIBO = ibo;
backEnd.pc.c_vboIndexBuffers++;
}
}
/*
============
R_BindNullIBO
============
*/
void R_BindNullIBO(void)
{
GLimp_LogComment("--- R_BindNullIBO ---\n");
if(glState.currentIBO)
{
qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
glState.currentIBO = NULL;
glState.vertexAttribPointersSet = 0;
}
}
/*
============
R_InitVBOs
============
*/
void R_InitVBOs(void)
{
int dataSize;
int offset;
ri.Printf(PRINT_ALL, "------- R_InitVBOs -------\n");
tr.numVBOs = 0;
tr.numIBOs = 0;
dataSize = sizeof(tess.xyz[0]);
dataSize += sizeof(tess.normal[0]);
#ifdef USE_VERT_TANGENT_SPACE
dataSize += sizeof(tess.tangent[0]);
dataSize += sizeof(tess.bitangent[0]);
#endif
dataSize += sizeof(tess.vertexColors[0]);
dataSize += sizeof(tess.texCoords[0][0]) * 2;
dataSize += sizeof(tess.lightdir[0]);
dataSize *= SHADER_MAX_VERTEXES;
tess.vbo = R_CreateVBO("tessVertexArray_VBO", NULL, dataSize, VBO_USAGE_DYNAMIC);
offset = 0;
tess.vbo->ofs_xyz = offset; offset += sizeof(tess.xyz[0]) * SHADER_MAX_VERTEXES;
tess.vbo->ofs_normal = offset; offset += sizeof(tess.normal[0]) * SHADER_MAX_VERTEXES;
#ifdef USE_VERT_TANGENT_SPACE
tess.vbo->ofs_tangent = offset; offset += sizeof(tess.tangent[0]) * SHADER_MAX_VERTEXES;
tess.vbo->ofs_bitangent = offset; offset += sizeof(tess.bitangent[0]) * SHADER_MAX_VERTEXES;
#endif
// these next two are actually interleaved
tess.vbo->ofs_st = offset;
tess.vbo->ofs_lightmap = offset + sizeof(tess.texCoords[0][0]);
offset += sizeof(tess.texCoords[0][0]) * 2 * SHADER_MAX_VERTEXES;
tess.vbo->ofs_vertexcolor = offset; offset += sizeof(tess.vertexColors[0]) * SHADER_MAX_VERTEXES;
tess.vbo->ofs_lightdir = offset;
tess.vbo->stride_xyz = sizeof(tess.xyz[0]);
tess.vbo->stride_normal = sizeof(tess.normal[0]);
#ifdef USE_VERT_TANGENT_SPACE
tess.vbo->stride_tangent = sizeof(tess.tangent[0]);
tess.vbo->stride_bitangent = sizeof(tess.bitangent[0]);
#endif
tess.vbo->stride_vertexcolor = sizeof(tess.vertexColors[0]);
tess.vbo->stride_st = sizeof(tess.texCoords[0][0]) * 2;
tess.vbo->stride_lightmap = sizeof(tess.texCoords[0][0]) * 2;
tess.vbo->stride_lightdir = sizeof(tess.lightdir[0]);
dataSize = sizeof(tess.indexes[0]) * SHADER_MAX_INDEXES;
tess.ibo = R_CreateIBO("tessVertexArray_IBO", NULL, dataSize, VBO_USAGE_DYNAMIC);
R_BindNullVBO();
R_BindNullIBO();
GL_CheckErrors();
}
/*
============
R_ShutdownVBOs
============
*/
void R_ShutdownVBOs(void)
{
int i;
VBO_t *vbo;
IBO_t *ibo;
ri.Printf(PRINT_ALL, "------- R_ShutdownVBOs -------\n");
R_BindNullVBO();
R_BindNullIBO();
for(i = 0; i < tr.numVBOs; i++)
{
vbo = tr.vbos[i];
if(vbo->vertexesVBO)
{
qglDeleteBuffersARB(1, &vbo->vertexesVBO);
}
//ri.Free(vbo);
}
for(i = 0; i < tr.numIBOs; i++)
{
ibo = tr.ibos[i];
if(ibo->indexesVBO)
{
qglDeleteBuffersARB(1, &ibo->indexesVBO);
}
//ri.Free(ibo);
}
tr.numVBOs = 0;
tr.numIBOs = 0;
}
/*
============
R_VBOList_f
============
*/
void R_VBOList_f(void)
{
int i;
VBO_t *vbo;
IBO_t *ibo;
int vertexesSize = 0;
int indexesSize = 0;
ri.Printf(PRINT_ALL, " size name\n");
ri.Printf(PRINT_ALL, "----------------------------------------------------------\n");
for(i = 0; i < tr.numVBOs; i++)
{
vbo = tr.vbos[i];
ri.Printf(PRINT_ALL, "%d.%02d MB %s\n", vbo->vertexesSize / (1024 * 1024),
(vbo->vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024), vbo->name);
vertexesSize += vbo->vertexesSize;
}
for(i = 0; i < tr.numIBOs; i++)
{
ibo = tr.ibos[i];
ri.Printf(PRINT_ALL, "%d.%02d MB %s\n", ibo->indexesSize / (1024 * 1024),
(ibo->indexesSize % (1024 * 1024)) * 100 / (1024 * 1024), ibo->name);
indexesSize += ibo->indexesSize;
}
ri.Printf(PRINT_ALL, " %i total VBOs\n", tr.numVBOs);
ri.Printf(PRINT_ALL, " %d.%02d MB total vertices memory\n", vertexesSize / (1024 * 1024),
(vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024));
ri.Printf(PRINT_ALL, " %i total IBOs\n", tr.numIBOs);
ri.Printf(PRINT_ALL, " %d.%02d MB total triangle indices memory\n", indexesSize / (1024 * 1024),
(indexesSize % (1024 * 1024)) * 100 / (1024 * 1024));
}
/*
==============
RB_UpdateVBOs
Adapted from Tess_UpdateVBOs from xreal
Update the default VBO to replace the client side vertex arrays
==============
*/
void RB_UpdateVBOs(unsigned int attribBits)
{
GLimp_LogComment("--- RB_UpdateVBOs ---\n");
backEnd.pc.c_dynamicVboDraws++;
// update the default VBO
if(tess.numVertexes > 0 && tess.numVertexes <= SHADER_MAX_VERTEXES)
{
R_BindVBO(tess.vbo);
if(attribBits & ATTR_BITS)
{
if(attribBits & ATTR_POSITION)
{
//ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0]));
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0]), tess.xyz);
}
if(attribBits & ATTR_TEXCOORD || attribBits & ATTR_LIGHTCOORD)
{
// these are interleaved, so we update both if either need it
//ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2);
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2, tess.texCoords);
}
if(attribBits & ATTR_NORMAL)
{
//ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0]));
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0]), tess.normal);
}
#ifdef USE_VERT_TANGENT_SPACE
if(attribBits & ATTR_TANGENT)
{
//ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0]));
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0]), tess.tangent);
}
if(attribBits & ATTR_BITANGENT)
{
//ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_bitangent, tess.numVertexes * sizeof(tess.bitangent[0]));
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_bitangent, tess.numVertexes * sizeof(tess.bitangent[0]), tess.bitangent);
}
#endif
if(attribBits & ATTR_COLOR)
{
//ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0]));
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0]), tess.vertexColors);
}
if(attribBits & ATTR_LIGHTDIRECTION)
{
//ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0]));
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0]), tess.lightdir);
}
}
else
{
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0]), tess.xyz);
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2, tess.texCoords);
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0]), tess.normal);
#ifdef USE_VERT_TANGENT_SPACE
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0]), tess.tangent);
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_bitangent, tess.numVertexes * sizeof(tess.bitangent[0]), tess.bitangent);
#endif
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0]), tess.vertexColors);
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0]), tess.lightdir);
}
}
// update the default IBO
if(tess.numIndexes > 0 && tess.numIndexes <= SHADER_MAX_INDEXES)
{
R_BindIBO(tess.ibo);
qglBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, tess.numIndexes * sizeof(tess.indexes[0]), tess.indexes);
}
}

850
code/renderergl2/tr_world.c Normal file
View file

@ -0,0 +1,850 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "tr_local.h"
/*
================
R_CullSurface
Tries to cull surfaces before they are lighted or
added to the sorting list.
================
*/
static qboolean R_CullSurface( msurface_t *surf ) {
if ( r_nocull->integer || surf->cullinfo.type == CULLINFO_NONE) {
return qfalse;
}
if (surf->cullinfo.type & CULLINFO_PLANE)
{
// Only true for SF_FACE, so treat like its own function
float d;
cullType_t ct;
if ( !r_facePlaneCull->integer ) {
return qfalse;
}
ct = surf->shader->cullType;
if (ct == CT_TWO_SIDED)
{
return qfalse;
}
// don't cull for depth shadow
/*
if ( tr.viewParms.flags & VPF_DEPTHSHADOW )
{
return qfalse;
}
*/
// shadowmaps draw back surfaces
if ( tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW) )
{
if (ct == CT_FRONT_SIDED)
{
ct = CT_BACK_SIDED;
}
else
{
ct = CT_FRONT_SIDED;
}
}
// do proper cull for orthographic projection
if (tr.viewParms.flags & VPF_ORTHOGRAPHIC) {
d = DotProduct(tr.viewParms.or.axis[0], surf->cullinfo.plane.normal);
if ( ct == CT_FRONT_SIDED ) {
if (d > 0)
return qtrue;
} else {
if (d < 0)
return qtrue;
}
return qfalse;
}
d = DotProduct (tr.or.viewOrigin, surf->cullinfo.plane.normal);
// don't cull exactly on the plane, because there are levels of rounding
// through the BSP, ICD, and hardware that may cause pixel gaps if an
// epsilon isn't allowed here
if ( ct == CT_FRONT_SIDED ) {
if ( d < surf->cullinfo.plane.dist - 8 ) {
return qtrue;
}
} else {
if ( d > surf->cullinfo.plane.dist + 8 ) {
return qtrue;
}
}
return qfalse;
}
if (surf->cullinfo.type & CULLINFO_SPHERE)
{
int sphereCull;
if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) {
sphereCull = R_CullLocalPointAndRadius( surf->cullinfo.localOrigin, surf->cullinfo.radius );
} else {
sphereCull = R_CullPointAndRadius( surf->cullinfo.localOrigin, surf->cullinfo.radius );
}
if ( sphereCull == CULL_OUT )
{
return qtrue;
}
}
if (surf->cullinfo.type & CULLINFO_BOX)
{
int boxCull;
if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) {
boxCull = R_CullLocalBox( surf->cullinfo.bounds );
} else {
boxCull = R_CullBox( surf->cullinfo.bounds );
}
if ( boxCull == CULL_OUT )
{
return qtrue;
}
}
return qfalse;
}
/*
====================
R_DlightSurface
The given surface is going to be drawn, and it touches a leaf
that is touched by one or more dlights, so try to throw out
more dlights if possible.
====================
*/
static int R_DlightSurface( msurface_t *surf, int dlightBits ) {
float d;
int i;
dlight_t *dl;
if ( surf->cullinfo.type & CULLINFO_PLANE )
{
int i;
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
if ( ! ( dlightBits & ( 1 << i ) ) ) {
continue;
}
dl = &tr.refdef.dlights[i];
d = DotProduct( dl->origin, surf->cullinfo.plane.normal ) - surf->cullinfo.plane.dist;
if ( d < -dl->radius || d > dl->radius ) {
// dlight doesn't reach the plane
dlightBits &= ~( 1 << i );
}
}
}
if ( surf->cullinfo.type & CULLINFO_BOX )
{
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
if ( ! ( dlightBits & ( 1 << i ) ) ) {
continue;
}
dl = &tr.refdef.dlights[i];
if ( dl->origin[0] - dl->radius > surf->cullinfo.bounds[1][0]
|| dl->origin[0] + dl->radius < surf->cullinfo.bounds[0][0]
|| dl->origin[1] - dl->radius > surf->cullinfo.bounds[1][1]
|| dl->origin[1] + dl->radius < surf->cullinfo.bounds[0][1]
|| dl->origin[2] - dl->radius > surf->cullinfo.bounds[1][2]
|| dl->origin[2] + dl->radius < surf->cullinfo.bounds[0][2] ) {
// dlight doesn't reach the bounds
dlightBits &= ~( 1 << i );
}
}
}
if ( surf->cullinfo.type & CULLINFO_SPHERE )
{
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
if ( ! ( dlightBits & ( 1 << i ) ) ) {
continue;
}
dl = &tr.refdef.dlights[i];
if (!SpheresIntersect(dl->origin, dl->radius, surf->cullinfo.localOrigin, surf->cullinfo.radius))
{
// dlight doesn't reach the bounds
dlightBits &= ~( 1 << i );
}
}
}
if ( *surf->data == SF_FACE ) {
((srfSurfaceFace_t *)surf->data)->dlightBits = dlightBits;
} else if ( *surf->data == SF_GRID ) {
((srfGridMesh_t *)surf->data)->dlightBits = dlightBits;
} else if ( *surf->data == SF_TRIANGLES ) {
((srfTriangles_t *)surf->data)->dlightBits = dlightBits;
} else if ( *surf->data == SF_VBO_MESH ) {
((srfVBOMesh_t *)surf->data)->dlightBits = dlightBits;
} else {
dlightBits = 0;
}
if ( dlightBits ) {
tr.pc.c_dlightSurfaces++;
}
return dlightBits;
}
/*
====================
R_PshadowSurface
Just like R_DlightSurface, cull any we can
====================
*/
static int R_PshadowSurface( msurface_t *surf, int pshadowBits ) {
float d;
int i;
pshadow_t *ps;
if ( surf->cullinfo.type & CULLINFO_PLANE )
{
int i;
for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) {
if ( ! ( pshadowBits & ( 1 << i ) ) ) {
continue;
}
ps = &tr.refdef.pshadows[i];
d = DotProduct( ps->lightOrigin, surf->cullinfo.plane.normal ) - surf->cullinfo.plane.dist;
if ( d < -ps->lightRadius || d > ps->lightRadius ) {
// pshadow doesn't reach the plane
pshadowBits &= ~( 1 << i );
}
}
}
if ( surf->cullinfo.type & CULLINFO_BOX )
{
for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) {
if ( ! ( pshadowBits & ( 1 << i ) ) ) {
continue;
}
ps = &tr.refdef.pshadows[i];
if ( ps->lightOrigin[0] - ps->lightRadius > surf->cullinfo.bounds[1][0]
|| ps->lightOrigin[0] + ps->lightRadius < surf->cullinfo.bounds[0][0]
|| ps->lightOrigin[1] - ps->lightRadius > surf->cullinfo.bounds[1][1]
|| ps->lightOrigin[1] + ps->lightRadius < surf->cullinfo.bounds[0][1]
|| ps->lightOrigin[2] - ps->lightRadius > surf->cullinfo.bounds[1][2]
|| ps->lightOrigin[2] + ps->lightRadius < surf->cullinfo.bounds[0][2]
|| BoxOnPlaneSide(surf->cullinfo.bounds[0], surf->cullinfo.bounds[1], &ps->cullPlane) == 2 ) {
// pshadow doesn't reach the bounds
pshadowBits &= ~( 1 << i );
}
}
}
if ( surf->cullinfo.type & CULLINFO_SPHERE )
{
for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) {
if ( ! ( pshadowBits & ( 1 << i ) ) ) {
continue;
}
ps = &tr.refdef.pshadows[i];
if (!SpheresIntersect(ps->viewOrigin, ps->viewRadius, surf->cullinfo.localOrigin, surf->cullinfo.radius)
|| DotProduct( surf->cullinfo.localOrigin, ps->cullPlane.normal ) - ps->cullPlane.dist < -surf->cullinfo.radius)
{
// pshadow doesn't reach the bounds
pshadowBits &= ~( 1 << i );
}
}
}
if ( *surf->data == SF_FACE ) {
((srfSurfaceFace_t *)surf->data)->pshadowBits = pshadowBits;
} else if ( *surf->data == SF_GRID ) {
((srfGridMesh_t *)surf->data)->pshadowBits = pshadowBits;
} else if ( *surf->data == SF_TRIANGLES ) {
((srfTriangles_t *)surf->data)->pshadowBits = pshadowBits;
} else if ( *surf->data == SF_VBO_MESH ) {
((srfVBOMesh_t *)surf->data)->pshadowBits = pshadowBits;
} else {
pshadowBits = 0;
}
if ( pshadowBits ) {
//tr.pc.c_dlightSurfaces++;
}
return pshadowBits;
}
/*
======================
R_AddWorldSurface
======================
*/
static void R_AddWorldSurface( msurface_t *surf, int dlightBits, int pshadowBits ) {
// FIXME: bmodel fog?
// try to cull before dlighting or adding
if ( R_CullSurface( surf ) ) {
return;
}
// check for dlighting
if ( dlightBits ) {
dlightBits = R_DlightSurface( surf, dlightBits );
dlightBits = ( dlightBits != 0 );
}
// check for pshadows
/*if ( pshadowBits ) */{
pshadowBits = R_PshadowSurface( surf, pshadowBits);
pshadowBits = ( pshadowBits != 0 );
}
R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits, pshadowBits );
}
/*
=============================================================
BRUSH MODELS
=============================================================
*/
/*
=================
R_AddBrushModelSurfaces
=================
*/
void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) {
bmodel_t *bmodel;
int clip;
model_t *pModel;
int i;
pModel = R_GetModelByHandle( ent->e.hModel );
bmodel = pModel->bmodel;
clip = R_CullLocalBox( bmodel->bounds );
if ( clip == CULL_OUT ) {
return;
}
R_SetupEntityLighting( &tr.refdef, ent );
R_DlightBmodel( bmodel );
for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) {
int surf = bmodel->firstSurface + i;
if (tr.world->surfacesViewCount[surf] != tr.viewCount)
{
tr.world->surfacesViewCount[surf] = tr.viewCount;
R_AddWorldSurface( tr.world->surfaces + surf, tr.currentEntity->needDlights, 0 );
}
}
}
/*
=============================================================
WORLD MODEL
=============================================================
*/
/*
================
R_RecursiveWorldNode
================
*/
static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits, int pshadowBits ) {
do {
int newDlights[2];
unsigned int newPShadows[2];
// if the node wasn't marked as potentially visible, exit
// pvs is skipped for depth shadows
if (!(tr.viewParms.flags & VPF_DEPTHSHADOW) && node->visCounts[tr.visIndex] != tr.visCounts[tr.visIndex]) {
return;
}
// if the bounding volume is outside the frustum, nothing
// inside can be visible OPTIMIZE: don't do this all the way to leafs?
if ( !r_nocull->integer ) {
int r;
if ( planeBits & 1 ) {
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[0]);
if (r == 2) {
return; // culled
}
if ( r == 1 ) {
planeBits &= ~1; // all descendants will also be in front
}
}
if ( planeBits & 2 ) {
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[1]);
if (r == 2) {
return; // culled
}
if ( r == 1 ) {
planeBits &= ~2; // all descendants will also be in front
}
}
if ( planeBits & 4 ) {
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[2]);
if (r == 2) {
return; // culled
}
if ( r == 1 ) {
planeBits &= ~4; // all descendants will also be in front
}
}
if ( planeBits & 8 ) {
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[3]);
if (r == 2) {
return; // culled
}
if ( r == 1 ) {
planeBits &= ~8; // all descendants will also be in front
}
}
if ( planeBits & 16 ) {
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[4]);
if (r == 2) {
return; // culled
}
if ( r == 1 ) {
planeBits &= ~16; // all descendants will also be in front
}
}
}
if ( node->contents != -1 ) {
break;
}
// node is just a decision point, so go down both sides
// since we don't care about sort orders, just go positive to negative
// determine which dlights are needed
newDlights[0] = 0;
newDlights[1] = 0;
if ( dlightBits ) {
int i;
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
dlight_t *dl;
float dist;
if ( dlightBits & ( 1 << i ) ) {
dl = &tr.refdef.dlights[i];
dist = DotProduct( dl->origin, node->plane->normal ) - node->plane->dist;
if ( dist > -dl->radius ) {
newDlights[0] |= ( 1 << i );
}
if ( dist < dl->radius ) {
newDlights[1] |= ( 1 << i );
}
}
}
}
newPShadows[0] = 0;
newPShadows[1] = 0;
if ( pshadowBits ) {
int i;
for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) {
pshadow_t *shadow;
float dist;
if ( pshadowBits & ( 1 << i ) ) {
shadow = &tr.refdef.pshadows[i];
dist = DotProduct( shadow->lightOrigin, node->plane->normal ) - node->plane->dist;
if ( dist > -shadow->lightRadius ) {
newPShadows[0] |= ( 1 << i );
}
if ( dist < shadow->lightRadius ) {
newPShadows[1] |= ( 1 << i );
}
}
}
}
// recurse down the children, front side first
R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0], newPShadows[0] );
// tail recurse
node = node->children[1];
dlightBits = newDlights[1];
pshadowBits = newPShadows[1];
} while ( 1 );
{
// leaf node, so add mark surfaces
int c;
int surf, *view;
tr.pc.c_leafs++;
// add to z buffer bounds
if ( node->mins[0] < tr.viewParms.visBounds[0][0] ) {
tr.viewParms.visBounds[0][0] = node->mins[0];
}
if ( node->mins[1] < tr.viewParms.visBounds[0][1] ) {
tr.viewParms.visBounds[0][1] = node->mins[1];
}
if ( node->mins[2] < tr.viewParms.visBounds[0][2] ) {
tr.viewParms.visBounds[0][2] = node->mins[2];
}
if ( node->maxs[0] > tr.viewParms.visBounds[1][0] ) {
tr.viewParms.visBounds[1][0] = node->maxs[0];
}
if ( node->maxs[1] > tr.viewParms.visBounds[1][1] ) {
tr.viewParms.visBounds[1][1] = node->maxs[1];
}
if ( node->maxs[2] > tr.viewParms.visBounds[1][2] ) {
tr.viewParms.visBounds[1][2] = node->maxs[2];
}
// add merged and unmerged surfaces
if (tr.world->viewSurfaces)
view = tr.world->viewSurfaces + node->firstmarksurface;
else
view = tr.world->marksurfaces + node->firstmarksurface;
c = node->nummarksurfaces;
while (c--) {
// just mark it as visible, so we don't jump out of the cache derefencing the surface
surf = *view;
if (surf < 0)
{
if (tr.world->mergedSurfacesViewCount[-surf - 1] != tr.viewCount)
{
tr.world->mergedSurfacesViewCount[-surf - 1] = tr.viewCount;
tr.world->mergedSurfacesDlightBits[-surf - 1] = dlightBits;
tr.world->mergedSurfacesPshadowBits[-surf - 1] = pshadowBits;
}
else
{
tr.world->mergedSurfacesDlightBits[-surf - 1] |= dlightBits;
tr.world->mergedSurfacesPshadowBits[-surf - 1] |= pshadowBits;
}
}
else
{
if (tr.world->surfacesViewCount[surf] != tr.viewCount)
{
tr.world->surfacesViewCount[surf] = tr.viewCount;
tr.world->surfacesDlightBits[surf] = dlightBits;
tr.world->surfacesPshadowBits[surf] = pshadowBits;
}
else
{
tr.world->surfacesDlightBits[surf] |= dlightBits;
tr.world->surfacesPshadowBits[surf] |= pshadowBits;
}
}
view++;
}
}
}
/*
===============
R_PointInLeaf
===============
*/
static mnode_t *R_PointInLeaf( const vec3_t p ) {
mnode_t *node;
float d;
cplane_t *plane;
if ( !tr.world ) {
ri.Error (ERR_DROP, "R_PointInLeaf: bad model");
}
node = tr.world->nodes;
while( 1 ) {
if (node->contents != -1) {
break;
}
plane = node->plane;
d = DotProduct (p,plane->normal) - plane->dist;
if (d > 0) {
node = node->children[0];
} else {
node = node->children[1];
}
}
return node;
}
/*
==============
R_ClusterPVS
==============
*/
static const byte *R_ClusterPVS (int cluster) {
if (!tr.world->vis || cluster < 0 || cluster >= tr.world->numClusters ) {
return tr.world->novis;
}
return tr.world->vis + cluster * tr.world->clusterBytes;
}
/*
=================
R_inPVS
=================
*/
qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ) {
mnode_t *leaf;
byte *vis;
leaf = R_PointInLeaf( p1 );
vis = ri.CM_ClusterPVS( leaf->cluster ); // why not R_ClusterPVS ??
leaf = R_PointInLeaf( p2 );
if ( !(vis[leaf->cluster>>3] & (1<<(leaf->cluster&7))) ) {
return qfalse;
}
return qtrue;
}
/*
===============
R_MarkLeaves
Mark the leaves and nodes that are in the PVS for the current
cluster
===============
*/
static void R_MarkLeaves (void) {
const byte *vis;
mnode_t *leaf, *parent;
int i;
int cluster;
// lockpvs lets designers walk around to determine the
// extent of the current pvs
if ( r_lockpvs->integer ) {
return;
}
// current viewcluster
leaf = R_PointInLeaf( tr.viewParms.pvsOrigin );
cluster = leaf->cluster;
// if the cluster is the same and the area visibility matrix
// hasn't changed, we don't need to mark everything again
for(i = 0; i < MAX_VISCOUNTS; i++)
{
if(tr.visClusters[i] == cluster)
{
//tr.visIndex = i;
break;
}
}
// if r_showcluster was just turned on, remark everything
if(i != MAX_VISCOUNTS && !tr.refdef.areamaskModified && !r_showcluster->modified)// && !r_dynamicBspOcclusionCulling->modified)
{
if(tr.visClusters[i] != tr.visClusters[tr.visIndex] && r_showcluster->integer)
{
ri.Printf(PRINT_ALL, "found cluster:%i area:%i index:%i\n", cluster, leaf->area, i);
}
tr.visIndex = i;
return;
}
// if the areamask was modified, invalidate all visclusters
// this caused doors to open into undrawn areas
if (tr.refdef.areamaskModified)
{
memset(tr.visClusters, -2, sizeof(tr.visClusters));
}
tr.visIndex = (tr.visIndex + 1) % MAX_VISCOUNTS;
tr.visCounts[tr.visIndex]++;
tr.visClusters[tr.visIndex] = cluster;
if ( r_showcluster->modified || r_showcluster->integer ) {
r_showcluster->modified = qfalse;
if ( r_showcluster->integer ) {
ri.Printf( PRINT_ALL, "cluster:%i area:%i\n", cluster, leaf->area );
}
}
// set all nodes to visible if there is no vis
// this caused some levels to simply not render
if (r_novis->integer || !tr.world->vis || tr.visClusters[tr.visIndex] == -1) {
for (i=0 ; i<tr.world->numnodes ; i++) {
if (tr.world->nodes[i].contents != CONTENTS_SOLID) {
tr.world->nodes[i].visCounts[tr.visIndex] = tr.visCounts[tr.visIndex];
}
}
return;
}
vis = R_ClusterPVS(tr.visClusters[tr.visIndex]);
for (i=0,leaf=tr.world->nodes ; i<tr.world->numnodes ; i++, leaf++) {
cluster = leaf->cluster;
if ( cluster < 0 || cluster >= tr.world->numClusters ) {
continue;
}
// check general pvs
if ( !(vis[cluster>>3] & (1<<(cluster&7))) ) {
continue;
}
// check for door connection
if ( (tr.refdef.areamask[leaf->area>>3] & (1<<(leaf->area&7)) ) ) {
continue; // not visible
}
parent = leaf;
do {
if(parent->visCounts[tr.visIndex] == tr.visCounts[tr.visIndex])
break;
parent->visCounts[tr.visIndex] = tr.visCounts[tr.visIndex];
parent = parent->parent;
} while (parent);
}
}
/*
=============
R_AddWorldSurfaces
=============
*/
void R_AddWorldSurfaces (void) {
int planeBits, dlightBits, pshadowBits;
if ( !r_drawworld->integer ) {
return;
}
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
return;
}
tr.currentEntityNum = REFENTITYNUM_WORLD;
tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT;
// determine which leaves are in the PVS / areamask
if (!(tr.viewParms.flags & VPF_DEPTHSHADOW))
R_MarkLeaves ();
// clear out the visible min/max
ClearBounds( tr.viewParms.visBounds[0], tr.viewParms.visBounds[1] );
// perform frustum culling and flag all the potentially visible surfaces
if ( tr.refdef.num_dlights > 32 ) {
tr.refdef.num_dlights = 32 ;
}
if ( tr.refdef.num_pshadows > 32 ) {
tr.refdef.num_pshadows = 32 ;
}
planeBits = (tr.viewParms.flags & VPF_FARPLANEFRUSTUM) ? 31 : 15;
if ( tr.viewParms.flags & VPF_DEPTHSHADOW )
{
dlightBits = 0;
pshadowBits = 0;
}
else if ( !(tr.viewParms.flags & VPF_SHADOWMAP) )
{
dlightBits = ( 1 << tr.refdef.num_dlights ) - 1;
pshadowBits = ( 1 << tr.refdef.num_pshadows ) - 1;
}
else
{
dlightBits = ( 1 << tr.refdef.num_dlights ) - 1;
pshadowBits = 0;
}
R_RecursiveWorldNode( tr.world->nodes, planeBits, dlightBits, pshadowBits);
// now add all the potentially visible surfaces
// also mask invisible dlights for next frame
{
int i;
tr.refdef.dlightMask = 0;
for (i = 0; i < tr.world->numWorldSurfaces; i++)
{
if (tr.world->surfacesViewCount[i] != tr.viewCount)
continue;
R_AddWorldSurface( tr.world->surfaces + i, tr.world->surfacesDlightBits[i], tr.world->surfacesPshadowBits[i] );
tr.refdef.dlightMask |= tr.world->surfacesDlightBits[i];
}
for (i = 0; i < tr.world->numMergedSurfaces; i++)
{
if (tr.world->mergedSurfacesViewCount[i] != tr.viewCount)
continue;
R_AddWorldSurface( tr.world->mergedSurfaces + i, tr.world->mergedSurfacesDlightBits[i], tr.world->mergedSurfacesPshadowBits[i] );
tr.refdef.dlightMask |= tr.world->mergedSurfacesDlightBits[i];
}
tr.refdef.dlightMask = ~tr.refdef.dlightMask;
}
}