raze-gles/source/build/src/polymer.cpp
pogokeen 1df7127609 Support software renderer upscaling engine-side in sdlayer and sdlayer12 with various scaling factors (beyond just pixel doubling).
When glsurface is available, use GL to upscale the render buffer.
Additionally, fix build issues with sdlayer12 introduced by GLAD changes (r6656).

git-svn-id: https://svn.eduke32.com/eduke32@6939 1a8010ca-5511-0410-912e-c29ae57300e0
2018-07-14 21:36:44 +00:00

6336 lines
198 KiB
C++

// blah
#if defined USE_OPENGL && defined POLYMER
#include "compat.h"
#define POLYMER_C
#include "polymer.h"
#include "engine_priv.h"
#include "xxhash.h"
#include "texcache.h"
// CVARS
int32_t pr_lighting = 1;
int32_t pr_normalmapping = 1;
int32_t pr_specularmapping = 1;
int32_t pr_shadows = 1;
int32_t pr_shadowcount = 5;
int32_t pr_shadowdetail = 4;
int32_t pr_shadowfiltering = 1;
int32_t pr_maxlightpasses = 10;
int32_t pr_maxlightpriority = PR_MAXLIGHTPRIORITY;
int32_t pr_fov = 426; // appears to be the classic setting.
double pr_customaspect = 0.0f;
int32_t pr_billboardingmode = 1;
int32_t pr_verbosity = 1; // 0: silent, 1: errors and one-times, 2: multiple-times, 3: flood
int32_t pr_wireframe = 0;
int32_t pr_vbos = 2;
int32_t pr_buckets = 0;
int32_t pr_gpusmoothing = 1;
int32_t pr_overrideparallax = 0;
float pr_parallaxscale = 0.1f;
float pr_parallaxbias = 0.0f;
int32_t pr_overridespecular = 0;
float pr_specularpower = 15.0f;
float pr_specularfactor = 1.0f;
int32_t pr_highpalookups = 1;
int32_t pr_artmapping = 1;
int32_t pr_overridehud = 0;
float pr_hudxadd = 0.0f;
float pr_hudyadd = 0.0f;
float pr_hudzadd = 0.0f;
int32_t pr_hudangadd = 0;
int32_t pr_hudfov = 426;
float pr_overridemodelscale = 0.0f;
int32_t pr_ati_fboworkaround = 0;
int32_t pr_ati_nodepthoffset = 0;
#ifdef __APPLE__
int32_t pr_ati_textureformat_one = 0;
#endif
int32_t pr_nullrender = 0; // 1: no draw, 2: no draw or updates
int32_t r_pr_maxlightpasses = 5; // value of the cvar (not live value), used to detect changes
GLenum mapvbousage = GL_STREAM_DRAW;
GLenum modelvbousage = GL_STATIC_DRAW;
// BUILD DATA
_prsector *prsectors[MAXSECTORS];
_prwall *prwalls[MAXWALLS];
_prsprite *prsprites[MAXSPRITES];
_prmaterial mdspritematerial;
_prhighpalookup prhighpalookups[MAXBASEPALS][MAXPALOOKUPS];
// One U8 texture per tile
GLuint prartmaps[MAXTILES];
// 256 U8U8U8 values per basepal
GLuint prbasepalmaps[MAXBASEPALS];
// numshades full indirections (32*256) per lookup
GLuint prlookups[MAXPALOOKUPS];
GLuint prmapvbo;
const GLsizeiptr proneplanesize = sizeof(_prvert) * 4;
const GLintptr prwalldatasize = sizeof(_prvert)* 4 * 3; // wall, over and mask planes for every wall
GLintptr prwalldataoffset;
GLuint prindexringvbo;
GLuint *prindexring;
const GLsizeiptr prindexringsize = 65535;
GLintptr prindexringoffset;
const GLbitfield prindexringmapflags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
_prbucket *prbuckethead;
int32_t prcanbucket;
static const _prvert vertsprite[4] =
{
{
-0.5f, 0.0f, 0.0f,
0.0f, 1.0f,
0xff, 0xff, 0xff, 0xff,
},
{
0.5f, 0.0f, 0.0f,
1.0f, 1.0f,
0xff, 0xff, 0xff, 0xff,
},
{
0.5f, 1.0f, 0.0f,
1.0f, 0.0f,
0xff, 0xff, 0xff, 0xff,
},
{
-0.5f, 1.0f, 0.0f,
0.0f, 0.0f,
0xff, 0xff, 0xff, 0xff,
},
};
static const _prvert horizsprite[4] =
{
{
-0.5f, 0.0f, 0.5f,
0.0f, 0.0f,
0xff, 0xff, 0xff, 0xff,
},
{
0.5f, 0.0f, 0.5f,
1.0f, 0.0f,
0xff, 0xff, 0xff, 0xff,
},
{
0.5f, 0.0f, -0.5f,
1.0f, 1.0f,
0xff, 0xff, 0xff, 0xff,
},
{
-0.5f, 0.0f, -0.5f,
0.0f, 1.0f,
0xff, 0xff, 0xff, 0xff,
},
};
static const GLfloat skyboxdata[4 * 5 * 6] =
{
// -ZY
-0.5f, -0.5f, 0.5f,
0.0f, 1.0f,
-0.5f, -0.5f, -0.5f,
1.0f, 1.0f,
-0.5f, 0.5f, -0.5f,
1.0f, 0.0f,
-0.5f, 0.5f, 0.5f,
0.0f, 0.0f,
// XY
-0.5f, -0.5f, -0.5f,
0.0f, 1.0f,
0.5f, -0.5f, -0.5f,
1.0f, 1.0f,
0.5f, 0.5f, -0.5f,
1.0f, 0.0f,
-0.5f, 0.5f, -0.5f,
0.0f, 0.0f,
// ZY
0.5f, -0.5f, -0.5f,
0.0f, 1.0f,
0.5f, -0.5f, 0.5f,
1.0f, 1.0f,
0.5f, 0.5f, 0.5f,
1.0f, 0.0f,
0.5f, 0.5f, -0.5f,
0.0f, 0.0f,
// -XY
0.5f, -0.5f, 0.5f,
0.0f, 1.0f,
-0.5f, -0.5f, 0.5f,
1.0f, 1.0f,
-0.5f, 0.5f, 0.5f,
1.0f, 0.0f,
0.5f, 0.5f, 0.5f,
0.0f, 0.0f,
// XZ
-0.5f, 0.5f, -0.5f,
1.0f, 1.0f,
0.5f, 0.5f, -0.5f,
1.0f, 0.0f,
0.5f, 0.5f, 0.5f,
0.0f, 0.0f,
-0.5f, 0.5f, 0.5f,
0.0f, 1.0f,
// X-Z
-0.5f, -0.5f, 0.5f,
0.0f, 0.0f,
0.5f, -0.5f, 0.5f,
0.0f, 1.0f,
0.5f, -0.5f, -0.5f,
1.0f, 1.0f,
-0.5f, -0.5f, -0.5f,
1.0f, 0.0f,
};
GLuint skyboxdatavbo;
GLfloat artskydata[16];
// LIGHTS
static _prplanelist *plpool;
#pragma pack(push,1)
_prlight prlights[PR_MAXLIGHTS];
int32_t lightcount;
int32_t curlight;
#pragma pack(pop)
static const GLfloat shadowBias[] =
{
0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0
};
// MATERIALS
static const _prprogrambit prprogrambits[PR_BIT_COUNT] = {
{
1 << PR_BIT_HEADER,
// vert_def
"#version 120\n"
"#extension GL_ARB_texture_rectangle : enable\n"
"\n",
// vert_prog
"",
// frag_def
"#version 120\n"
"#extension GL_ARB_texture_rectangle : enable\n"
"\n",
// frag_prog
"",
},
{
1 << PR_BIT_ANIM_INTERPOLATION,
// vert_def
"attribute vec4 nextFrameData;\n"
"attribute vec4 nextFrameNormal;\n"
"uniform float frameProgress;\n"
"\n",
// vert_prog
" vec4 currentFramePosition;\n"
" vec4 nextFramePosition;\n"
"\n"
" currentFramePosition = curVertex * (1.0 - frameProgress);\n"
" nextFramePosition = nextFrameData * frameProgress;\n"
" curVertex = currentFramePosition + nextFramePosition;\n"
"\n"
" currentFramePosition = vec4(curNormal, 1.0) * (1.0 - frameProgress);\n"
" nextFramePosition = nextFrameNormal * frameProgress;\n"
" curNormal = vec3(currentFramePosition + nextFramePosition);\n"
"\n",
// frag_def
"",
// frag_prog
"",
},
{
1 << PR_BIT_LIGHTING_PASS,
// vert_def
"",
// vert_prog
"",
// frag_def
"",
// frag_prog
" isLightingPass = 1;\n"
" result = vec4(0.0, 0.0, 0.0, 1.0);\n"
"\n",
},
{
1 << PR_BIT_NORMAL_MAP,
// vert_def
"attribute vec3 T;\n"
"attribute vec3 B;\n"
"attribute vec3 N;\n"
"uniform vec3 eyePosition;\n"
"varying vec3 tangentSpaceEyeVec;\n"
"\n",
// vert_prog
" TBN = mat3(T, B, N);\n"
" tangentSpaceEyeVec = eyePosition - vec3(curVertex);\n"
" tangentSpaceEyeVec = TBN * tangentSpaceEyeVec;\n"
"\n"
" isNormalMapped = 1;\n"
"\n",
// frag_def
"uniform sampler2D normalMap;\n"
"uniform vec2 normalBias;\n"
"varying vec3 tangentSpaceEyeVec;\n"
"\n",
// frag_prog
" vec4 normalStep;\n"
" float biasedHeight;\n"
"\n"
" eyeVec = normalize(tangentSpaceEyeVec);\n"
"\n"
" for (int i = 0; i < 4; i++) {\n"
" normalStep = texture2D(normalMap, commonTexCoord.st);\n"
" biasedHeight = normalStep.a * normalBias.x - normalBias.y;\n"
" commonTexCoord += (biasedHeight - commonTexCoord.z) * normalStep.z * eyeVec;\n"
" }\n"
"\n"
" normalTexel = texture2D(normalMap, commonTexCoord.st);\n"
"\n"
" isNormalMapped = 1;\n"
"\n",
},
{
1 << PR_BIT_ART_MAP,
// vert_def
"varying vec3 horizDistance;\n"
"\n",
// vert_prog
" gl_TexCoord[0] = gl_MultiTexCoord0;\n"
" horizDistance = vec3(gl_ModelViewMatrix * curVertex);\n"
"\n",
// frag_def
"uniform sampler2D artMap;\n"
"uniform sampler2D basePalMap;\n"
"uniform sampler2DRect lookupMap;\n"
"uniform float shadeOffset;\n"
"uniform float visibility;\n"
"varying vec3 horizDistance;\n"
"\n",
// frag_prog
" float shadeLookup = length(horizDistance) / 1.024 * visibility;\n"
" shadeLookup = shadeLookup + shadeOffset;\n"
"\n"
" float colorIndex = texture2D(artMap, commonTexCoord.st).r * 256.0;\n"
" float colorIndexNear = texture2DRect(lookupMap, vec2(colorIndex, floor(shadeLookup))).r;\n"
" float colorIndexFar = texture2DRect(lookupMap, vec2(colorIndex, floor(shadeLookup + 1.0))).r;\n"
" float colorIndexFullbright = texture2DRect(lookupMap, vec2(colorIndex, 0.0)).r;\n"
"\n"
" vec3 texelNear = texture2D(basePalMap, vec2(colorIndexNear, 0.5)).rgb;\n"
" vec3 texelFar = texture2D(basePalMap, vec2(colorIndexFar, 0.5)).rgb;\n"
" diffuseTexel.rgb = texture2D(basePalMap, vec2(colorIndexFullbright, 0.5)).rgb;\n"
"\n"
" if (isLightingPass == 0) {\n"
" result.rgb = mix(texelNear, texelFar, fract(shadeLookup));\n"
" result.a = 1.0;\n"
" if (colorIndex == 256.0)\n"
" result.a = 0.0;\n"
" }\n"
"\n",
},
{
1 << PR_BIT_DIFFUSE_MAP,
// vert_def
"uniform vec2 diffuseScale;\n"
"\n",
// vert_prog
" gl_TexCoord[0] = vec4(diffuseScale, 1.0, 1.0) * gl_MultiTexCoord0;\n"
"\n",
// frag_def
"uniform sampler2D diffuseMap;\n"
"\n",
// frag_prog
" diffuseTexel = texture2D(diffuseMap, commonTexCoord.st);\n"
"\n",
},
{
1 << PR_BIT_DIFFUSE_DETAIL_MAP,
// vert_def
"uniform vec2 detailScale;\n"
"varying vec2 fragDetailScale;\n"
"\n",
// vert_prog
" fragDetailScale = detailScale;\n"
" if (isNormalMapped == 0)\n"
" gl_TexCoord[1] = vec4(detailScale, 1.0, 1.0) * gl_MultiTexCoord0;\n"
"\n",
// frag_def
"uniform sampler2D detailMap;\n"
"varying vec2 fragDetailScale;\n"
"\n",
// frag_prog
" if (isNormalMapped == 0)\n"
" diffuseTexel *= texture2D(detailMap, gl_TexCoord[1].st);\n"
" else\n"
" diffuseTexel *= texture2D(detailMap, commonTexCoord.st * fragDetailScale);\n"
" diffuseTexel.rgb *= 2.0;\n"
"\n",
},
{
1 << PR_BIT_DIFFUSE_MODULATION,
// vert_def
"",
// vert_prog
" gl_FrontColor = gl_Color;\n"
"\n",
// frag_def
"",
// frag_prog
" if (isLightingPass == 0)\n"
" result *= vec4(gl_Color);\n"
"\n",
},
{
1 << PR_BIT_DIFFUSE_MAP2,
// vert_def
"",
// vert_prog
"",
// frag_def
"",
// frag_prog
" if (isLightingPass == 0)\n"
" result *= diffuseTexel;\n"
"\n",
},
{
1 << PR_BIT_HIGHPALOOKUP_MAP,
// vert_def
"",
// vert_prog
"",
// frag_def
"uniform sampler3D highPalookupMap;\n"
"\n",
// frag_prog
" float highPalScale = 0.9921875; // for 6 bits\n"
" float highPalBias = 0.00390625;\n"
"\n"
" if (isLightingPass == 0)\n"
" result.rgb = texture3D(highPalookupMap, result.rgb * highPalScale + highPalBias).rgb;\n"
" diffuseTexel.rgb = texture3D(highPalookupMap, diffuseTexel.rgb * highPalScale + highPalBias).rgb;\n"
"\n",
},
{
1 << PR_BIT_SPECULAR_MAP,
// vert_def
"",
// vert_prog
"",
// frag_def
"uniform sampler2D specMap;\n"
"\n",
// frag_prog
" specTexel = texture2D(specMap, commonTexCoord.st);\n"
"\n"
" isSpecularMapped = 1;\n"
"\n",
},
{
1 << PR_BIT_SPECULAR_MATERIAL,
// vert_def
"",
// vert_prog
"",
// frag_def
"uniform vec2 specMaterial;\n"
"\n",
// frag_prog
" specularMaterial = specMaterial;\n"
"\n",
},
{
1 << PR_BIT_MIRROR_MAP,
// vert_def
"",
// vert_prog
"",
// frag_def
"uniform sampler2DRect mirrorMap;\n"
"\n",
// frag_prog
" vec4 mirrorTexel;\n"
" vec2 mirrorCoords;\n"
"\n"
" mirrorCoords = gl_FragCoord.st;\n"
" if (isNormalMapped == 1) {\n"
" mirrorCoords += 100.0 * (normalTexel.rg - 0.5);\n"
" }\n"
" mirrorTexel = texture2DRect(mirrorMap, mirrorCoords);\n"
" result = vec4((result.rgb * (1.0 - specTexel.a)) + (mirrorTexel.rgb * specTexel.rgb * specTexel.a), result.a);\n"
"\n",
},
{
1 << PR_BIT_FOG,
// vert_def
"",
// vert_prog
"",
// frag_def
#ifdef PR_LINEAR_FOG
"uniform bool linearFog;\n"
#endif
"",
// frag_prog
" float fragDepth;\n"
" float fogFactor;\n"
"\n"
" fragDepth = gl_FragCoord.z / gl_FragCoord.w;\n"
#ifdef PR_LINEAR_FOG
" if (!linearFog) {\n"
#endif
" fragDepth *= fragDepth;\n"
" fogFactor = exp2(-gl_Fog.density * gl_Fog.density * fragDepth * 1.442695);\n"
#ifdef PR_LINEAR_FOG
" } else {\n"
" fogFactor = gl_Fog.scale * (gl_Fog.end - fragDepth);\n"
" fogFactor = clamp(fogFactor, 0.0, 1.0);"
" }\n"
#endif
" result.rgb = mix(gl_Fog.color.rgb, result.rgb, fogFactor);\n"
"\n",
},
{
1 << PR_BIT_GLOW_MAP,
// vert_def
"",
// vert_prog
"",
// frag_def
"uniform sampler2D glowMap;\n"
"\n",
// frag_prog
" vec4 glowTexel;\n"
"\n"
" glowTexel = texture2D(glowMap, commonTexCoord.st);\n"
" result = vec4((result.rgb * (1.0 - glowTexel.a)) + (glowTexel.rgb * glowTexel.a), result.a);\n"
"\n",
},
{
1 << PR_BIT_PROJECTION_MAP,
// vert_def
"uniform mat4 shadowProjMatrix;\n"
"\n",
// vert_prog
" gl_TexCoord[2] = shadowProjMatrix * curVertex;\n"
"\n",
// frag_def
"",
// frag_prog
"",
},
{
1 << PR_BIT_SHADOW_MAP,
// vert_def
"",
// vert_prog
"",
// frag_def
"uniform sampler2DShadow shadowMap;\n"
"\n",
// frag_prog
" shadowResult = shadow2DProj(shadowMap, gl_TexCoord[2]).a;\n"
"\n",
},
{
1 << PR_BIT_LIGHT_MAP,
// vert_def
"",
// vert_prog
"",
// frag_def
"uniform sampler2D lightMap;\n"
"\n",
// frag_prog
" lightTexel = texture2D(lightMap, vec2(gl_TexCoord[2].s, -gl_TexCoord[2].t) / gl_TexCoord[2].q).rgb;\n"
"\n",
},
{
1 << PR_BIT_SPOT_LIGHT,
// vert_def
"",
// vert_prog
"",
// frag_def
"uniform vec3 spotDir;\n"
"uniform vec2 spotRadius;\n"
"\n",
// frag_prog
" spotVector = spotDir;\n"
" spotCosRadius = spotRadius;\n"
" isSpotLight = 1;\n"
"\n",
},
{
1 << PR_BIT_POINT_LIGHT,
// vert_def
"varying vec3 vertexNormal;\n"
"varying vec3 eyeVector;\n"
"varying vec3 lightVector;\n"
"varying vec3 tangentSpaceLightVector;\n"
"\n",
// vert_prog
" vec3 vertexPos;\n"
"\n"
" vertexPos = vec3(gl_ModelViewMatrix * curVertex);\n"
" eyeVector = -vertexPos;\n"
" lightVector = gl_LightSource[0].ambient.rgb - vertexPos;\n"
"\n"
" if (isNormalMapped == 1) {\n"
" tangentSpaceLightVector = gl_LightSource[0].specular.rgb - vec3(curVertex);\n"
" tangentSpaceLightVector = TBN * tangentSpaceLightVector;\n"
" } else\n"
" vertexNormal = normalize(gl_NormalMatrix * curNormal);\n"
"\n",
// frag_def
"varying vec3 vertexNormal;\n"
"varying vec3 eyeVector;\n"
"varying vec3 lightVector;\n"
"varying vec3 tangentSpaceLightVector;\n"
"\n",
// frag_prog
" float pointLightDistance;\n"
" float lightAttenuation;\n"
" float spotAttenuation;\n"
" vec3 N, L, E, R, D;\n"
" vec3 lightDiffuse;\n"
" float lightSpecular;\n"
" float NdotL;\n"
" float spotCosAngle;\n"
"\n"
" L = normalize(lightVector);\n"
"\n"
" pointLightDistance = dot(lightVector,lightVector);\n"
" lightAttenuation = clamp(1.0 - pointLightDistance * gl_LightSource[0].linearAttenuation, 0.0, 1.0);\n"
" spotAttenuation = 1.0;\n"
"\n"
" if (isSpotLight == 1) {\n"
" D = normalize(spotVector);\n"
" spotCosAngle = dot(-L, D);\n"
" spotAttenuation = clamp((spotCosAngle - spotCosRadius.x) * spotCosRadius.y, 0.0, 1.0);\n"
" }\n"
"\n"
" if (isNormalMapped == 1) {\n"
" E = eyeVec;\n"
" N = normalize(2.0 * (normalTexel.rgb - 0.5));\n"
" L = normalize(tangentSpaceLightVector);\n"
" } else {\n"
" E = normalize(eyeVector);\n"
" N = normalize(vertexNormal);\n"
" }\n"
" NdotL = max(dot(N, L), 0.0);\n"
"\n"
" R = reflect(-L, N);\n"
"\n"
" lightDiffuse = gl_Color.a * shadowResult * lightTexel *\n"
" gl_LightSource[0].diffuse.rgb * lightAttenuation * spotAttenuation;\n"
" result += vec4(lightDiffuse * diffuseTexel.a * diffuseTexel.rgb * NdotL, 0.0);\n"
"\n"
" if (isSpecularMapped == 0)\n"
" specTexel.rgb = diffuseTexel.rgb * diffuseTexel.a;\n"
"\n"
" lightSpecular = pow( max(dot(R, E), 0.0), specularMaterial.x * specTexel.a) * specularMaterial.y;\n"
" result += vec4(lightDiffuse * specTexel.rgb * lightSpecular, 0.0);\n"
"\n",
},
{
1 << PR_BIT_FOOTER,
// vert_def
"void main(void)\n"
"{\n"
" vec4 curVertex = gl_Vertex;\n"
" vec3 curNormal = gl_Normal;\n"
" int isNormalMapped = 0;\n"
" mat3 TBN;\n"
"\n"
" gl_TexCoord[0] = gl_MultiTexCoord0;\n"
"\n",
// vert_prog
" gl_Position = gl_ModelViewProjectionMatrix * curVertex;\n"
"}\n",
// frag_def
"void main(void)\n"
"{\n"
" vec3 commonTexCoord = vec3(gl_TexCoord[0].st, 0.0);\n"
" vec4 result = vec4(1.0, 1.0, 1.0, 1.0);\n"
" vec4 diffuseTexel = vec4(1.0, 1.0, 1.0, 1.0);\n"
" vec4 specTexel = vec4(1.0, 1.0, 1.0, 1.0);\n"
" vec4 normalTexel;\n"
" int isLightingPass = 0;\n"
" int isNormalMapped = 0;\n"
" int isSpecularMapped = 0;\n"
" vec3 eyeVec;\n"
" int isSpotLight = 0;\n"
" vec3 spotVector;\n"
" vec2 spotCosRadius;\n"
" float shadowResult = 1.0;\n"
" vec2 specularMaterial = vec2(15.0, 1.0);\n"
" vec3 lightTexel = vec3(1.0, 1.0, 1.0);\n"
"\n",
// frag_prog
" gl_FragColor = result;\n"
"}\n",
}
};
_prprograminfo prprograms[1 << PR_BIT_COUNT];
int32_t overridematerial;
int32_t globaloldoverridematerial;
int32_t rotatespritematerialbits;
// RENDER TARGETS
_prrt *prrts;
// CONTROL
GLfloat spritemodelview[16];
GLfloat mdspritespace[4][4];
GLfloat rootmodelviewmatrix[16];
GLfloat *curmodelviewmatrix;
GLfloat rootskymodelviewmatrix[16];
GLfloat *curskymodelviewmatrix;
static int16_t sectorqueue[MAXSECTORS];
static int16_t querydelay[MAXSECTORS];
static GLuint queryid[MAXWALLS];
static int16_t drawingstate[MAXSECTORS];
int16_t *cursectormasks;
int16_t *cursectormaskcount;
float horizang;
fix16_t viewangle;
int32_t depth;
_prmirror mirrors[10];
#if defined __clang__ && defined __APPLE__
// XXX: OS X 10.9 deprecated GLUtesselator.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
GLUtesselator* prtess;
#if defined __clang__ && defined __APPLE__
#pragma clang diagnostic pop
#endif
static int16_t cursky;
static char curskypal;
static int8_t curskyshade;
static float curskyangmul = 1;
_pranimatespritesinfo asi;
int32_t polymersearching;
int32_t culledface;
// EXTERNAL FUNCTIONS
int32_t polymer_init(void)
{
int32_t i, j, t = timerGetTicks();
if (pr_verbosity >= 1) OSD_Printf("Initializing Polymer subsystem...\n");
if (!glinfo.texnpot ||
!glinfo.depthtex ||
!glinfo.shadow ||
!glinfo.fbos ||
!glinfo.rect ||
!glinfo.multitex ||
!glinfo.vbos ||
!glinfo.occlusionqueries ||
!glinfo.glsl)
{
OSD_Printf("PR : Your video card driver/combo doesn't support the necessary features!\n");
OSD_Printf("PR : Disabling Polymer...\n");
return 0;
}
// clean up existing stuff since it will be initialized again if we're re-entering here
polymer_uninit();
Bmemset(&prsectors[0], 0, sizeof(prsectors[0]) * MAXSECTORS);
Bmemset(&prwalls[0], 0, sizeof(prwalls[0]) * MAXWALLS);
prtess = bgluNewTess();
if (prtess == 0)
{
OSD_Printf("PR : Tessellation object initialization failed!\n");
return 0;
}
polymer_loadboard();
polymer_initartsky();
skyboxdatavbo = 0;
i = 0;
while (i < nextmodelid)
{
if (models[i])
{
md3model_t* m;
m = (md3model_t*)models[i];
m->indices = NULL;
}
i++;
}
i = 0;
while (i < (1 << PR_BIT_COUNT))
{
prprograms[i].handle = 0;
i++;
}
overridematerial = 0xFFFFFFFF;
polymersearching = FALSE;
polymer_initrendertargets(pr_shadowcount + 1);
// Prime highpalookup maps
i = 0;
while (i < MAXBASEPALS)
{
j = 0;
while (j < MAXPALOOKUPS)
{
if (prhighpalookups[i][j].data)
{
glGenTextures(1, &prhighpalookups[i][j].map);
glBindTexture(GL_TEXTURE_3D, prhighpalookups[i][j].map);
glTexImage3D(GL_TEXTURE_3D, // target
0, // mip level
GL_RGBA, // internalFormat
PR_HIGHPALOOKUP_DIM, // width
PR_HIGHPALOOKUP_DIM, // height
PR_HIGHPALOOKUP_DIM, // depth
0, // border
GL_BGRA, // upload format
GL_UNSIGNED_BYTE, // upload component type
prhighpalookups[i][j].data); // data pointer
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP);
glBindTexture(GL_TEXTURE_3D, 0);
}
j++;
}
i++;
}
#ifndef __APPLE__
if (glinfo.debugoutput) {
// Enable everything.
glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
glDebugMessageCallbackARB((GLDEBUGPROCARB)polymer_debugoutputcallback, NULL);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
}
#endif
if (pr_verbosity >= 1) OSD_Printf("PR : Initialization complete in %d ms.\n", timerGetTicks()-t);
return 1;
}
void polymer_uninit(void)
{
int32_t i, j;
if (prtess)
{
bgluDeleteTess(prtess);
prtess = NULL;
}
polymer_freeboard();
polymer_initrendertargets(0);
i = 0;
while (i < MAXBASEPALS)
{
j = 0;
while (j < MAXPALOOKUPS)
{
// if (prhighpalookups[i][j].data) {
// DO_FREE_AND_NULL(prhighpalookups[i][j].data);
// }
if (prhighpalookups[i][j].map) {
glDeleteTextures(1, &prhighpalookups[i][j].map);
prhighpalookups[i][j].map = 0;
}
j++;
}
i++;
}
i = 0;
while (plpool)
{
_prplanelist* next = plpool->n;
Bfree(plpool);
plpool = next;
i++;
}
if (pr_verbosity >= 3)
OSD_Printf("PR: freed %d planelists\n", i);
}
void polymer_setaspect(int32_t ang)
{
float aspect;
float fang = (float)ang * atanf(fviewingrange*(1.f/65536.f)) * (4.f/fPI);
if (pr_customaspect != 0.0f)
aspect = pr_customaspect;
else
aspect = (float)(windowxy2.x-windowxy1.x+1) /
(float)(windowxy2.y-windowxy1.y+1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
bgluPerspective(fang * (360.f/2048.f), aspect, 0.01f, 100.0f);
}
void polymer_glinit(void)
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearStencil(0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glViewport(windowxy1.x, ydim-(windowxy2.y+1),windowxy2.x-windowxy1.x+1, windowxy2.y-windowxy1.y+1);
// texturing
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
if (pr_wireframe)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
else
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
polymer_setaspect(pr_fov);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_FOG);
culledface = GL_BACK;
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
}
void polymer_resetlights(void)
{
int32_t i;
_prsector *s;
_prwall *w;
i = 0;
while (i < numsectors)
{
s = prsectors[i];
if (!s) {
i++;
continue;
}
polymer_resetplanelights(&s->floor);
polymer_resetplanelights(&s->ceil);
i++;
}
i = 0;
while (i < numwalls)
{
w = prwalls[i];
if (!w) {
i++;
continue;
}
polymer_resetplanelights(&w->wall);
polymer_resetplanelights(&w->over);
polymer_resetplanelights(&w->mask);
i++;
}
i = 0;
while (i < PR_MAXLIGHTS)
{
prlights[i].flags.active = 0;
i++;
}
lightcount = 0;
if (!engineLoadMHK(NULL))
OSD_Printf("polymer_resetlights: reloaded maphack\n");
}
void polymer_loadboard(void)
{
int32_t i;
polymer_freeboard();
// in the big map buffer, sectors have floor and ceiling vertices for each wall first, then walls
prwalldataoffset = numwalls * 2 * sizeof(_prvert);
glGenBuffers(1, &prmapvbo);
glBindBuffer(GL_ARRAY_BUFFER, prmapvbo);
glBufferData(GL_ARRAY_BUFFER, prwalldataoffset + (numwalls * prwalldatasize), NULL, mapvbousage);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &prindexringvbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prindexringvbo);
if (pr_buckets)
{
glBufferStorage(GL_ELEMENT_ARRAY_BUFFER, prindexringsize * sizeof(GLuint), NULL, prindexringmapflags | GL_DYNAMIC_STORAGE_BIT);
prindexring = (GLuint*)glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, prindexringsize * sizeof(GLuint), prindexringmapflags);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
i = 0;
while (i < numsectors)
{
polymer_initsector(i);
polymer_updatesector(i);
i++;
}
i = 0;
while (i < numwalls)
{
polymer_initwall(i);
polymer_updatewall(i);
i++;
}
polymer_getsky();
polymer_resetlights();
if (pr_verbosity >= 1 && numsectors) OSD_Printf("PR : Board loaded.\n");
}
// The parallaxed ART sky angle divisor corresponding to a horizfrac of 32768.
#define DEFAULT_ARTSKY_ANGDIV 4.3027f
void polymer_drawrooms(int32_t daposx, int32_t daposy, int32_t daposz, fix16_t daang, fix16_t dahoriz, int16_t dacursectnum)
{
int16_t cursectnum;
int32_t i, cursectflorz, cursectceilz;
float skyhoriz, ang, tiltang;
float pos[3];
pthtyp* pth;
if (videoGetRenderMode() == REND_CLASSIC) return;
videoBeginDrawing();
// TODO: support for screen resizing
// frameoffset = frameplace + windowxy1.y*bytesperline + windowxy1.x;
if (pr_verbosity >= 3) OSD_Printf("PR : Drawing rooms...\n");
// fogcalc_old needs this
gvisibility = ((float)globalvisibility)*FOGSCALE;
ang = fix16_to_float(daang) * (360.f/2048.f);
horizang = -(float)atan2(fix16_to_float(dahoriz)-100.f, 128.f) * (180.f*(float)M_1_PI);
tiltang = (gtang * 90.0f);
pos[0] = (float)daposy;
pos[1] = -(float)(daposz) / 16.0f;
pos[2] = -(float)daposx;
polymer_updatelights();
// polymer_resetlights();
// if (pr_lighting)
// polymer_applylights();
depth = 0;
if (pr_shadows && lightcount && (pr_shadowcount > 0))
polymer_prepareshadows();
// hack for parallax skies
skyhoriz = horizang;
if (skyhoriz < -180.0f)
skyhoriz += 360.0f;
drawingskybox = 1;
pth = texcache_fetch(cursky, 0, 0, DAMETH_NOMASK);
drawingskybox = 0;
// if it's not a skybox, make the sky parallax
// DEFAULT_ARTSKY_ANGDIV is computed from eyeballed values
// need to recompute it if we ever change the max horiz amplitude
if (!pth || !(pth->flags & PTH_SKYBOX))
skyhoriz *= curskyangmul;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(tiltang, 0.0f, 0.0f, -1.0f);
glRotatef(skyhoriz, 1.0f, 0.0f, 0.0f);
glRotatef(ang, 0.0f, 1.0f, 0.0f);
glScalef(1.0f / 1000.0f, 1.0f / 1000.0f, 1.0f / 1000.0f);
glTranslatef(-pos[0], -pos[1], -pos[2]);
glGetFloatv(GL_MODELVIEW_MATRIX, rootskymodelviewmatrix);
curskymodelviewmatrix = rootskymodelviewmatrix;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(tiltang, 0.0f, 0.0f, -1.0f);
glRotatef(horizang, 1.0f, 0.0f, 0.0f);
glRotatef(ang, 0.0f, 1.0f, 0.0f);
glScalef(1.0f / 1000.0f, 1.0f / 1000.0f, 1.0f / 1000.0f);
glTranslatef(-pos[0], -pos[1], -pos[2]);
glGetFloatv(GL_MODELVIEW_MATRIX, rootmodelviewmatrix);
cursectnum = dacursectnum;
updatesectorbreadth(daposx, daposy, &cursectnum);
if (cursectnum >= 0 && cursectnum < numsectors)
dacursectnum = cursectnum;
else if (pr_verbosity>=2)
OSD_Printf("PR : got sector %d after update!\n", cursectnum);
// unflag all sectors
i = numsectors-1;
while (i >= 0)
{
prsectors[i]->flags.uptodate = 0;
prsectors[i]->wallsproffset = 0.0f;
prsectors[i]->floorsproffset = 0.0f;
i--;
}
i = numwalls-1;
while (i >= 0)
{
prwalls[i]->flags.uptodate = 0;
i--;
}
if (searchit == 2 && !polymersearching)
{
globaloldoverridematerial = overridematerial;
overridematerial = prprogrambits[PR_BIT_DIFFUSE_MODULATION].bit;
overridematerial |= prprogrambits[PR_BIT_DIFFUSE_MAP2].bit;
polymersearching = TRUE;
}
if (!searchit && polymersearching) {
overridematerial = globaloldoverridematerial;
polymersearching = FALSE;
}
if (dacursectnum > -1 && dacursectnum < numsectors)
getzsofslope(dacursectnum, daposx, daposy, &cursectceilz, &cursectflorz);
// external view (editor)
if ((dacursectnum < 0) || (dacursectnum >= numsectors) ||
(daposz > cursectflorz) ||
(daposz < cursectceilz))
{
prcanbucket = 1;
if (!editstatus && pr_verbosity>=1)
{
if ((unsigned)dacursectnum < (unsigned)numsectors)
OSD_Printf("PR : EXT sec=%d z=%d (%d, %d)\n", dacursectnum, daposz, cursectflorz, cursectceilz);
else
OSD_Printf("PR : EXT sec=%d z=%d\n", dacursectnum, daposz);
}
curmodelviewmatrix = rootmodelviewmatrix;
i = numsectors-1;
while (i >= 0)
{
polymer_updatesector(i);
polymer_drawsector(i, FALSE);
polymer_scansprites(i, tsprite, &spritesortcnt);
i--;
}
i = numwalls-1;
while (i >= 0)
{
polymer_updatewall(i);
polymer_drawwall(sectorofwall(i), i);
i--;
}
polymer_emptybuckets();
viewangle = daang;
videoEndDrawing();
return;
}
// GO!
polymer_displayrooms(dacursectnum);
curmodelviewmatrix = rootmodelviewmatrix;
// build globals used by rotatesprite
viewangle = daang;
set_globalang(daang);
// polymost globals used by polymost_dorotatesprite
gcosang = fcosglobalang*(1./262144.f);
gsinang = fsinglobalang*(1./262144.f);
gcosang2 = gcosang*fviewingrange*(1./65536.f);
gsinang2 = gsinang*fviewingrange*(1./65536.f);
if (pr_verbosity >= 3) OSD_Printf("PR : Rooms drawn.\n");
videoEndDrawing();
}
void polymer_drawmasks(void)
{
glEnable(GL_ALPHA_TEST);
glEnable(GL_BLEND);
// glEnable(GL_POLYGON_OFFSET_FILL);
// while (--spritesortcnt)
// {
// tspriteptr[spritesortcnt] = &tsprite[spritesortcnt];
// polymer_drawsprite(spritesortcnt);
// }
glEnable(GL_CULL_FACE);
if (cursectormaskcount) {
// We (kind of) queue sector masks near to far, so drawing them in reverse
// order is the sane approach here. Of course impossible cases will arise.
while (*cursectormaskcount) {
polymer_drawsector(cursectormasks[--(*cursectormaskcount)], TRUE);
}
// This should _always_ be called after a corresponding pr_displayrooms()
// unless we're in "external view" mode, which was checked above.
// Both the top-level game drawrooms and the recursive internal passes
// should be accounted for here. If these free cause corruption, there's
// an accounting bug somewhere.
DO_FREE_AND_NULL(cursectormaskcount);
DO_FREE_AND_NULL(cursectormasks);
}
glDisable(GL_CULL_FACE);
// glDisable(GL_POLYGON_OFFSET_FILL);
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
}
void polymer_editorpick(void)
{
GLubyte picked[3];
int16_t num;
glReadPixels(searchx, ydim - searchy, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, picked);
num = B_UNBUF16(&picked[1]);
searchstat = picked[0];
switch (searchstat) {
case 0: // wall
case 5: // botomwall
case 4: // 1-way/masked wall
searchsector = sectorofwall(num);
searchbottomwall = searchwall = num;
searchisbottom = (searchstat==5);
if (searchstat == 5) {
searchstat = 0;
if (wall[num].nextwall >= 0 && (wall[num].cstat & 2)) {
searchbottomwall = wall[num].nextwall;
}
}
break;
case 1: // floor
case 2: // ceiling
searchsector = num;
// Apologies to Plagman for littering here, but this feature is quite essential
{
GLdouble model[16];
GLdouble proj[16];
GLint view[4];
GLdouble x,y,z;
GLfloat scr[3], scrv[3];
GLdouble scrx,scry,scrz;
GLfloat dadepth;
int16_t k, bestk=0;
GLfloat bestwdistsq = (GLfloat)3.4e38, wdistsq;
GLfloat w1[2], w2[2], w21[2], pw1[2], pw2[2];
GLfloat ptonline[2];
GLfloat scrvxz[2];
GLfloat scrvxznorm, scrvxzn[2], scrpxz[2];
GLfloat w1d, w2d;
walltype *wal = &wall[sector[searchsector].wallptr];
GLfloat t, svcoeff, p[2];
GLfloat *pl;
glGetDoublev(GL_MODELVIEW_MATRIX, model);
glGetDoublev(GL_PROJECTION_MATRIX, proj);
glGetIntegerv(GL_VIEWPORT, view);
glReadPixels(searchx, ydim-searchy, 1,1, GL_DEPTH_COMPONENT, GL_FLOAT, &dadepth);
bgluUnProject(searchx, ydim-searchy, dadepth, model, proj, view, &x, &y, &z);
bgluUnProject(searchx, ydim-searchy, 0.0, model, proj, view, &scrx, &scry, &scrz);
scr[0]=scrx, scr[1]=scry, scr[2]=scrz;
scrv[0] = x-scrx;
scrv[1] = y-scry;
scrv[2] = z-scrz;
scrvxz[0] = x-scrx;
scrvxz[1] = z-scrz;
if (prsectors[searchsector]==NULL)
{
//OSD_Printf("polymer_editorpick: prsectors[searchsector]==NULL !!!\n");
searchwall = sector[num].wallptr;
}
else
{
if (searchstat==1)
pl = prsectors[searchsector]->ceil.plane;
else
pl = prsectors[searchsector]->floor.plane;
if (pl == NULL)
{
searchwall = sector[num].wallptr;
return;
}
t = dot3f(pl,scrv);
svcoeff = -(dot3f(pl,scr)+pl[3])/t;
// point on plane (x and z)
p[0] = scrx + svcoeff*scrv[0];
p[1] = scrz + svcoeff*scrv[2];
for (k=0; k<sector[searchsector].wallnum; k++)
{
w1[1] = -(float)wal[k].x;
w1[0] = (float)wal[k].y;
w2[1] = -(float)wall[wal[k].point2].x;
w2[0] = (float)wall[wal[k].point2].y;
scrvxznorm = sqrt(dot2f(scrvxz,scrvxz));
scrvxzn[0] = scrvxz[1]/scrvxznorm;
scrvxzn[1] = -scrvxz[0]/scrvxznorm;
relvec2f(p,w1, pw1);
relvec2f(p,w2, pw2);
relvec2f(w2,w1, w21);
w1d = dot2f(scrvxzn,pw1);
w2d = dot2f(scrvxzn,pw2);
w2d = -w2d;
if (w1d <= 0 || w2d <= 0)
continue;
ptonline[0] = w2[0]+(w2d/(w1d+w2d))*w21[0];
ptonline[1] = w2[1]+(w2d/(w1d+w2d))*w21[1];
relvec2f(p,ptonline, scrpxz);
if (dot2f(scrvxz,scrpxz)<0)
continue;
wdistsq = dot2f(scrpxz,scrpxz);
if (wdistsq < bestwdistsq)
{
bestk = k;
bestwdistsq = wdistsq;
}
}
searchwall = sector[searchsector].wallptr + bestk;
}
}
// :P
// searchwall = sector[num].wallptr;
break;
case 3:
// sprite
searchsector = sprite[num].sectnum;
searchwall = num;
break;
}
searchit = 0;
}
void polymer_inb4rotatesprite(int16_t tilenum, char pal, int8_t shade, int32_t method)
{
_prmaterial rotatespritematerial;
polymer_getbuildmaterial(&rotatespritematerial, tilenum, pal, shade, 0, method);
rotatespritematerialbits = polymer_bindmaterial(&rotatespritematerial, NULL, 0);
}
void polymer_postrotatesprite(void)
{
polymer_unbindmaterial(rotatespritematerialbits);
}
static void polymer_setupdiffusemodulation(_prplane *plane, GLubyte modulation, GLubyte *data)
{
plane->material.diffusemodulation[0] = modulation;
plane->material.diffusemodulation[1] = ((GLubyte *) data)[0];
plane->material.diffusemodulation[2] = ((GLubyte *) data)[1];
plane->material.diffusemodulation[3] = 0xFF;
}
static void polymer_drawsearchplane(_prplane *plane, GLubyte *oldcolor, GLubyte modulation, GLubyte *data)
{
Bmemcpy(oldcolor, plane->material.diffusemodulation, sizeof(GLubyte) * 4);
polymer_setupdiffusemodulation(plane, modulation, data);
polymer_drawplane(plane);
Bmemcpy(plane->material.diffusemodulation, oldcolor, sizeof(GLubyte) * 4);
}
void polymer_drawmaskwall(int32_t damaskwallcnt)
{
usectortype *sec;
walltype *wal;
_prwall *w;
GLubyte oldcolor[4];
if (pr_verbosity >= 3) OSD_Printf("PR : Masked wall %i...\n", damaskwallcnt);
sec = (usectortype *)&sector[sectorofwall(maskwall[damaskwallcnt])];
wal = &wall[maskwall[damaskwallcnt]];
w = prwalls[maskwall[damaskwallcnt]];
glEnable(GL_CULL_FACE);
if (searchit == 2) {
polymer_drawsearchplane(&w->mask, oldcolor, 0x04, (GLubyte *)&maskwall[damaskwallcnt]);
} else {
calc_and_apply_fog(wal->picnum, fogshade(wal->shade, wal->pal), sec->visibility, get_floor_fogpal(sec));
polymer_drawplane(&w->mask);
}
glDisable(GL_CULL_FACE);
}
void polymer_drawsprite(int32_t snum)
{
int32_t i, j, cs;
_prsprite *s;
uspritetype *const tspr = tspriteptr[snum];
const usectortype *sec;
if (pr_verbosity >= 3) OSD_Printf("PR : Sprite %i...\n", snum);
if (bad_tspr(tspr))
return;
if ((tspr->cstat & 8192) && (depth && !mirrors[depth-1].plane))
return;
if ((tspr->cstat & 16384) && (!depth || mirrors[depth-1].plane))
return;
DO_TILE_ANIM(tspr->picnum, tspr->owner+32768);
sec = (usectortype *)&sector[tspr->sectnum];
calc_and_apply_fog(tspr->picnum, fogshade(tspr->shade, tspr->pal), sec->visibility,
get_floor_fogpal((usectortype *)&sector[tspr->sectnum]));
if (usemodels && tile2model[Ptile2tile(tspr->picnum,tspr->pal)].modelid >= 0 &&
tile2model[Ptile2tile(tspr->picnum,tspr->pal)].framenum >= 0 &&
!(spriteext[tspr->owner].flags & SPREXT_NOTMD))
{
glEnable(GL_CULL_FACE);
SWITCH_CULL_DIRECTION;
polymer_drawmdsprite(tspr);
SWITCH_CULL_DIRECTION;
glDisable(GL_CULL_FACE);
return;
}
cs = tspr->cstat;
// I think messing with the tspr is safe at this point?
// If not, change that to modify a temp position in updatesprite itself.
// I don't think this flags are meant to change on the fly so it'd possibly
// be safe to cache a plane that has them applied.
if (spriteext[tspr->owner].flags & SPREXT_AWAY1)
{
tspr->x += sintable[(tspr->ang + 512) & 2047] >> 13;
tspr->y += sintable[tspr->ang & 2047] >> 13;
}
else if (spriteext[tspr->owner].flags & SPREXT_AWAY2)
{
tspr->x -= sintable[(tspr->ang + 512) & 2047] >> 13;
tspr->y -= sintable[tspr->ang & 2047] >> 13;
}
polymer_updatesprite(snum);
Bassert(tspr->owner < MAXSPRITES);
s = prsprites[tspr->owner];
if (s == NULL)
return;
switch ((tspr->cstat>>4) & 3)
{
case 1:
prsectors[tspr->sectnum]->wallsproffset += 0.5f;
if (!depth || mirrors[depth-1].plane)
glPolygonOffset(-1.0f, -1.0f);
break;
case 2:
prsectors[tspr->sectnum]->floorsproffset += 0.5f;
if (!depth || mirrors[depth-1].plane)
glPolygonOffset(-1.0f, -1.0f);
break;
}
if ((cs & 48) == 0)
{
int32_t curpriority = 0;
s->plane.lightcount = 0;
while ((curpriority < pr_maxlightpriority) && (!depth || mirrors[depth-1].plane))
{
i = j = 0;
while (j < lightcount)
{
while (!prlights[i].flags.active)
i++;
if (prlights[i].priority != curpriority)
{
i++;
j++;
continue;
}
if (polymer_planeinlight(&s->plane, &prlights[i]))
s->plane.lights[s->plane.lightcount++] = i;
i++;
j++;
}
curpriority++;
}
}
if ((tspr->cstat & 64) && (tspr->cstat & SPR_ALIGN_MASK))
{
if ((tspr->cstat & SPR_ALIGN_MASK)==SPR_FLOOR && (tspr->cstat & SPR_YFLIP))
SWITCH_CULL_DIRECTION;
glEnable(GL_CULL_FACE);
}
if ((!depth || mirrors[depth-1].plane) && !pr_ati_nodepthoffset)
glEnable(GL_POLYGON_OFFSET_FILL);
polymer_drawplane(&s->plane);
if ((!depth || mirrors[depth-1].plane) && !pr_ati_nodepthoffset)
glDisable(GL_POLYGON_OFFSET_FILL);
if ((tspr->cstat & 64) && (tspr->cstat & SPR_ALIGN_MASK))
{
if ((tspr->cstat & SPR_ALIGN_MASK)==SPR_FLOOR && (tspr->cstat & SPR_YFLIP))
SWITCH_CULL_DIRECTION;
glDisable(GL_CULL_FACE);
}
}
void polymer_setanimatesprites(animatespritesptr animatesprites, int32_t x, int32_t y, int32_t a, int32_t smoothratio)
{
asi.animatesprites = animatesprites;
asi.x = x;
asi.y = y;
asi.a = a;
asi.smoothratio = smoothratio;
}
int16_t polymer_addlight(_prlight* light)
{
int32_t lighti;
if (lightcount >= PR_MAXLIGHTS || light->priority > pr_maxlightpriority || !pr_lighting)
return -1;
if ((light->sector == -1) || (light->sector >= numsectors))
return -1;
lighti = 0;
while ((lighti < PR_MAXLIGHTS) && (prlights[lighti].flags.active))
lighti++;
if (lighti == PR_MAXLIGHTS)
return -1;
#if 0
// Spot lights disabled on ATI cards because they cause crashes with
// Catalyst 12.8 drivers.
// See: http://forums.duke4.net/topic/5723-hrp-polymer-crash/
if (pr_ati_fboworkaround && light->radius)
return -1;
#endif
Bmemcpy(&prlights[lighti], light, sizeof(_prlight));
if (light->radius) {
polymer_processspotlight(&prlights[lighti]);
// get the texture handle for the lightmap
if (light->tilenum > 0) {
int16_t picnum = light->tilenum;
pthtyp* pth;
DO_TILE_ANIM(picnum, 0);
if (!waloff[picnum])
tileLoad(picnum);
pth = NULL;
pth = texcache_fetch(picnum, 0, 0, DAMETH_NOMASK);
if (pth)
light->lightmap = pth->glpic;
}
}
prlights[lighti].flags.isinview = 0;
prlights[lighti].flags.active = 1;
prlights[lighti].flags.invalidate = 0;
prlights[lighti].planecount = 0;
prlights[lighti].planelist = NULL;
polymer_culllight(lighti);
lightcount++;
return lighti;
}
void polymer_deletelight(int16_t lighti)
{
if (!prlights[lighti].flags.active)
{
#ifdef DEBUGGINGAIDS
if (pr_verbosity >= 2)
OSD_Printf("PR : Called polymer_deletelight on inactive light\n");
// currently known cases: when reloading maphack lights (didn't set maphacklightcnt=0
// but did loadmaphack()->delete_maphack_lights() after polymer_resetlights())
#endif
return;
}
polymer_removelight(lighti);
prlights[lighti].flags.active = 0;
lightcount--;
}
void polymer_invalidatelights(void)
{
int32_t i = PR_MAXLIGHTS-1;
do
prlights[i].flags.invalidate = prlights[i].flags.active;
while (i--);
}
void polymer_texinvalidate(void)
{
int32_t i;
i = 0;
while (i < MAXSPRITES) {
polymer_invalidatesprite(i);
i++;
}
i = numsectors - 1;
if (!numsectors || !prsectors[i])
return;
do
prsectors[i--]->flags.invalidtex = 1;
while (i >= 0);
i = numwalls - 1;
do
prwalls[i--]->flags.invalidtex = 1;
while (i >= 0);
}
void polymer_definehighpalookup(char basepalnum, char palnum, char *data)
{
prhighpalookups[basepalnum][palnum].data = (char *)Xmalloc(PR_HIGHPALOOKUP_DATA_SIZE);
Bmemcpy(prhighpalookups[basepalnum][palnum].data, data, PR_HIGHPALOOKUP_DATA_SIZE);
}
int32_t polymer_havehighpalookup(int32_t basepalnum, int32_t palnum)
{
if ((uint32_t)basepalnum >= MAXBASEPALS || (uint32_t)palnum >= MAXPALOOKUPS)
return 0;
return (prhighpalookups[basepalnum][palnum].data != NULL);
}
// CORE
static void polymer_displayrooms(const int16_t dacursectnum)
{
usectortype *sec;
int32_t i;
int16_t bunchnum;
int16_t ns;
GLint result;
int16_t doquery;
int32_t front;
int32_t back;
GLfloat localskymodelviewmatrix[16];
GLfloat localmodelviewmatrix[16];
GLfloat localprojectionmatrix[16];
float frustum[5 * 4];
int32_t localspritesortcnt;
uspritetype localtsprite[MAXSPRITESONSCREEN];
int16_t localmaskwall[MAXWALLSB];
int16_t localmaskwallcnt;
_prmirror mirrorlist[10];
int mirrorcount;
int16_t *localsectormasks;
int16_t *localsectormaskcount;
int32_t gx, gy, gz, px, py, pz;
GLdouble plane[4];
float coeff;
curmodelviewmatrix = localmodelviewmatrix;
glGetFloatv(GL_MODELVIEW_MATRIX, localmodelviewmatrix);
glGetFloatv(GL_PROJECTION_MATRIX, localprojectionmatrix);
polymer_extractfrustum(localmodelviewmatrix, localprojectionmatrix, frustum);
Bmemset(querydelay, 0, sizeof(int16_t) * numsectors);
Bmemset(queryid, 0, sizeof(GLuint) * numwalls);
Bmemset(drawingstate, 0, sizeof(int16_t) * numsectors);
front = 0;
back = 1;
sectorqueue[0] = dacursectnum;
drawingstate[dacursectnum] = 1;
localspritesortcnt = localmaskwallcnt = 0;
mirrorcount = 0;
localsectormasks = (int16_t *)Xmalloc(sizeof(int16_t) * numsectors);
localsectormaskcount = (int16_t *)Xcalloc(sizeof(int16_t), 1);
cursectormasks = localsectormasks;
cursectormaskcount = localsectormaskcount;
glDisable(GL_DEPTH_TEST);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
polymer_drawsky(cursky, curskypal, curskyshade);
glEnable(GL_DEPTH_TEST);
// depth-only occlusion testing pass
// overridematerial = 0;
prcanbucket = 1;
while (front != back)
{
sec = (usectortype *)&sector[sectorqueue[front]];
polymer_pokesector(sectorqueue[front]);
polymer_drawsector(sectorqueue[front], FALSE);
polymer_scansprites(sectorqueue[front], localtsprite, &localspritesortcnt);
doquery = 0;
i = sec->wallnum-1;
do
{
// if we have a level boundary somewhere in the sector,
// consider these walls as visportals
if (wall[sec->wallptr + i].nextsector < 0 && pr_buckets == 0)
doquery = 1;
}
while (--i >= 0);
i = sec->wallnum-1;
while (i >= 0)
{
if ((wall[sec->wallptr + i].nextsector >= 0) &&
(wallvisible(globalposx, globalposy, sec->wallptr + i)) &&
(polymer_planeinfrustum(&prwalls[sec->wallptr + i]->mask, frustum)))
{
if ((prwalls[sec->wallptr + i]->mask.vertcount == 4) &&
!(prwalls[sec->wallptr + i]->underover & 4) &&
!(prwalls[sec->wallptr + i]->underover & 8))
{
// early exit for closed sectors
_prwall *w;
w = prwalls[sec->wallptr + i];
if ((w->mask.buffer[0].y >= w->mask.buffer[3].y) &&
(w->mask.buffer[1].y >= w->mask.buffer[2].y))
{
i--;
continue;
}
}
if ((wall[sec->wallptr + i].cstat & 48) == 16)
{
int pic = wall[sec->wallptr + i].overpicnum;
if (tilesiz[pic].x > 0 && tilesiz[pic].y > 0)
localmaskwall[localmaskwallcnt++] = sec->wallptr + i;
}
if (!depth && (overridematerial & prprogrambits[PR_BIT_MIRROR_MAP].bit) &&
wall[sec->wallptr + i].overpicnum == 560 &&
wall[sec->wallptr + i].cstat & 32)
{
mirrorlist[mirrorcount].plane = &prwalls[sec->wallptr + i]->mask;
mirrorlist[mirrorcount].sectnum = sectorqueue[front];
mirrorlist[mirrorcount].wallnum = sec->wallptr + i;
mirrorcount++;
}
if (!(wall[sec->wallptr + i].cstat & 32)) {
if (doquery && (!drawingstate[wall[sec->wallptr + i].nextsector]))
{
float pos[3], sqdist;
int32_t oldoverridematerial;
pos[0] = fglobalposy;
pos[1] = fglobalposz * (-1.f/16.f);
pos[2] = -fglobalposx;
sqdist = prwalls[sec->wallptr + i]->mask.plane[0] * pos[0] +
prwalls[sec->wallptr + i]->mask.plane[1] * pos[1] +
prwalls[sec->wallptr + i]->mask.plane[2] * pos[2] +
prwalls[sec->wallptr + i]->mask.plane[3];
// hack to avoid occlusion querying portals that are too close to the viewpoint
// this is needed because of the near z-clipping plane;
if (sqdist < 100)
queryid[sec->wallptr + i] = 0xFFFFFFFF;
else {
_prwall *w;
w = prwalls[sec->wallptr + i];
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
glGenQueries(1, &queryid[sec->wallptr + i]);
glBeginQuery(GL_SAMPLES_PASSED, queryid[sec->wallptr + i]);
oldoverridematerial = overridematerial;
overridematerial = 0;
if ((w->underover & 4) && (w->underover & 1))
polymer_drawplane(&w->wall);
polymer_drawplane(&w->mask);
if ((w->underover & 8) && (w->underover & 2))
polymer_drawplane(&w->over);
overridematerial = oldoverridematerial;
glEndQuery(GL_SAMPLES_PASSED);
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
} else
queryid[sec->wallptr + i] = 0xFFFFFFFF;
}
}
i--;
}
// Cram as much CPU or GPU work as we can between queuing the
// occlusion queries and reaping them.
i = sec->wallnum-1;
do
{
if (wallvisible(globalposx, globalposy, sec->wallptr + i))
polymer_drawwall(sectorqueue[front], sec->wallptr + i);
}
while (--i >= 0);
#ifdef YAX_ENABLE
// queue ROR neighbors
if ((bunchnum = yax_getbunch(sectorqueue[front], YAX_FLOOR)) >= 0) {
for (SECTORS_OF_BUNCH(bunchnum, YAX_CEILING, ns)) {
if (ns >= 0 && !drawingstate[ns] &&
polymer_planeinfrustum(&prsectors[ns]->ceil, frustum)) {
sectorqueue[back++] = ns;
drawingstate[ns] = 1;
}
}
}
if ((bunchnum = yax_getbunch(sectorqueue[front], YAX_CEILING)) >= 0) {
for (SECTORS_OF_BUNCH(bunchnum, YAX_FLOOR, ns)) {
if (ns >= 0 && !drawingstate[ns] &&
polymer_planeinfrustum(&prsectors[ns]->floor, frustum)) {
sectorqueue[back++] = ns;
drawingstate[ns] = 1;
}
}
}
#endif
i = sec->wallnum-1;
do
{
if ((queryid[sec->wallptr + i]) &&
(!drawingstate[wall[sec->wallptr + i].nextsector]))
{
// REAP
result = 0;
if (doquery && (queryid[sec->wallptr + i] != 0xFFFFFFFF))
{
glGetQueryObjectiv(queryid[sec->wallptr + i],
GL_QUERY_RESULT,
&result);
glDeleteQueries(1, &queryid[sec->wallptr + i]);
} else if (queryid[sec->wallptr + i] == 0xFFFFFFFF)
result = 1;
queryid[sec->wallptr + i] = 0;
if (result || !doquery)
{
sectorqueue[back++] = wall[sec->wallptr + i].nextsector;
drawingstate[wall[sec->wallptr + i].nextsector] = 1;
}
} else if (queryid[sec->wallptr + i] &&
queryid[sec->wallptr + i] != 0xFFFFFFFF)
{
glDeleteQueries(1, &queryid[sec->wallptr + i]);
queryid[sec->wallptr + i] = 0;
}
}
while (--i >= 0);
front++;
}
polymer_emptybuckets();
// do the actual shaded drawing
// overridematerial = 0xFFFFFFFF;
// go through the sector queue again
// front = 0;
// while (front < back)
// {
// sec = &sector[sectorqueue[front]];
//
// polymer_drawsector(sectorqueue[front]);
//
// i = 0;
// while (i < sec->wallnum)
// {
// polymer_drawwall(sectorqueue[front], sec->wallptr + i);
//
// i++;
// }
//
// front++;
// }
i = mirrorcount-1;
while (i >= 0)
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, prrts[0].fbo);
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(windowxy1.x, ydim-(windowxy2.y+1),windowxy2.x-windowxy1.x+1, windowxy2.y-windowxy1.y+1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Bmemcpy(localskymodelviewmatrix, curskymodelviewmatrix, sizeof(GLfloat) * 16);
curskymodelviewmatrix = localskymodelviewmatrix;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
plane[0] = mirrorlist[i].plane->plane[0];
plane[1] = mirrorlist[i].plane->plane[1];
plane[2] = mirrorlist[i].plane->plane[2];
plane[3] = mirrorlist[i].plane->plane[3];
glClipPlane(GL_CLIP_PLANE0, plane);
polymer_inb4mirror(mirrorlist[i].plane->buffer, mirrorlist[i].plane->plane);
SWITCH_CULL_DIRECTION;
//glEnable(GL_CLIP_PLANE0);
if (mirrorlist[i].wallnum >= 0)
renderPrepareMirror(globalposx, globalposy, qglobalang,
mirrorlist[i].wallnum, &gx, &gy, &viewangle);
gx = globalposx;
gy = globalposy;
gz = globalposz;
// map the player pos from build to polymer
px = globalposy;
py = -globalposz / 16;
pz = -globalposx;
// calculate new player position on the other side of the mirror
// this way the basic build visibility shit can be used (wallvisible)
coeff = mirrorlist[i].plane->plane[0] * px +
mirrorlist[i].plane->plane[1] * py +
mirrorlist[i].plane->plane[2] * pz +
mirrorlist[i].plane->plane[3];
coeff /= (float)(mirrorlist[i].plane->plane[0] * mirrorlist[i].plane->plane[0] +
mirrorlist[i].plane->plane[1] * mirrorlist[i].plane->plane[1] +
mirrorlist[i].plane->plane[2] * mirrorlist[i].plane->plane[2]);
px = (int32_t)(-coeff*mirrorlist[i].plane->plane[0]*2 + px);
py = (int32_t)(-coeff*mirrorlist[i].plane->plane[1]*2 + py);
pz = (int32_t)(-coeff*mirrorlist[i].plane->plane[2]*2 + pz);
// map back from polymer to build
set_globalpos(-pz, px, -py * 16);
mirrors[depth++] = mirrorlist[i];
polymer_displayrooms(mirrorlist[i].sectnum);
depth--;
cursectormasks = localsectormasks;
cursectormaskcount = localsectormaskcount;
set_globalpos(gx, gy, gz);
glDisable(GL_CLIP_PLANE0);
SWITCH_CULL_DIRECTION;
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glPopAttrib();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
mirrorlist[i].plane->material.mirrormap = prrts[0].color;
polymer_drawplane(mirrorlist[i].plane);
mirrorlist[i].plane->material.mirrormap = 0;
i--;
}
spritesortcnt = localspritesortcnt;
Bmemcpy(tsprite, localtsprite, sizeof(spritetype) * spritesortcnt);
maskwallcnt = localmaskwallcnt;
Bmemcpy(maskwall, localmaskwall, sizeof(int16_t) * maskwallcnt);
if (depth)
{
set_globalang(viewangle);
if (mirrors[depth - 1].plane)
display_mirror = 1;
polymer_animatesprites();
if (mirrors[depth - 1].plane)
display_mirror = 0;
glDisable(GL_CULL_FACE);
renderDrawMasks();
glEnable(GL_CULL_FACE);
}
return;
}
static void polymer_emptybuckets(void)
{
_prbucket *bucket = prbuckethead;
if (pr_buckets == 0)
return;
glBindBuffer(GL_ARRAY_BUFFER, prmapvbo);
glVertexPointer(3, GL_FLOAT, sizeof(_prvert), NULL);
glTexCoordPointer(2, GL_FLOAT, sizeof(_prvert), (GLvoid *)(3 * sizeof(GLfloat)));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prindexringvbo);
uint32_t indexcount = 0;
while (bucket != NULL)
{
indexcount += bucket->count;
bucket = bucket->next;
}
// ensure space in index ring, wrap otherwise
if (indexcount + prindexringoffset >= (unsigned)prindexringsize)
{
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
prindexring = (GLuint *)glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, prindexringsize * sizeof(GLuint), GL_MAP_INVALIDATE_BUFFER_BIT | prindexringmapflags);
prindexringoffset = 0;
}
// put indices in the ring, all at once
bucket = prbuckethead;
while (bucket != NULL)
{
if (bucket->count == 0)
{
bucket = bucket->next;
continue;
}
memcpy(&prindexring[prindexringoffset], bucket->indices, bucket->count * sizeof(GLuint));
bucket->indiceoffset = (GLuint*)(prindexringoffset * sizeof(GLuint));
prindexringoffset += bucket->count;
bucket = bucket->next;
}
bucket = prbuckethead;
while (bucket != NULL)
{
if (bucket->count == 0)
{
bucket = bucket->next;
continue;
}
int32_t materialbits = polymer_bindmaterial(&bucket->material, NULL, 0);
glDrawElements(GL_TRIANGLES, bucket->count, GL_UNSIGNED_INT, bucket->indiceoffset);
polymer_unbindmaterial(materialbits);
bucket->count = 0;
bucket = bucket->next;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
prcanbucket = 0;
}
static hashtable_t h_buckets = { 2048, NULL };
static _prbucket* polymer_findbucket(int16_t tilenum, char pal)
{
char propstr[16];
Bsprintf(propstr, "%d_%d", tilenum, pal);
_prbucket *bucketptr = prbuckethead ? (_prbucket *)hash_find(&h_buckets, propstr) : NULL;
// find bucket
// no buckets or no bucket found, create one
if (bucketptr == NULL || (intptr_t)bucketptr == -1)
{
bucketptr = (_prbucket *)Xmalloc(sizeof (_prbucket));
if (h_buckets.items == NULL)
hash_init(&h_buckets);
// insert, since most likely to use same pattern next frame
// will need to reorder by MRU first every once in a while
// or move to hashing lookup
bucketptr->next = prbuckethead;
prbuckethead = bucketptr;
bucketptr->tilenum = tilenum;
bucketptr->pal = pal;
bucketptr->invalidmaterial = 1;
bucketptr->count = 0;
bucketptr->buffersize = 1024;
bucketptr->indices = (GLuint *)Xmalloc(bucketptr->buffersize * sizeof(GLuint));
hash_add(&h_buckets, propstr, (intptr_t)bucketptr, 1);
}
return bucketptr;
}
static void polymer_bucketplane(_prplane* plane)
{
_prbucket *bucketptr = plane->bucket;
uint32_t neededindicecount;
int32_t i;
// we don't keep buffers for quads
neededindicecount = (plane->indicescount == 0) ? 6 : plane->indicescount;
// ensure size
while (bucketptr->count + neededindicecount >= bucketptr->buffersize)
{
bucketptr->buffersize *= 2;
bucketptr->indices = (GLuint *)Xrealloc(bucketptr->indices, bucketptr->buffersize * sizeof(GLuint));
}
// queue indices
i = 0;
if (plane->indicescount > 0)
{
while (i < plane->indicescount)
{
bucketptr->indices[bucketptr->count] = plane->indices[i] + plane->mapvbo_vertoffset;
bucketptr->count++;
i++;
}
}
else
{
static const uint32_t quadindices[6] = { 0, 1, 2, 0, 2, 3 };
while (i < 6)
{
bucketptr->indices[bucketptr->count] = quadindices[i] + plane->mapvbo_vertoffset;
bucketptr->count++;
i++;
}
}
}
static void polymer_drawplane(_prplane* plane)
{
int32_t materialbits;
if (pr_nullrender >= 1) return;
// debug code for drawing plane inverse TBN
// glDisable(GL_TEXTURE_2D);
// glBegin(GL_LINES);
// glColor4f(1.0, 0.0, 0.0, 1.0);
// glVertex3f(plane->buffer[0],
// plane->buffer[1],
// plane->buffer[2]);
// glVertex3f(plane->buffer[0] + plane->t[0] * 50,
// plane->buffer[1] + plane->t[1] * 50,
// plane->buffer[2] + plane->t[2] * 50);
// glColor4f(0.0, 1.0, 0.0, 1.0);
// glVertex3f(plane->buffer[0],
// plane->buffer[1],
// plane->buffer[2]);
// glVertex3f(plane->buffer[0] + plane->b[0] * 50,
// plane->buffer[1] + plane->b[1] * 50,
// plane->buffer[2] + plane->b[2] * 50);
// glColor4f(0.0, 0.0, 1.0, 1.0);
// glVertex3f(plane->buffer[0],
// plane->buffer[1],
// plane->buffer[2]);
// glVertex3f(plane->buffer[0] + plane->n[0] * 50,
// plane->buffer[1] + plane->n[1] * 50,
// plane->buffer[2] + plane->n[2] * 50);
// glEnd();
// glEnable(GL_TEXTURE_2D);
// debug code for drawing plane normals
// glDisable(GL_TEXTURE_2D);
// glBegin(GL_LINES);
// glColor4f(1.0, 1.0, 1.0, 1.0);
// glVertex3f(plane->buffer[0],
// plane->buffer[1],
// plane->buffer[2]);
// glVertex3f(plane->buffer[0] + plane->plane[0] * 50,
// plane->buffer[1] + plane->plane[1] * 50,
// plane->buffer[2] + plane->plane[2] * 50);
// glEnd();
// glEnable(GL_TEXTURE_2D);
if (pr_buckets && pr_vbos > 0 && prcanbucket && plane->bucket)
{
polymer_bucketplane(plane);
return;
}
glNormal3f((float)(plane->plane[0]), (float)(plane->plane[1]), (float)(plane->plane[2]));
GLuint planevbo;
GLintptr geomfbooffset;
if (plane->mapvbo_vertoffset != (uint32_t)-1)
{
planevbo = prmapvbo;
geomfbooffset = plane->mapvbo_vertoffset * sizeof(_prvert);
}
else
{
planevbo = plane->vbo;
geomfbooffset = 0;
}
if (planevbo && (pr_vbos > 0))
{
glBindBuffer(GL_ARRAY_BUFFER, planevbo);
glVertexPointer(3, GL_FLOAT, sizeof(_prvert), (GLvoid *)(geomfbooffset));
glTexCoordPointer(2, GL_FLOAT, sizeof(_prvert), (GLvoid *)(geomfbooffset + (3 * sizeof(GLfloat))));
if (plane->indices)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, plane->ivbo);
} else {
glVertexPointer(3, GL_FLOAT, sizeof(_prvert), &plane->buffer->x);
glTexCoordPointer(2, GL_FLOAT, sizeof(_prvert), &plane->buffer->u);
}
curlight = 0;
do {
materialbits = polymer_bindmaterial(&plane->material, plane->lights, plane->lightcount);
if (materialbits & prprogrambits[PR_BIT_NORMAL_MAP].bit)
{
glVertexAttrib3fv(prprograms[materialbits].attrib_T, &plane->tbn[0][0]);
glVertexAttrib3fv(prprograms[materialbits].attrib_B, &plane->tbn[1][0]);
glVertexAttrib3fv(prprograms[materialbits].attrib_N, &plane->tbn[2][0]);
}
if (plane->indices)
{
if (planevbo && (pr_vbos > 0))
glDrawElements(GL_TRIANGLES, plane->indicescount, GL_UNSIGNED_SHORT, NULL);
else
glDrawElements(GL_TRIANGLES, plane->indicescount, GL_UNSIGNED_SHORT, plane->indices);
} else
glDrawArrays(GL_QUADS, 0, 4);
polymer_unbindmaterial(materialbits);
if (plane->lightcount && (!depth || mirrors[depth-1].plane))
prlights[plane->lights[curlight]].flags.isinview = 1;
curlight++;
} while ((curlight < plane->lightcount) && (curlight < pr_maxlightpasses) && (!depth || mirrors[depth-1].plane));
if (planevbo && (pr_vbos > 0))
{
glBindBuffer(GL_ARRAY_BUFFER, 0);
if (plane->indices)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
static inline void polymer_inb4mirror(_prvert* buffer, GLfloat* plane)
{
float pv;
float reflectionmatrix[16];
pv = buffer->x * plane[0] +
buffer->y * plane[1] +
buffer->z * plane[2];
reflectionmatrix[0] = 1 - (2 * plane[0] * plane[0]);
reflectionmatrix[1] = -2 * plane[0] * plane[1];
reflectionmatrix[2] = -2 * plane[0] * plane[2];
reflectionmatrix[3] = 0;
reflectionmatrix[4] = -2 * plane[0] * plane[1];
reflectionmatrix[5] = 1 - (2 * plane[1] * plane[1]);
reflectionmatrix[6] = -2 * plane[1] * plane[2];
reflectionmatrix[7] = 0;
reflectionmatrix[8] = -2 * plane[0] * plane[2];
reflectionmatrix[9] = -2 * plane[1] * plane[2];
reflectionmatrix[10] = 1 - (2 * plane[2] * plane[2]);
reflectionmatrix[11] = 0;
reflectionmatrix[12] = 2 * pv * plane[0];
reflectionmatrix[13] = 2 * pv * plane[1];
reflectionmatrix[14] = 2 * pv * plane[2];
reflectionmatrix[15] = 1;
glMultMatrixf(reflectionmatrix);
glPushMatrix();
glLoadMatrixf(curskymodelviewmatrix);
glMultMatrixf(reflectionmatrix);
glGetFloatv(GL_MODELVIEW_MATRIX, curskymodelviewmatrix);
glPopMatrix();
}
static void polymer_animatesprites(void)
{
if (asi.animatesprites)
asi.animatesprites(globalposx, globalposy, fix16_to_int(viewangle), asi.smoothratio);
}
static void polymer_freeboard(void)
{
int32_t i;
i = 0;
while (i < MAXSECTORS)
{
if (prsectors[i])
{
Bfree(prsectors[i]->verts);
Bfree(prsectors[i]->floor.buffer);
Bfree(prsectors[i]->ceil.buffer);
Bfree(prsectors[i]->floor.indices);
Bfree(prsectors[i]->ceil.indices);
if (prsectors[i]->ceil.vbo) glDeleteBuffers(1, &prsectors[i]->ceil.vbo);
if (prsectors[i]->ceil.ivbo) glDeleteBuffers(1, &prsectors[i]->ceil.ivbo);
if (prsectors[i]->floor.vbo) glDeleteBuffers(1, &prsectors[i]->floor.vbo);
if (prsectors[i]->floor.ivbo) glDeleteBuffers(1, &prsectors[i]->floor.ivbo);
DO_FREE_AND_NULL(prsectors[i]);
}
i++;
}
i = 0;
while (i < MAXWALLS)
{
if (prwalls[i])
{
Bfree(prwalls[i]->bigportal);
Bfree(prwalls[i]->mask.buffer);
Bfree(prwalls[i]->over.buffer);
// Bfree(prwalls[i]->cap);
Bfree(prwalls[i]->wall.buffer);
if (prwalls[i]->wall.vbo) glDeleteBuffers(1, &prwalls[i]->wall.vbo);
if (prwalls[i]->over.vbo) glDeleteBuffers(1, &prwalls[i]->over.vbo);
if (prwalls[i]->mask.vbo) glDeleteBuffers(1, &prwalls[i]->mask.vbo);
if (prwalls[i]->stuffvbo) glDeleteBuffers(1, &prwalls[i]->stuffvbo);
DO_FREE_AND_NULL(prwalls[i]);
}
i++;
}
i = 0;
while (i < MAXSPRITES)
{
if (prsprites[i])
{
Bfree(prsprites[i]->plane.buffer);
if (prsprites[i]->plane.vbo) glDeleteBuffers(1, &prsprites[i]->plane.vbo);
DO_FREE_AND_NULL(prsprites[i]);
}
i++;
}
i = 0;
while (i < MAXTILES)
{
polymer_invalidateartmap(i);
i++;
}
i = 0;
while (i < MAXBASEPALS)
{
if (prbasepalmaps[i])
{
glDeleteTextures(1, &prbasepalmaps[i]);
prbasepalmaps[i] = 0;
}
i++;
}
i = 0;
while (i < MAXPALOOKUPS)
{
if (prlookups[i])
{
glDeleteTextures(1, &prlookups[i]);
prlookups[i] = 0;
}
i++;
}
}
// SECTORS
static int32_t polymer_initsector(int16_t sectnum)
{
usectortype *sec;
_prsector* s;
if (pr_verbosity >= 2) OSD_Printf("PR : Initializing sector %i...\n", sectnum);
sec = (usectortype *)&sector[sectnum];
s = (_prsector *)Xcalloc(1, sizeof(_prsector));
s->verts = (GLdouble *)Xcalloc(sec->wallnum, sizeof(GLdouble) * 3);
s->floor.buffer = (_prvert *)Xcalloc(sec->wallnum, sizeof(_prvert));
s->floor.vertcount = sec->wallnum;
s->ceil.buffer = (_prvert *)Xcalloc(sec->wallnum, sizeof(_prvert));
s->ceil.vertcount = sec->wallnum;
glGenBuffers(1, &s->floor.vbo);
glGenBuffers(1, &s->ceil.vbo);
glGenBuffers(1, &s->floor.ivbo);
glGenBuffers(1, &s->ceil.ivbo);
glBindBuffer(GL_ARRAY_BUFFER, s->floor.vbo);
glBufferData(GL_ARRAY_BUFFER, sec->wallnum * sizeof(GLfloat) * 5, NULL, mapvbousage);
glBindBuffer(GL_ARRAY_BUFFER, s->ceil.vbo);
glBufferData(GL_ARRAY_BUFFER, sec->wallnum * sizeof(GLfloat) * 5, NULL, mapvbousage);
glBindBuffer(GL_ARRAY_BUFFER, 0);
s->flags.empty = 1; // let updatesector know that everything needs to go
prsectors[sectnum] = s;
if (pr_verbosity >= 2) OSD_Printf("PR : Initialized sector %i.\n", sectnum);
return 1;
}
static int32_t polymer_updatesector(int16_t sectnum)
{
_prsector* s;
usectortype *sec;
walltype *wal;
int32_t i, j;
int32_t ceilz, florz;
int32_t tex, tey, heidiff;
float secangcos, secangsin, scalecoef, xpancoef, ypancoef;
int32_t ang, needfloor, wallinvalidate;
int16_t curstat, curpicnum, floorpicnum, ceilingpicnum;
char curxpanning, curypanning;
_prvert* curbuffer;
if (pr_nullrender >= 3) return 0;
s = prsectors[sectnum];
sec = (usectortype *)&sector[sectnum];
secangcos = secangsin = 2;
if (s == NULL)
{
if (pr_verbosity >= 1) OSD_Printf("PR : Can't update uninitialized sector %i.\n", sectnum);
return -1;
}
needfloor = wallinvalidate = 0;
// geometry
wal = &wall[sec->wallptr];
i = 0;
while (i < sec->wallnum)
{
if ((-wal->x != s->verts[(i*3)+2]))
{
s->verts[(i*3)+2] = s->floor.buffer[i].z = s->ceil.buffer[i].z = -(float)wal->x;
needfloor = wallinvalidate = 1;
}
if ((wal->y != s->verts[i*3]))
{
s->verts[i*3] = s->floor.buffer[i].x = s->ceil.buffer[i].x = (float)wal->y;
needfloor = wallinvalidate = 1;
}
i++;
wal = &wall[sec->wallptr + i];
}
if ((s->flags.empty) ||
needfloor ||
(sec->floorz != s->floorz) ||
(sec->ceilingz != s->ceilingz) ||
(sec->floorheinum != s->floorheinum) ||
(sec->ceilingheinum != s->ceilingheinum))
{
wallinvalidate = 1;
wal = &wall[sec->wallptr];
i = 0;
while (i < sec->wallnum)
{
getzsofslope(sectnum, wal->x, wal->y, &ceilz, &florz);
s->floor.buffer[i].y = -(float)(florz) / 16.0f;
s->ceil.buffer[i].y = -(float)(ceilz) / 16.0f;
i++;
wal = &wall[sec->wallptr + i];
}
s->floorz = sec->floorz;
s->ceilingz = sec->ceilingz;
s->floorheinum = sec->floorheinum;
s->ceilingheinum = sec->ceilingheinum;
}
else if (sec->visibility != s->visibility)
wallinvalidate = 1;
floorpicnum = sec->floorpicnum;
DO_TILE_ANIM(floorpicnum, sectnum);
ceilingpicnum = sec->ceilingpicnum;
DO_TILE_ANIM(ceilingpicnum, sectnum);
if ((!s->flags.empty) && (!needfloor) &&
(floorpicnum == s->floorpicnum_anim) &&
(ceilingpicnum == s->ceilingpicnum_anim) &&
#ifndef UNTRACKED_STRUCTS
(s->trackedrev == sectorchanged[sectnum]))
#else
!Bmemcmp(&s->ceilingstat, &sec->ceilingstat, offsetof(sectortype, visibility) - offsetof(sectortype, ceilingstat)))
#endif
goto attributes;
wal = &wall[sec->wallptr];
i = 0;
while (i < sec->wallnum)
{
j = 2;
curstat = sec->floorstat;
curbuffer = s->floor.buffer;
curpicnum = floorpicnum;
curxpanning = sec->floorxpanning;
curypanning = sec->floorypanning;
while (j)
{
if (j == 1)
{
curstat = sec->ceilingstat;
curbuffer = s->ceil.buffer;
curpicnum = ceilingpicnum;
curxpanning = sec->ceilingxpanning;
curypanning = sec->ceilingypanning;
}
if (!waloff[curpicnum])
tileLoad(curpicnum);
if (((sec->floorstat & 64) || (sec->ceilingstat & 64)) &&
((secangcos == 2) && (secangsin == 2)))
{
ang = (getangle(wall[wal->point2].x - wal->x, wall[wal->point2].y - wal->y) + 512) & 2047;
secangcos = (float)(sintable[(ang+512)&2047]) / 16383.0f;
secangsin = (float)(sintable[ang&2047]) / 16383.0f;
}
// relative texturing
if (curstat & 64)
{
xpancoef = (float)(wal->x - wall[sec->wallptr].x);
ypancoef = (float)(wall[sec->wallptr].y - wal->y);
tex = (int32_t)(xpancoef * secangsin + ypancoef * secangcos);
tey = (int32_t)(xpancoef * secangcos - ypancoef * secangsin);
} else {
tex = wal->x;
tey = -wal->y;
}
if ((curstat & (2+64)) == (2+64))
{
heidiff = (int32_t)(curbuffer[i].y - curbuffer[0].y);
// don't forget the sign, tey could be negative with concave sectors
if (tey >= 0)
tey = (int32_t)sqrt((double)((tey * tey) + (heidiff * heidiff)));
else
tey = -(int32_t)sqrt((double)((tey * tey) + (heidiff * heidiff)));
}
if (curstat & 4)
swaplong(&tex, &tey);
if (curstat & 16) tex = -tex;
if (curstat & 32) tey = -tey;
scalecoef = (curstat & 8) ? 8.0f : 16.0f;
if (curxpanning)
{
xpancoef = (float)(pow2long[picsiz[curpicnum] & 15]);
xpancoef *= (float)(curxpanning) / (256.0f * (float)(tilesiz[curpicnum].x));
}
else
xpancoef = 0;
if (curypanning)
{
ypancoef = (float)(pow2long[picsiz[curpicnum] >> 4]);
ypancoef *= (float)(curypanning) / (256.0f * (float)(tilesiz[curpicnum].y));
}
else
ypancoef = 0;
curbuffer[i].u = ((float)(tex) / (scalecoef * tilesiz[curpicnum].x)) + xpancoef;
curbuffer[i].v = ((float)(tey) / (scalecoef * tilesiz[curpicnum].y)) + ypancoef;
j--;
}
i++;
wal = &wall[sec->wallptr + i];
}
s->floorxpanning = sec->floorxpanning;
s->ceilingxpanning = sec->ceilingxpanning;
s->floorypanning = sec->floorypanning;
s->ceilingypanning = sec->ceilingypanning;
#ifndef UNTRACKED_STRUCTS
s->trackedrev = sectorchanged[sectnum];
#endif
i = -1;
attributes:
if ((pr_vbos > 0) && ((i == -1) || (wallinvalidate)))
{
if (pr_vbos > 0)
{
if (pr_nullrender < 2)
{
/*glBindBuffer(GL_ARRAY_BUFFER, s->floor.vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sec->wallnum * sizeof(GLfloat)* 5, s->floor.buffer);
glBindBuffer(GL_ARRAY_BUFFER, s->ceil.vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sec->wallnum * sizeof(GLfloat)* 5, s->ceil.buffer);
*/
s->floor.mapvbo_vertoffset = sec->wallptr * 2;
s->ceil.mapvbo_vertoffset = s->floor.mapvbo_vertoffset + sec->wallnum;
GLintptr sector_offset = s->floor.mapvbo_vertoffset * sizeof(_prvert);
GLsizeiptr cur_sector_size = sec->wallnum * sizeof(_prvert);
glBindBuffer(GL_ARRAY_BUFFER, prmapvbo);
// floor
glBufferSubData(GL_ARRAY_BUFFER, sector_offset, cur_sector_size, s->floor.buffer);
// ceiling
glBufferSubData(GL_ARRAY_BUFFER, sector_offset + cur_sector_size, cur_sector_size, s->ceil.buffer);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
else
{
s->floor.mapvbo_vertoffset = -1;
s->ceil.mapvbo_vertoffset = -1;
}
}
if ((!s->flags.empty) && (!s->flags.invalidtex) &&
(floorpicnum == s->floorpicnum_anim) &&
(ceilingpicnum == s->ceilingpicnum_anim) &&
!Bmemcmp(&s->ceilingstat, &sec->ceilingstat, offsetof(sectortype, visibility) - offsetof(sectortype, ceilingstat)))
goto finish;
s->floor.bucket = polymer_getbuildmaterial(&s->floor.material, floorpicnum, sec->floorpal, sec->floorshade, sec->visibility, (sec->floorstat & 384) ? DAMETH_MASK : DAMETH_NOMASK);
if (sec->floorstat & 256) {
if (sec->floorstat & 128) {
s->floor.material.diffusemodulation[3] = 0x55;
} else {
s->floor.material.diffusemodulation[3] = 0xAA;
}
}
s->ceil.bucket = polymer_getbuildmaterial(&s->ceil.material, ceilingpicnum, sec->ceilingpal, sec->ceilingshade, sec->visibility, (sec->ceilingstat & 384) ? DAMETH_MASK : DAMETH_NOMASK);
if (sec->ceilingstat & 256) {
if (sec->ceilingstat & 128) {
s->ceil.material.diffusemodulation[3] = 0x55;
} else {
s->ceil.material.diffusemodulation[3] = 0xAA;
}
}
s->flags.invalidtex = 0;
// copy ceilingstat through visibility members
Bmemcpy(&s->ceilingstat, &sec->ceilingstat, offsetof(sectortype, visibility) - offsetof(sectortype, ceilingstat));
s->floorpicnum_anim = floorpicnum;
s->ceilingpicnum_anim = ceilingpicnum;
finish:
if (needfloor)
{
polymer_buildfloor(sectnum);
if ((pr_vbos > 0))
{
if (pr_nullrender < 2)
{
if (s->oldindicescount < s->indicescount)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->floor.ivbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, s->indicescount * sizeof(GLushort), NULL, mapvbousage);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->ceil.ivbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, s->indicescount * sizeof(GLushort), NULL, mapvbousage);
s->oldindicescount = s->indicescount;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->floor.ivbo);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, s->indicescount * sizeof(GLushort), s->floor.indices);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->ceil.ivbo);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, s->indicescount * sizeof(GLushort), s->ceil.indices);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
}
if (wallinvalidate)
{
s->invalidid++;
polymer_invalidatesectorlights(sectnum);
polymer_computeplane(&s->floor);
polymer_computeplane(&s->ceil);
}
s->flags.empty = 0;
s->flags.uptodate = 1;
if (pr_verbosity >= 3) OSD_Printf("PR : Updated sector %i.\n", sectnum);
return 0;
}
void PR_CALLBACK polymer_tesserror(GLenum error)
{
/* This callback is called by the tesselator whenever it raises an error.
GLU_TESS_ERROR6 is the "no error"/"null" error spam in e1l1 and others. */
if (pr_verbosity >= 1 && error != GLU_TESS_ERROR6) OSD_Printf("PR : Tesselation error number %i reported : %s.\n", error, bgluErrorString(errno));
}
void PR_CALLBACK polymer_tessedgeflag(GLenum error)
{
// Passing an edgeflag callback forces the tesselator to output a triangle list
UNREFERENCED_PARAMETER(error);
return;
}
void PR_CALLBACK polymer_tessvertex(void* vertex, void* sector)
{
_prsector* s;
s = (_prsector*)sector;
if (s->curindice >= s->indicescount)
{
if (pr_verbosity >= 2) OSD_Printf("PR : Indice overflow, extending the indices list... !\n");
s->indicescount++;
s->floor.indices = (GLushort *)Xrealloc(s->floor.indices, s->indicescount * sizeof(GLushort));
s->ceil.indices = (GLushort *)Xrealloc(s->ceil.indices, s->indicescount * sizeof(GLushort));
}
s->ceil.indices[s->curindice] = (intptr_t)vertex;
s->curindice++;
}
static int32_t polymer_buildfloor(int16_t sectnum)
{
// This function tesselates the floor/ceiling of a sector and stores the triangles in a display list.
_prsector* s;
usectortype *sec;
intptr_t i;
if (pr_verbosity >= 2) OSD_Printf("PR : Tesselating floor of sector %i...\n", sectnum);
s = prsectors[sectnum];
sec = (usectortype *)&sector[sectnum];
if (s == NULL)
return -1;
if (s->floor.indices == NULL)
{
s->indicescount = (max(3, sec->wallnum) - 2) * 3;
s->floor.indices = (GLushort *)Xcalloc(s->indicescount, sizeof(GLushort));
s->ceil.indices = (GLushort *)Xcalloc(s->indicescount, sizeof(GLushort));
}
s->curindice = 0;
bgluTessCallback(prtess, GLU_TESS_VERTEX_DATA, (void (PR_CALLBACK *)(void))polymer_tessvertex);
bgluTessCallback(prtess, GLU_TESS_EDGE_FLAG, (void (PR_CALLBACK *)(void))polymer_tessedgeflag);
bgluTessCallback(prtess, GLU_TESS_ERROR, (void (PR_CALLBACK *)(void))polymer_tesserror);
bgluTessProperty(prtess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE);
bgluTessBeginPolygon(prtess, s);
bgluTessBeginContour(prtess);
i = 0;
while (i < sec->wallnum)
{
bgluTessVertex(prtess, s->verts + (3 * i), (void *)i);
if ((i != (sec->wallnum - 1)) && ((sec->wallptr + i) > wall[sec->wallptr + i].point2))
{
bgluTessEndContour(prtess);
bgluTessBeginContour(prtess);
}
i++;
}
bgluTessEndContour(prtess);
bgluTessEndPolygon(prtess);
i = 0;
while (i < s->indicescount)
{
s->floor.indices[s->indicescount - i - 1] = s->ceil.indices[i];
i++;
}
s->floor.indicescount = s->ceil.indicescount = s->indicescount;
if (pr_verbosity >= 2) OSD_Printf("PR : Tesselated floor of sector %i.\n", sectnum);
return 1;
}
static void polymer_drawsector(int16_t sectnum, int32_t domasks)
{
usectortype *sec;
_prsector* s;
GLubyte oldcolor[4];
int32_t draw;
int32_t queuedmask;
if (pr_verbosity >= 3) OSD_Printf("PR : Drawing sector %i...\n", sectnum);
sec = (usectortype *)&sector[sectnum];
s = prsectors[sectnum];
queuedmask = FALSE;
// If you're thinking of 'optimizing' the following logic, you'd better
// provide compelling evidence that the generated code is more efficient
// than what GCC can come up with on its own.
draw = TRUE;
// Draw masks regardless; avoid all non-masks TROR links
if (sec->floorstat & 384) {
draw = domasks;
} else if (yax_getbunch(sectnum, YAX_FLOOR) >= 0) {
draw = FALSE;
}
// Parallaxed
if (sec->floorstat & 1) {
draw = FALSE;
}
if (draw || (searchit == 2)) {
if (searchit == 2) {
polymer_drawsearchplane(&s->floor, oldcolor, 0x02, (GLubyte *) &sectnum);
}
else {
calc_and_apply_fog(sec->floorpicnum, fogshade(sec->floorshade, sec->floorpal),
sec->visibility, get_floor_fogpal(sec));
polymer_drawplane(&s->floor);
}
} else if (!domasks && cursectormaskcount && sec->floorstat & 384) {
// If we just skipped a mask, queue it for later
cursectormasks[(*cursectormaskcount)++] = sectnum;
// Don't queue it twice if the ceiling is also a mask, though.
queuedmask = TRUE;
}
draw = TRUE;
// Draw masks regardless; avoid all non-masks TROR links
if (sec->ceilingstat & 384) {
draw = domasks;
} else if (yax_getbunch(sectnum, YAX_CEILING) >= 0) {
draw = FALSE;
}
// Parallaxed
if (sec->ceilingstat & 1) {
draw = FALSE;
}
if (draw || (searchit == 2)) {
if (searchit == 2) {
polymer_drawsearchplane(&s->ceil, oldcolor, 0x01, (GLubyte *) &sectnum);
}
else {
calc_and_apply_fog(sec->ceilingpicnum, fogshade(sec->ceilingshade, sec->ceilingpal),
sec->visibility, get_ceiling_fogpal(sec));
polymer_drawplane(&s->ceil);
}
} else if (!domasks && !queuedmask && cursectormaskcount &&
(sec->ceilingstat & 384)) {
// If we just skipped a mask, queue it for later
cursectormasks[(*cursectormaskcount)++] = sectnum;
}
if (pr_verbosity >= 3) OSD_Printf("PR : Finished drawing sector %i...\n", sectnum);
}
// WALLS
static int32_t polymer_initwall(int16_t wallnum)
{
_prwall *w;
if (pr_verbosity >= 2) OSD_Printf("PR : Initializing wall %i...\n", wallnum);
w = (_prwall *)Xcalloc(1, sizeof(_prwall));
if (w->mask.buffer == NULL) {
w->mask.buffer = (_prvert *)Xmalloc(4 * sizeof(_prvert));
w->mask.vertcount = 4;
}
if (w->bigportal == NULL)
w->bigportal = (GLfloat *)Xmalloc(4 * sizeof(GLfloat) * 5);
//if (w->cap == NULL)
// w->cap = (GLfloat *)Xmalloc(4 * sizeof(GLfloat) * 3);
glGenBuffers(1, &w->wall.vbo);
glGenBuffers(1, &w->over.vbo);
glGenBuffers(1, &w->mask.vbo);
glGenBuffers(1, &w->stuffvbo);
glBindBuffer(GL_ARRAY_BUFFER, w->wall.vbo);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(GLfloat) * 5, NULL, mapvbousage);
glBindBuffer(GL_ARRAY_BUFFER, w->over.vbo);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(GLfloat) * 5, NULL, mapvbousage);
glBindBuffer(GL_ARRAY_BUFFER, w->mask.vbo);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(GLfloat) * 5, NULL, mapvbousage);
glBindBuffer(GL_ARRAY_BUFFER, w->stuffvbo);
glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat) * 5, NULL, mapvbousage);
glBindBuffer(GL_ARRAY_BUFFER, 0);
w->flags.empty = 1;
prwalls[wallnum] = w;
if (pr_verbosity >= 2) OSD_Printf("PR : Initialized wall %i.\n", wallnum);
return 1;
}
// TODO: r_npotwallmode. Needs polymost_is_npotmode() handling among others.
#define DAMETH_WALL 0
static float calc_ypancoef(char curypanning, int16_t curpicnum, int32_t dopancor)
{
#ifdef NEW_MAP_FORMAT
if (g_loadedMapVersion >= 10)
return curypanning / 256.0f;
#endif
{
float ypancoef = (float)(pow2long[picsiz[curpicnum] >> 4]);
if (ypancoef < tilesiz[curpicnum].y)
ypancoef *= 2;
if (dopancor)
{
int32_t yoffs = Blrintf((ypancoef - tilesiz[curpicnum].y) * (255.0f / ypancoef));
if (curypanning > 256 - yoffs)
curypanning -= yoffs;
}
ypancoef *= (float)curypanning / (256.0f * (float)tilesiz[curpicnum].y);
return ypancoef;
}
}
#define NBYTES_WALL_CSTAT_THROUGH_YPANNING \
(offsetof(walltype, ypanning)+sizeof(wall[0].ypanning) - offsetof(walltype, cstat))
static void polymer_updatewall(int16_t wallnum)
{
int16_t nwallnum, nnwallnum, curpicnum, wallpicnum, walloverpicnum, nwallpicnum;
char curxpanning, curypanning, underwall, overwall, curpal;
int8_t curshade;
walltype *wal;
sectortype *sec, *nsec;
_prwall *w;
_prsector *s, *ns;
int32_t xref, yref;
float ypancoef, dist;
int32_t i;
uint32_t invalid;
int32_t sectofwall = sectorofwall(wallnum);
if (pr_nullrender >= 3) return;
// yes, this function is messy and unefficient
// it also works, bitches
sec = &sector[sectofwall];
if (sectofwall < 0 || sectofwall >= numsectors ||
wallnum < 0 || wallnum > numwalls ||
sec->wallptr > wallnum || wallnum >= (sec->wallptr + sec->wallnum))
return; // yay, corrupt map
wal = &wall[wallnum];
nwallnum = wal->nextwall;
w = prwalls[wallnum];
s = prsectors[sectofwall];
invalid = s->invalidid;
if (nwallnum >= 0 && nwallnum < numwalls && wal->nextsector >= 0 && wal->nextsector < numsectors)
{
ns = prsectors[wal->nextsector];
invalid += ns->invalidid;
nsec = &sector[wal->nextsector];
}
else
{
ns = NULL;
nsec = NULL;
}
if (w->wall.buffer == NULL) {
w->wall.buffer = (_prvert *)Xcalloc(4, sizeof(_prvert)); // XXX
w->wall.vertcount = 4;
}
wallpicnum = wal->picnum;
DO_TILE_ANIM(wallpicnum, wallnum+16384);
walloverpicnum = wal->overpicnum;
if (walloverpicnum>=0)
DO_TILE_ANIM(walloverpicnum, wallnum+16384);
if (nwallnum >= 0 && nwallnum < numwalls)
{
nwallpicnum = wall[nwallnum].picnum;
DO_TILE_ANIM(nwallpicnum, wallnum+16384);
}
else
nwallpicnum = 0;
if ((!w->flags.empty) && (!w->flags.invalidtex) &&
(w->invalidid == invalid) &&
(wallpicnum == w->picnum_anim) &&
(walloverpicnum == w->overpicnum_anim) &&
#ifndef UNTRACKED_STRUCTS
(w->trackedrev == wallchanged[wallnum]) &&
#else
!Bmemcmp(&wal->cstat, &w->cstat, NBYTES_WALL_CSTAT_THROUGH_YPANNING) &&
#endif
((nwallnum < 0 || nwallnum > numwalls) ||
((nwallpicnum == w->nwallpicnum) &&
(wall[nwallnum].xpanning == w->nwallxpanning) &&
(wall[nwallnum].ypanning == w->nwallypanning) &&
(wall[nwallnum].cstat == w->nwallcstat) &&
(wall[nwallnum].shade == w->nwallshade))))
{
w->flags.uptodate = 1;
return; // screw you guys I'm going home
}
else
{
w->invalidid = invalid;
Bmemcpy(&w->cstat, &wal->cstat, NBYTES_WALL_CSTAT_THROUGH_YPANNING);
w->picnum_anim = wallpicnum;
w->overpicnum_anim = walloverpicnum;
#ifndef UNTRACKED_STRUCTS
w->trackedrev = wallchanged[wallnum];
#endif
if (nwallnum >= 0 && nwallnum < numwalls)
{
w->nwallpicnum = nwallpicnum;
w->nwallxpanning = wall[nwallnum].xpanning;
w->nwallypanning = wall[nwallnum].ypanning;
w->nwallcstat = wall[nwallnum].cstat;
w->nwallshade = wall[nwallnum].shade;
}
}
w->underover = underwall = overwall = 0;
if (wal->cstat & 8)
xref = 1;
else
xref = 0;
if ((unsigned)wal->nextsector >= (unsigned)numsectors || !ns)
{
Bmemcpy(w->wall.buffer, &s->floor.buffer[wallnum - sec->wallptr], sizeof(GLfloat) * 3);
Bmemcpy(&w->wall.buffer[1], &s->floor.buffer[wal->point2 - sec->wallptr], sizeof(GLfloat) * 3);
Bmemcpy(&w->wall.buffer[2], &s->ceil.buffer[wal->point2 - sec->wallptr], sizeof(GLfloat) * 3);
Bmemcpy(&w->wall.buffer[3], &s->ceil.buffer[wallnum - sec->wallptr], sizeof(GLfloat) * 3);
if (wal->nextsector < 0)
curpicnum = wallpicnum;
else
curpicnum = walloverpicnum;
w->wall.bucket = polymer_getbuildmaterial(&w->wall.material, curpicnum, wal->pal, wal->shade, sec->visibility, DAMETH_WALL);
if (wal->cstat & 4)
yref = sec->floorz;
else
yref = sec->ceilingz;
if ((wal->cstat & 32) && (wal->nextsector >= 0))
{
if ((!(wal->cstat & 2) && (wal->cstat & 4)) || ((wal->cstat & 2) && (wall[nwallnum].cstat & 4)))
yref = sec->ceilingz;
else
yref = nsec->floorz;
}
if (wal->ypanning)
// white (but not 1-way)
ypancoef = calc_ypancoef(wal->ypanning, curpicnum, !(wal->cstat & 4));
else
ypancoef = 0;
i = 0;
while (i < 4)
{
if ((i == 0) || (i == 3))
dist = (float)xref;
else
dist = (float)(xref == 0);
w->wall.buffer[i].u = ((dist * 8.0f * wal->xrepeat) + wal->xpanning) / (float)(tilesiz[curpicnum].x);
w->wall.buffer[i].v = (-(float)(yref + (w->wall.buffer[i].y * 16)) / ((tilesiz[curpicnum].y * 2048.0f) / (float)(wal->yrepeat))) + ypancoef;
if (wal->cstat & 256) w->wall.buffer[i].v = -w->wall.buffer[i].v;
i++;
}
w->underover |= 1;
}
else
{
nnwallnum = wall[nwallnum].point2;
if ((s->floor.buffer[wallnum - sec->wallptr].y < ns->floor.buffer[nnwallnum - nsec->wallptr].y) ||
(s->floor.buffer[wal->point2 - sec->wallptr].y < ns->floor.buffer[nwallnum - nsec->wallptr].y))
underwall = 1;
if ((underwall) || (wal->cstat & 16) || (wal->cstat & 32))
{
int32_t refwall;
if (s->floor.buffer[wallnum - sec->wallptr].y < ns->floor.buffer[nnwallnum - nsec->wallptr].y)
Bmemcpy(w->wall.buffer, &s->floor.buffer[wallnum - sec->wallptr], sizeof(GLfloat) * 3);
else
Bmemcpy(w->wall.buffer, &ns->floor.buffer[nnwallnum - nsec->wallptr], sizeof(GLfloat) * 3);
Bmemcpy(&w->wall.buffer[1], &s->floor.buffer[wal->point2 - sec->wallptr], sizeof(GLfloat) * 3);
Bmemcpy(&w->wall.buffer[2], &ns->floor.buffer[nwallnum - nsec->wallptr], sizeof(GLfloat) * 3);
Bmemcpy(&w->wall.buffer[3], &ns->floor.buffer[nnwallnum - nsec->wallptr], sizeof(GLfloat) * 3);
if (wal->cstat & 2)
refwall = nwallnum;
else
refwall = wallnum;
curpicnum = (wal->cstat & 2) ? nwallpicnum : wallpicnum;
curpal = wall[refwall].pal;
curshade = wall[refwall].shade;
curxpanning = wall[refwall].xpanning;
curypanning = wall[refwall].ypanning;
w->wall.bucket = polymer_getbuildmaterial(&w->wall.material, curpicnum, curpal, curshade, sec->visibility, DAMETH_WALL);
if (!(wall[refwall].cstat&4))
yref = nsec->floorz;
else
yref = sec->ceilingz;
if (curypanning)
// under
ypancoef = calc_ypancoef(curypanning, curpicnum, !(wall[refwall].cstat & 4));
else
ypancoef = 0;
i = 0;
while (i < 4)
{
if ((i == 0) || (i == 3))
dist = (float)xref;
else
dist = (float)(xref == 0);
w->wall.buffer[i].u = ((dist * 8.0f * wal->xrepeat) + curxpanning) / (float)(tilesiz[curpicnum].x);
w->wall.buffer[i].v = (-(float)(yref + (w->wall.buffer[i].y * 16)) / ((tilesiz[curpicnum].y * 2048.0f) / (float)(wal->yrepeat))) + ypancoef;
if ((!(wal->cstat & 2) && (wal->cstat & 256)) ||
((wal->cstat & 2) && (wall[nwallnum].cstat & 256)))
w->wall.buffer[i].v = -w->wall.buffer[i].v;
i++;
}
if (underwall)
w->underover |= 1;
Bmemcpy(w->mask.buffer, &w->wall.buffer[3], sizeof(GLfloat) * 5);
Bmemcpy(&w->mask.buffer[1], &w->wall.buffer[2], sizeof(GLfloat) * 5);
}
else
{
Bmemcpy(w->mask.buffer, &s->floor.buffer[wallnum - sec->wallptr], sizeof(GLfloat) * 5);
Bmemcpy(&w->mask.buffer[1], &s->floor.buffer[wal->point2 - sec->wallptr], sizeof(GLfloat) * 5);
}
if ((s->ceil.buffer[wallnum - sec->wallptr].y > ns->ceil.buffer[nnwallnum - nsec->wallptr].y) ||
(s->ceil.buffer[wal->point2 - sec->wallptr].y > ns->ceil.buffer[nwallnum - nsec->wallptr].y))
overwall = 1;
if ((overwall) || (wal->cstat & 48))
{
if (w->over.buffer == NULL) {
w->over.buffer = (_prvert *)Xmalloc(4 * sizeof(_prvert));
w->over.vertcount = 4;
}
Bmemcpy(w->over.buffer, &ns->ceil.buffer[nnwallnum - nsec->wallptr], sizeof(GLfloat) * 3);
Bmemcpy(&w->over.buffer[1], &ns->ceil.buffer[nwallnum - nsec->wallptr], sizeof(GLfloat) * 3);
if (s->ceil.buffer[wal->point2 - sec->wallptr].y > ns->ceil.buffer[nwallnum - nsec->wallptr].y)
Bmemcpy(&w->over.buffer[2], &s->ceil.buffer[wal->point2 - sec->wallptr], sizeof(GLfloat) * 3);
else
Bmemcpy(&w->over.buffer[2], &ns->ceil.buffer[nwallnum - nsec->wallptr], sizeof(GLfloat) * 3);
Bmemcpy(&w->over.buffer[3], &s->ceil.buffer[wallnum - sec->wallptr], sizeof(GLfloat) * 3);
if ((wal->cstat & 16) || (wal->overpicnum == 0))
curpicnum = wallpicnum;
else
curpicnum = wallpicnum;
w->over.bucket = polymer_getbuildmaterial(&w->over.material, curpicnum, wal->pal, wal->shade, sec->visibility, DAMETH_WALL);
if (wal->cstat & 48)
{
// mask
w->mask.bucket = polymer_getbuildmaterial(&w->mask.material, walloverpicnum, wal->pal, wal->shade, sec->visibility, DAMETH_WALL | ((wal->cstat & 48) == 48 ? DAMETH_NOMASK : DAMETH_MASK));
if (wal->cstat & 128)
{
if (wal->cstat & 512)
w->mask.material.diffusemodulation[3] = 0x55;
else
w->mask.material.diffusemodulation[3] = 0xAA;
}
}
if (wal->cstat & 4)
yref = sec->ceilingz;
else
yref = nsec->ceilingz;
if (wal->ypanning)
// over
ypancoef = calc_ypancoef(wal->ypanning, curpicnum, wal->cstat & 4);
else
ypancoef = 0;
i = 0;
while (i < 4)
{
if ((i == 0) || (i == 3))
dist = (float)xref;
else
dist = (float)(xref == 0);
w->over.buffer[i].u = ((dist * 8.0f * wal->xrepeat) + wal->xpanning) / (float)(tilesiz[curpicnum].x);
w->over.buffer[i].v = (-(float)(yref + (w->over.buffer[i].y * 16)) / ((tilesiz[curpicnum].y * 2048.0f) / (float)(wal->yrepeat))) + ypancoef;
if (wal->cstat & 256) w->over.buffer[i].v = -w->over.buffer[i].v;
i++;
}
if (overwall)
w->underover |= 2;
Bmemcpy(&w->mask.buffer[2], &w->over.buffer[1], sizeof(GLfloat) * 5);
Bmemcpy(&w->mask.buffer[3], &w->over.buffer[0], sizeof(GLfloat) * 5);
if ((wal->cstat & 16) || (wal->cstat & 32))
{
const int botSwap = (wal->cstat & 4);
if (wal->cstat & 32)
{
// 1-sided wall
if (nsec)
yref = botSwap ? sec->ceilingz : nsec->ceilingz;
else
yref = botSwap ? sec->floorz : sec->ceilingz;
}
else
{
// masked wall
if (botSwap)
yref = min(sec->floorz, nsec->floorz);
else
yref = max(sec->ceilingz, nsec->ceilingz);
}
curpicnum = walloverpicnum;
if (wal->ypanning)
// mask / 1-way
ypancoef = calc_ypancoef(wal->ypanning, curpicnum, 0);
else
ypancoef = 0;
i = 0;
while (i < 4)
{
if ((i == 0) || (i == 3))
dist = (float)xref;
else
dist = (float)(xref == 0);
w->mask.buffer[i].u = ((dist * 8.0f * wal->xrepeat) + wal->xpanning) / (float)(tilesiz[curpicnum].x);
w->mask.buffer[i].v = (-(float)(yref + (w->mask.buffer[i].y * 16)) / ((tilesiz[curpicnum].y * 2048.0f) / (float)(wal->yrepeat))) + ypancoef;
if (wal->cstat & 256) w->mask.buffer[i].v = -w->mask.buffer[i].v;
i++;
}
}
}
else
{
Bmemcpy(&w->mask.buffer[2], &s->ceil.buffer[wal->point2 - sec->wallptr], sizeof(GLfloat) * 5);
Bmemcpy(&w->mask.buffer[3], &s->ceil.buffer[wallnum - sec->wallptr], sizeof(GLfloat) * 5);
}
}
// make sure shade color handling is correct below XXX
if (wal->nextsector < 0)
Bmemcpy(w->mask.buffer, w->wall.buffer, sizeof(_prvert) * 4);
Bmemcpy(w->bigportal, &s->floor.buffer[wallnum - sec->wallptr], sizeof(GLfloat) * 3);
Bmemcpy(&w->bigportal[5], &s->floor.buffer[wal->point2 - sec->wallptr], sizeof(GLfloat) * 3);
Bmemcpy(&w->bigportal[10], &s->ceil.buffer[wal->point2 - sec->wallptr], sizeof(GLfloat) * 3);
Bmemcpy(&w->bigportal[15], &s->ceil.buffer[wallnum - sec->wallptr], sizeof(GLfloat) * 3);
//Bmemcpy(&w->cap[0], &s->ceil.buffer[wallnum - sec->wallptr], sizeof(GLfloat) * 3);
//Bmemcpy(&w->cap[3], &s->ceil.buffer[wal->point2 - sec->wallptr], sizeof(GLfloat) * 3);
//Bmemcpy(&w->cap[6], &s->ceil.buffer[wal->point2 - sec->wallptr], sizeof(GLfloat) * 3);
//Bmemcpy(&w->cap[9], &s->ceil.buffer[wallnum - sec->wallptr], sizeof(GLfloat) * 3);
//w->cap[7] += 1048576; // this number is the result of 1048574 + 2
//w->cap[10] += 1048576; // this one is arbitrary
if (w->underover & 1)
polymer_computeplane(&w->wall);
if (w->underover & 2)
polymer_computeplane(&w->over);
polymer_computeplane(&w->mask);
if ((pr_vbos > 0))
{
if (pr_nullrender < 2)
{
const GLintptr thiswalloffset = prwalldataoffset + (prwalldatasize * wallnum);
const GLintptr thisoveroffset = thiswalloffset + proneplanesize;
const GLintptr thismaskoffset = thisoveroffset + proneplanesize;
glBindBuffer(GL_ARRAY_BUFFER, prmapvbo);
glBufferSubData(GL_ARRAY_BUFFER, thiswalloffset, proneplanesize, w->wall.buffer);
glBindBuffer(GL_ARRAY_BUFFER, prmapvbo);
if (w->over.buffer)
glBufferSubData(GL_ARRAY_BUFFER, thisoveroffset, proneplanesize, w->over.buffer);
glBindBuffer(GL_ARRAY_BUFFER, prmapvbo);
glBufferSubData(GL_ARRAY_BUFFER, thismaskoffset, proneplanesize, w->mask.buffer);
glBindBuffer(GL_ARRAY_BUFFER, w->stuffvbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(GLfloat)* 5, w->bigportal);
//glBufferSubData(GL_ARRAY_BUFFER, 4 * sizeof(GLfloat)* 5, 4 * sizeof(GLfloat)* 3, w->cap);
glBindBuffer(GL_ARRAY_BUFFER, 0);
w->wall.mapvbo_vertoffset = thiswalloffset / sizeof(_prvert);
w->over.mapvbo_vertoffset = thisoveroffset / sizeof(_prvert);
w->mask.mapvbo_vertoffset = thismaskoffset / sizeof(_prvert);
}
}
else
{
w->wall.mapvbo_vertoffset = -1;
w->over.mapvbo_vertoffset = -1;
w->mask.mapvbo_vertoffset = -1;
}
w->flags.empty = 0;
w->flags.uptodate = 1;
w->flags.invalidtex = 0;
if (pr_verbosity >= 3) OSD_Printf("PR : Updated wall %i.\n", wallnum);
}
static void polymer_drawwall(int16_t sectnum, int16_t wallnum)
{
usectortype *sec;
walltype *wal;
_prwall *w;
GLubyte oldcolor[4];
int32_t parallaxedfloor = 0, parallaxedceiling = 0;
if (pr_verbosity >= 3) OSD_Printf("PR : Drawing wall %i...\n", wallnum);
sec = (usectortype *)&sector[sectnum];
wal = &wall[wallnum];
w = prwalls[wallnum];
if ((sec->floorstat & 1) && (wal->nextsector >= 0) &&
(sector[wal->nextsector].floorstat & 1))
parallaxedfloor = 1;
if ((sec->ceilingstat & 1) && (wal->nextsector >= 0) &&
(sector[wal->nextsector].ceilingstat & 1))
parallaxedceiling = 1;
calc_and_apply_fog(wal->picnum, fogshade(wal->shade, wal->pal), sec->visibility, get_floor_fogpal(sec));
if ((w->underover & 1) && (!parallaxedfloor || (searchit == 2)))
{
if (searchit == 2) {
polymer_drawsearchplane(&w->wall, oldcolor, 0x05, (GLubyte *) &wallnum);
}
else
polymer_drawplane(&w->wall);
}
if ((w->underover & 2) && (!parallaxedceiling || (searchit == 2)))
{
if (searchit == 2) {
polymer_drawsearchplane(&w->over, oldcolor, 0x00, (GLubyte *) &wallnum);
}
else
polymer_drawplane(&w->over);
}
if ((wall[wallnum].cstat & 32) && (wall[wallnum].nextsector >= 0))
{
if (searchit == 2) {
polymer_drawsearchplane(&w->mask, oldcolor, 0x04, (GLubyte *) &wallnum);
}
else
polymer_drawplane(&w->mask);
}
//if (!searchit && (sector[sectnum].ceilingstat & 1) &&
// ((wall[wallnum].nextsector < 0) ||
// !(sector[wall[wallnum].nextsector].ceilingstat & 1)))
//{
// glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
// if (pr_vbos)
// {
// glBindBuffer(GL_ARRAY_BUFFER, w->stuffvbo);
// glVertexPointer(3, GL_FLOAT, 0, (const GLvoid*)(4 * sizeof(GLfloat) * 5));
// }
// else
// glVertexPointer(3, GL_FLOAT, 0, w->cap);
// glDrawArrays(GL_QUADS, 0, 4);
// if (pr_vbos)
// glBindBuffer(GL_ARRAY_BUFFER, 0);
// glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
//}
if (pr_verbosity >= 3) OSD_Printf("PR : Finished drawing wall %i...\n", wallnum);
}
// HSR
static void polymer_computeplane(_prplane* p)
{
GLfloat vec1[5], vec2[5], norm, r;// BxN[3], NxT[3], TxB[3];
int32_t i;
_prvert* buffer;
GLfloat* plane;
if (p->indices && (p->indicescount < 3))
return; // corrupt sector (E3L4, I'm looking at you)
buffer = p->buffer;
plane = p->plane;
i = 0;
do
{
vec1[0] = buffer[(INDICE(1))].x - buffer[(INDICE(0))].x; //x1
vec1[1] = buffer[(INDICE(1))].y - buffer[(INDICE(0))].y; //y1
vec1[2] = buffer[(INDICE(1))].z - buffer[(INDICE(0))].z; //z1
vec1[3] = buffer[(INDICE(1))].u - buffer[(INDICE(0))].u; //s1
vec1[4] = buffer[(INDICE(1))].v - buffer[(INDICE(0))].v; //t1
vec2[0] = buffer[(INDICE(2))].x - buffer[(INDICE(1))].x; //x2
vec2[1] = buffer[(INDICE(2))].y - buffer[(INDICE(1))].y; //y2
vec2[2] = buffer[(INDICE(2))].z - buffer[(INDICE(1))].z; //z2
vec2[3] = buffer[(INDICE(2))].u - buffer[(INDICE(1))].u; //s2
vec2[4] = buffer[(INDICE(2))].v - buffer[(INDICE(1))].v; //t2
polymer_crossproduct(vec2, vec1, plane);
norm = plane[0] * plane[0] + plane[1] * plane[1] + plane[2] * plane[2];
// hack to work around a precision issue with slopes
if (norm >= 15000)
{
float tangent[3][3];
double det;
// normalize the normal/plane equation and calculate its plane norm
norm = -sqrt(norm);
norm = 1.0 / norm;
plane[0] *= norm;
plane[1] *= norm;
plane[2] *= norm;
plane[3] = -(plane[0] * buffer->x + plane[1] * buffer->y + plane[2] * buffer->z);
// calculate T and B
r = 1.0 / (vec1[3] * vec2[4] - vec2[3] * vec1[4]);
// tangent
tangent[0][0] = (vec2[4] * vec1[0] - vec1[4] * vec2[0]) * r;
tangent[0][1] = (vec2[4] * vec1[1] - vec1[4] * vec2[1]) * r;
tangent[0][2] = (vec2[4] * vec1[2] - vec1[4] * vec2[2]) * r;
polymer_normalize(&tangent[0][0]);
// bitangent
tangent[1][0] = (vec1[3] * vec2[0] - vec2[3] * vec1[0]) * r;
tangent[1][1] = (vec1[3] * vec2[1] - vec2[3] * vec1[1]) * r;
tangent[1][2] = (vec1[3] * vec2[2] - vec2[3] * vec1[2]) * r;
polymer_normalize(&tangent[1][0]);
// normal
tangent[2][0] = plane[0];
tangent[2][1] = plane[1];
tangent[2][2] = plane[2];
INVERT_3X3(p->tbn, det, tangent);
break;
}
i+= (p->indices) ? 3 : 1;
}
while ((p->indices && i < p->indicescount) ||
(!p->indices && i < p->vertcount));
}
static inline void polymer_crossproduct(GLfloat* in_a, GLfloat* in_b, GLfloat* out)
{
out[0] = in_a[1] * in_b[2] - in_a[2] * in_b[1];
out[1] = in_a[2] * in_b[0] - in_a[0] * in_b[2];
out[2] = in_a[0] * in_b[1] - in_a[1] * in_b[0];
}
static inline void polymer_transformpoint(const float* inpos, float* pos, float* matrix)
{
pos[0] = inpos[0] * matrix[0] +
inpos[1] * matrix[4] +
inpos[2] * matrix[8] +
+ matrix[12];
pos[1] = inpos[0] * matrix[1] +
inpos[1] * matrix[5] +
inpos[2] * matrix[9] +
+ matrix[13];
pos[2] = inpos[0] * matrix[2] +
inpos[1] * matrix[6] +
inpos[2] * matrix[10] +
+ matrix[14];
}
static inline void polymer_normalize(float* vec)
{
double norm;
norm = vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2];
norm = sqrt(norm);
norm = 1.0 / norm;
vec[0] *= norm;
vec[1] *= norm;
vec[2] *= norm;
}
static inline void polymer_pokesector(int16_t sectnum)
{
sectortype *sec = &sector[sectnum];
_prsector *s = prsectors[sectnum];
walltype *wal = &wall[sec->wallptr];
int32_t i = 0;
if (!s->flags.uptodate)
polymer_updatesector(sectnum);
do
{
if ((wal->nextsector >= 0) && (!prsectors[wal->nextsector]->flags.uptodate))
polymer_updatesector(wal->nextsector);
if (!prwalls[sec->wallptr + i]->flags.uptodate)
polymer_updatewall(sec->wallptr + i);
i++;
wal = &wall[sec->wallptr + i];
}
while (i < sec->wallnum);
}
static void polymer_extractfrustum(GLfloat* modelview, GLfloat* projection, float* frustum)
{
GLfloat matrix[16];
int32_t i;
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(projection);
glMultMatrixf(modelview);
glGetFloatv(GL_TEXTURE_MATRIX, matrix);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
i = 0;
do
{
uint32_t ii = i<<2, iii = (i<<2) + 3;
frustum[i] = matrix[iii] + matrix[ii]; // left
frustum[i + 4] = matrix[iii] - matrix[ii]; // right
frustum[i + 8] = matrix[iii] - matrix[ii + 1]; // top
frustum[i + 12] = matrix[iii] + matrix[ii + 1]; // bottom
frustum[i + 16] = matrix[iii] - matrix[ii + 2]; // far
}
while (++i < 4);
if (pr_verbosity >= 3) OSD_Printf("PR : Frustum extracted.\n");
}
static inline int32_t polymer_planeinfrustum(_prplane *plane, float* frustum)
{
int32_t i, j, k = -1;
i = 4;
do
{
int32_t ii = i * 4;
j = k = plane->vertcount - 1;
do
{
k -= ((frustum[ii + 0] * plane->buffer[j].x +
frustum[ii + 1] * plane->buffer[j].y +
frustum[ii + 2] * plane->buffer[j].z +
frustum[ii + 3]) < 0.f);
}
while (j--);
if (k == -1)
return 0; // OUT !
}
while (i--);
return 1;
}
static inline void polymer_scansprites(int16_t sectnum, uspritetype* localtsprite, int32_t* localspritesortcnt)
{
int32_t i;
spritetype *spr;
for (i = headspritesect[sectnum];i >=0;i = nextspritesect[i])
{
spr = &sprite[i];
if ((((spr->cstat&0x8000) == 0) || (showinvisibility)) &&
(spr->xrepeat > 0) && (spr->yrepeat > 0) &&
(*localspritesortcnt < maxspritesonscreen))
{
// this function's localtsprite is either the tsprite global or
// polymer_drawroom's locattsprite, so no aliasing
Bmemcpy(&localtsprite[*localspritesortcnt], spr, sizeof(spritetype));
localtsprite[*localspritesortcnt].extra = 0;
localtsprite[(*localspritesortcnt)++].owner = i;
}
}
}
void polymer_updatesprite(int32_t snum)
{
int32_t xsize, ysize, i, j;
int32_t tilexoff, tileyoff, xoff, yoff, centeryoff=0;
uspritetype *tspr = tspriteptr[snum];
float xratio, yratio, ang;
float spos[3];
const _prvert *inbuffer;
uint8_t flipu, flipv;
_prsprite *s;
const uint32_t cs = tspr->cstat;
const uint32_t alignmask = (cs & SPR_ALIGN_MASK);
const uint8_t flooraligned = (alignmask==SPR_FLOOR);
if (pr_nullrender >= 3) return;
if (pr_verbosity >= 3) OSD_Printf("PR : Updating sprite %i...\n", snum);
int32_t const curpicnum = tspr->picnum;
if (tspr->owner < 0 || curpicnum < 0) return;
s = prsprites[tspr->owner];
if (s == NULL)
{
s = prsprites[tspr->owner] = (_prsprite *)Xcalloc(sizeof(_prsprite), 1);
s->plane.buffer = (_prvert *)Xcalloc(4, sizeof(_prvert)); // XXX
s->plane.vertcount = 4;
s->plane.mapvbo_vertoffset = -1;
s->hash = 0xDEADBEEF;
}
if ((tspr->cstat & 48) && (pr_vbos > 0) && !s->plane.vbo)
{
if (pr_nullrender < 2)
{
glGenBuffers(1, &s->plane.vbo);
glBindBuffer(GL_ARRAY_BUFFER, s->plane.vbo);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(_prvert), NULL, mapvbousage);
}
}
if (tspr->cstat & 48 && searchit != 2)
{
uint32_t const changed = XXH32((uint8_t *) tspr, offsetof(spritetype, owner), 0xDEADBEEF);
if (changed == s->hash)
return;
s->hash = changed;
}
polymer_getbuildmaterial(&s->plane.material, curpicnum, tspr->pal, tspr->shade,
sector[tspr->sectnum].visibility, DAMETH_MASK | DAMETH_CLAMPED);
if (tspr->cstat & 2)
{
if (tspr->cstat & 512)
s->plane.material.diffusemodulation[3] = 0x55;
else
s->plane.material.diffusemodulation[3] = 0xAA;
}
float f = s->plane.material.diffusemodulation[3] * (1.0f - spriteext[tspr->owner].alpha);
s->plane.material.diffusemodulation[3] = (GLubyte)f;
if (searchit == 2)
{
polymer_setupdiffusemodulation(&s->plane, 0x03, (GLubyte *) &tspr->owner);
s->hash = 0xDEADBEEF;
}
if (((tspr->cstat>>4) & 3) == 0)
xratio = (float)(tspr->xrepeat) * 0.20f; // 32 / 160
else
xratio = (float)(tspr->xrepeat) * 0.25f;
yratio = (float)(tspr->yrepeat) * 0.25f;
xsize = tilesiz[curpicnum].x;
ysize = tilesiz[curpicnum].y;
if (usehightile && h_xsize[curpicnum])
{
xsize = h_xsize[curpicnum];
ysize = h_ysize[curpicnum];
}
xsize = (int32_t)(xsize * xratio);
ysize = (int32_t)(ysize * yratio);
tilexoff = (int32_t)tspr->xoffset;
tileyoff = (int32_t)tspr->yoffset;
tilexoff += (usehightile && h_xsize[curpicnum]) ? h_xoffs[curpicnum] : picanm[curpicnum].xofs;
tileyoff += (usehightile && h_xsize[curpicnum]) ? h_yoffs[curpicnum] : picanm[curpicnum].yofs;
xoff = (int32_t)(tilexoff * xratio);
yoff = (int32_t)(tileyoff * yratio);
if ((tspr->cstat & 128) && !flooraligned)
{
if (alignmask == 0)
yoff -= ysize / 2;
else
centeryoff = ysize / 2;
}
spos[0] = (float)tspr->y;
spos[1] = -(float)(tspr->z) / 16.0f;
spos[2] = -(float)tspr->x;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
inbuffer = vertsprite;
{
const uint8_t xflip = !!(cs & SPR_XFLIP);
const uint8_t yflip = !!(cs & SPR_YFLIP);
// Initially set flipu and flipv.
flipu = (xflip ^ flooraligned);
flipv = (yflip && !flooraligned);
if (pr_billboardingmode && alignmask==0)
{
// do surgery on the face tspr to make it look like a wall sprite
tspr->cstat |= 16;
tspr->ang = (fix16_to_int(viewangle) + 1024) & 2047;
}
if (flipu)
xoff = -xoff;
if (yflip && alignmask!=0)
yoff = -yoff;
}
switch (tspr->cstat & SPR_ALIGN_MASK)
{
case 0:
ang = (float)((fix16_to_int(viewangle)) & 2047) * (360.f/2048.f);
glTranslatef(spos[0], spos[1], spos[2]);
glRotatef(-ang, 0.0f, 1.0f, 0.0f);
glRotatef(-horizang, 1.0f, 0.0f, 0.0f);
glTranslatef((float)(-xoff), (float)(yoff), 0.0f);
glScalef((float)(xsize), (float)(ysize), 1.0f);
break;
case SPR_WALL:
ang = (float)((tspr->ang + 1024) & 2047) * (360.f/2048.f);
glTranslatef(spos[0], spos[1], spos[2]);
glRotatef(-ang, 0.0f, 1.0f, 0.0f);
glTranslatef((float)(-xoff), (float)(yoff-centeryoff), 0.0f);
glScalef((float)(xsize), (float)(ysize), 1.0f);
break;
case SPR_FLOOR:
ang = (float)((tspr->ang + 1024) & 2047) * (360.f/2048.f);
glTranslatef(spos[0], spos[1], spos[2]);
glRotatef(-ang, 0.0f, 1.0f, 0.0f);
glTranslatef((float)(-xoff), 1.0f, (float)(yoff));
glScalef((float)(xsize), 1.0f, (float)(ysize));
inbuffer = horizsprite;
break;
}
glGetFloatv(GL_MODELVIEW_MATRIX, spritemodelview);
glPopMatrix();
Bmemcpy(s->plane.buffer, inbuffer, sizeof(_prvert) * 4);
if (flipu || flipv)
{
i = 0;
do
{
if (flipu)
s->plane.buffer[i].u =
(s->plane.buffer[i].u - 1.0f) * -1.0f;
if (flipv)
s->plane.buffer[i].v =
(s->plane.buffer[i].v - 1.0f) * -1.0f;
}
while (++i < 4);
}
i = 0;
do
polymer_transformpoint(&inbuffer[i].x, &s->plane.buffer[i].x, spritemodelview);
while (++i < 4);
polymer_computeplane(&s->plane);
if (pr_nullrender < 2)
{
if (alignmask && (pr_vbos > 0))
{
glBindBuffer(GL_ARRAY_BUFFER, s->plane.vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(_prvert), s->plane.buffer);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
else if (s->plane.vbo) // clean up the vbo if a wall/floor sprite becomes a face sprite
{
glDeleteBuffers(1, &s->plane.vbo);
s->plane.vbo = 0;
}
}
if (alignmask)
{
int32_t curpriority = 0;
polymer_resetplanelights(&s->plane);
while (curpriority < pr_maxlightpriority)
{
i = j = 0;
while (j < lightcount)
{
while (!prlights[i].flags.active)
i++;
if (prlights[i].priority != curpriority)
{
i++;
j++;
continue;
}
if (polymer_planeinlight(&s->plane, &prlights[i]))
polymer_addplanelight(&s->plane, i);
i++;
j++;
}
curpriority++;
}
}
}
// SKIES
static void polymer_getsky(void)
{
int32_t i;
i = 0;
while (i < numsectors)
{
if (sector[i].ceilingstat & 1)
{
int32_t horizfrac;
cursky = sector[i].ceilingpicnum;
curskypal = sector[i].ceilingpal;
curskyshade = sector[i].ceilingshade;
getpsky(cursky, &horizfrac, NULL, NULL, NULL);
switch (horizfrac)
{
case 0:
// psky always at same level wrt screen
curskyangmul = 0.f;
break;
case 65536:
// psky horiz follows camera horiz
curskyangmul = 1.f;
break;
default:
// sky has hard-coded parallax
curskyangmul = 1/DEFAULT_ARTSKY_ANGDIV;
break;
}
return;
}
i++;
}
}
void polymer_drawsky(int16_t tilenum, char palnum, int8_t shade)
{
float pos[3];
pthtyp* pth;
pos[0] = fglobalposy;
pos[1] = fglobalposz * (-1.f/16.f);
pos[2] = -fglobalposx;
glPushMatrix();
glLoadIdentity();
glLoadMatrixf(curskymodelviewmatrix);
glTranslatef(pos[0], pos[1], pos[2]);
glScalef(1000.0f, 1000.0f, 1000.0f);
drawingskybox = 1;
pth = texcache_fetch(tilenum, 0, 0, DAMETH_NOMASK);
drawingskybox = 0;
if (pth && (pth->flags & PTH_SKYBOX))
polymer_drawskybox(tilenum, palnum, shade);
else
polymer_drawartsky(tilenum, palnum, shade);
glPopMatrix();
}
static void polymer_initartsky(void)
{
GLfloat halfsqrt2 = 0.70710678f;
artskydata[0] = -1.0f; artskydata[1] = 0.0f; // 0
artskydata[2] = -halfsqrt2; artskydata[3] = halfsqrt2; // 1
artskydata[4] = 0.0f; artskydata[5] = 1.0f; // 2
artskydata[6] = halfsqrt2; artskydata[7] = halfsqrt2; // 3
artskydata[8] = 1.0f; artskydata[9] = 0.0f; // 4
artskydata[10] = halfsqrt2; artskydata[11] = -halfsqrt2; // 5
artskydata[12] = 0.0f; artskydata[13] = -1.0f; // 6
artskydata[14] = -halfsqrt2; artskydata[15] = -halfsqrt2; // 7
}
static void polymer_drawartsky(int16_t tilenum, char palnum, int8_t shade)
{
pthtyp* pth;
GLuint glpics[PSKYOFF_MAX+1];
GLfloat glcolors[PSKYOFF_MAX+1][3];
int32_t i, j;
GLfloat height = 2.45f / 2.0f;
int32_t dapskybits;
const int8_t *dapskyoff = getpsky(tilenum, NULL, &dapskybits, NULL, NULL);
const int32_t numskytilesm1 = (1<<dapskybits)-1;
i = 0;
while (i <= PSKYOFF_MAX)
{
int16_t picnum = tilenum + i;
// Prevent oob by bad user input:
if (picnum >= MAXTILES)
picnum = MAXTILES-1;
DO_TILE_ANIM(picnum, 0);
if (!waloff[picnum])
tileLoad(picnum);
pth = texcache_fetch(picnum, palnum, 0, DAMETH_NOMASK);
glpics[i] = pth ? pth->glpic : 0;
glcolors[i][0] = glcolors[i][1] = glcolors[i][2] = getshadefactor(shade);
if (pth)
{
// tinting
polytintflags_t const tintflags = hictinting[palnum].f;
if (!(tintflags & HICTINT_PRECOMPUTED))
{
if (pth->flags & PTH_HIGHTILE)
{
if (pth->palnum != palnum || (pth->effects & HICTINT_IN_MEMORY) || (tintflags & HICTINT_APPLYOVERALTPAL))
hictinting_apply(glcolors[i], palnum);
}
else if (tintflags & (HICTINT_USEONART|HICTINT_ALWAYSUSEART))
hictinting_apply(glcolors[i], palnum);
}
// global tinting
if ((pth->flags & PTH_HIGHTILE) && have_basepal_tint())
hictinting_apply(glcolors[i], MAXPALOOKUPS-1);
globaltinting_apply(glcolors[i]);
}
i++;
}
i = 0;
j = 8; // In Polymer, an ART sky has always 8 sides...
while (i < j)
{
GLint oldswrap;
// ... but in case a multi-psky specifies less than 8, repeat cyclically:
const int8_t tileofs = dapskyoff[i&numskytilesm1];
glColor4f(glcolors[tileofs][0], glcolors[tileofs][1], glcolors[tileofs][2], 1.0f);
glBindTexture(GL_TEXTURE_2D, glpics[tileofs]);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &oldswrap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP);
polymer_drawartskyquad(i, (i + 1) & (j - 1), height);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, oldswrap);
i++;
}
}
static void polymer_drawartskyquad(int32_t p1, int32_t p2, GLfloat height)
{
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
//OSD_Printf("PR: drawing %f %f %f\n", skybox[(p1 * 2) + 1], height, skybox[p1 * 2]);
glVertex3f(artskydata[(p1 * 2) + 1], height, artskydata[p1 * 2]);
glTexCoord2f(0.0f, 1.0f);
//OSD_Printf("PR: drawing %f %f %f\n", skybox[(p1 * 2) + 1], -height, skybox[p1 * 2]);
glVertex3f(artskydata[(p1 * 2) + 1], -height, artskydata[p1 * 2]);
glTexCoord2f(1.0f, 1.0f);
//OSD_Printf("PR: drawing %f %f %f\n", skybox[(p2 * 2) + 1], -height, skybox[p2 * 2]);
glVertex3f(artskydata[(p2 * 2) + 1], -height, artskydata[p2 * 2]);
glTexCoord2f(1.0f, 0.0f);
//OSD_Printf("PR: drawing %f %f %f\n", skybox[(p2 * 2) + 1], height, skybox[p2 * 2]);
glVertex3f(artskydata[(p2 * 2) + 1], height, artskydata[p2 * 2]);
glEnd();
}
static void polymer_drawskybox(int16_t tilenum, char palnum, int8_t shade)
{
pthtyp* pth;
int32_t i;
GLfloat color[3];
if ((pr_vbos > 0) && (skyboxdatavbo == 0))
{
glGenBuffers(1, &skyboxdatavbo);
glBindBuffer(GL_ARRAY_BUFFER, skyboxdatavbo);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(GLfloat) * 5 * 6, skyboxdata, modelvbousage);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
if (pr_vbos > 0)
glBindBuffer(GL_ARRAY_BUFFER, skyboxdatavbo);
DO_TILE_ANIM(tilenum, 0);
i = 0;
while (i < 6)
{
drawingskybox = i + 1;
pth = texcache_fetch(tilenum, palnum, 0, DAMETH_CLAMPED);
color[0] = color[1] = color[2] = getshadefactor(shade);
if (pth)
{
// tinting
polytintflags_t const tintflags = hictinting[palnum].f;
if (!(tintflags & HICTINT_PRECOMPUTED))
{
if (pth->flags & PTH_HIGHTILE)
{
if (pth->palnum != palnum || (pth->effects & HICTINT_IN_MEMORY) || (tintflags & HICTINT_APPLYOVERALTPAL))
hictinting_apply(color, palnum);
}
else if (tintflags & (HICTINT_USEONART|HICTINT_ALWAYSUSEART))
hictinting_apply(color, palnum);
}
// global tinting
if ((pth->flags & PTH_HIGHTILE) && have_basepal_tint())
hictinting_apply(color, MAXPALOOKUPS-1);
globaltinting_apply(color);
}
glColor4f(color[0], color[1], color[2], 1.0);
glBindTexture(GL_TEXTURE_2D, pth ? pth->glpic : 0);
if (pr_vbos > 0)
{
glVertexPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), (GLfloat*)(4 * 5 * i * sizeof(GLfloat)));
glTexCoordPointer(2, GL_FLOAT, 5 * sizeof(GLfloat), (GLfloat*)(((4 * 5 * i) + 3) * sizeof(GLfloat)));
} else {
glVertexPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), &skyboxdata[4 * 5 * i]);
glTexCoordPointer(2, GL_FLOAT, 5 * sizeof(GLfloat), &skyboxdata[3 + (4 * 5 * i)]);
}
glDrawArrays(GL_QUADS, 0, 4);
i++;
}
drawingskybox = 0;
if (pr_vbos > 0)
glBindBuffer(GL_ARRAY_BUFFER, 0);
return;
}
// MDSPRITES
static void polymer_drawmdsprite(uspritetype *tspr)
{
md3model_t* m;
mdskinmap_t* sk;
float *v0, *v1;
md3surf_t *s;
char targetpal, usinghighpal, foundpalskin;
float spos2[3], spos[3], tspos[3], lpos[3], tlpos[3], vec[3], mat[4][4];
float ang;
float scale;
double det;
int32_t surfi, i, j;
GLubyte* color;
int32_t materialbits;
float sradius, lradius;
int16_t modellights[PR_MAXLIGHTS];
char modellightcount;
uint8_t curpriority;
uint8_t lpal = (tspr->owner >= MAXSPRITES) ? tspr->pal : sprite[tspr->owner].pal;
m = (md3model_t*)models[tile2model[Ptile2tile(tspr->picnum,lpal)].modelid];
updateanimation((md2model_t *)m,tspr,lpal);
if ((pr_vbos > 1) && (m->indices == NULL))
polymer_loadmodelvbos(m);
// Hackish, but that means it's a model drawn by rotatesprite.
if (tspriteptr[maxspritesonscreen] == tspr) {
float x, y, z;
spos[0] = fglobalposy;
spos[1] = fglobalposz * (-1.f/16.f);
spos[2] = -fglobalposx;
// The coordinates are actually floats disguised as int in this case
memcpy(&x, &tspr->x, sizeof(float));
memcpy(&y, &tspr->y, sizeof(float));
memcpy(&z, &tspr->z, sizeof(float));
spos2[0] = y - globalposy;
spos2[1] = (z - fglobalposz) * (-1.f/16.f);
spos2[2] = fglobalposx - x;
} else {
spos[0] = (float)tspr->y;
spos[1] = -(float)(tspr->z) / 16.0f;
spos[2] = -(float)tspr->x;
spos2[0] = spos2[1] = spos2[2] = 0.0f;
}
ang = (float)((tspr->ang+spriteext[tspr->owner].angoff) & 2047) * (360.f/2048.f);
ang -= 90.0f;
if (((tspr->cstat>>4) & 3) == 2)
ang -= 90.0f;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
scale = (1.0/4.0);
scale *= m->scale;
if (pr_overridemodelscale) {
scale *= pr_overridemodelscale;
} else {
scale *= m->bscale;
}
if (tspriteptr[maxspritesonscreen] == tspr) {
float playerang, radplayerang, cosminusradplayerang, sinminusradplayerang, hudzoom;
playerang = (globalang & 2047) * (360.f/2048.f) - 90.0f;
radplayerang = (globalang & 2047) * (2.0f * fPI / 2048.0f);
cosminusradplayerang = cos(-radplayerang);
sinminusradplayerang = sin(-radplayerang);
hudzoom = 65536.0 / spriteext[tspr->owner].offset.z;
glTranslatef(spos[0], spos[1], spos[2]);
glRotatef(horizang, -cosminusradplayerang, 0.0f, sinminusradplayerang);
glRotatef(spriteext[tspr->owner].roll * (360.f/2048.f), sinminusradplayerang, 0.0f, cosminusradplayerang);
glRotatef(-playerang, 0.0f, 1.0f, 0.0f);
glScalef(hudzoom, 1.0f, 1.0f);
glRotatef(playerang, 0.0f, 1.0f, 0.0f);
glTranslatef(spos2[0], spos2[1], spos2[2]);
glRotatef(-ang, 0.0f, 1.0f, 0.0f);
} else {
glTranslatef(spos[0], spos[1], spos[2]);
glRotatef(-ang, 0.0f, 1.0f, 0.0f);
}
if (((tspr->cstat>>4) & 3) == 2)
{
glTranslatef(0.0f, 0.0, -(float)(tilesiz[tspr->picnum].y * tspr->yrepeat) / 8.0f);
glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
}
else
glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
if ((tspr->cstat & 128) && (((tspr->cstat>>4) & 3) != 2))
glTranslatef(0.0f, 0.0, -(float)(tilesiz[tspr->picnum].y * tspr->yrepeat) / 8.0f);
// yoffset differs from zadd in that it does not follow cstat&8 y-flipping
glTranslatef(0.0f, 0.0, m->yoffset * 64 * scale * tspr->yrepeat);
if (tspr->cstat & 8)
{
glTranslatef(0.0f, 0.0, (float)(tilesiz[tspr->picnum].y * tspr->yrepeat) / 4.0f);
glScalef(1.0f, 1.0f, -1.0f);
}
if (tspr->cstat & 4)
glScalef(1.0f, -1.0f, 1.0f);
if (!(tspr->cstat & 4) != !(tspr->cstat & 8)) {
// Only inverting one coordinate will reverse the winding order of
// faces, so we need to account for that when culling.
SWITCH_CULL_DIRECTION;
}
glScalef(scale * tspr->xrepeat, scale * tspr->xrepeat, scale * tspr->yrepeat);
glTranslatef(0.0f, 0.0, m->zadd * 64);
// scripted model rotation
if (tspr->owner < MAXSPRITES &&
(spriteext[tspr->owner].pitch || spriteext[tspr->owner].roll))
{
float pitchang, rollang, offsets[3];
pitchang = (float)(spriteext[tspr->owner].pitch) * (360.f/2048.f);
rollang = (float)(spriteext[tspr->owner].roll) * (360.f/2048.f);
offsets[0] = -spriteext[tspr->owner].offset.x / (scale * tspr->xrepeat);
offsets[1] = -spriteext[tspr->owner].offset.y / (scale * tspr->xrepeat);
offsets[2] = (float)(spriteext[tspr->owner].offset.z) / 16.0f / (scale * tspr->yrepeat);
glTranslatef(-offsets[0], -offsets[1], -offsets[2]);
glRotatef(pitchang, 0.0f, 1.0f, 0.0f);
glRotatef(rollang, -1.0f, 0.0f, 0.0f);
glTranslatef(offsets[0], offsets[1], offsets[2]);
}
glGetFloatv(GL_MODELVIEW_MATRIX, spritemodelview);
glPopMatrix();
glPushMatrix();
glMultMatrixf(spritemodelview);
// invert this matrix to get the polymer -> mdsprite space
memcpy(mat, spritemodelview, sizeof(float) * 16);
INVERT_4X4(mdspritespace, det, mat);
// debug code for drawing the model bounding sphere
// glDisable(GL_TEXTURE_2D);
// glBegin(GL_LINES);
// glColor4f(1.0, 0.0, 0.0, 1.0);
// glVertex3f(m->head.frames[m->cframe].cen.x,
// m->head.frames[m->cframe].cen.y,
// m->head.frames[m->cframe].cen.z);
// glVertex3f(m->head.frames[m->cframe].cen.x + m->head.frames[m->cframe].r,
// m->head.frames[m->cframe].cen.y,
// m->head.frames[m->cframe].cen.z);
// glColor4f(0.0, 1.0, 0.0, 1.0);
// glVertex3f(m->head.frames[m->cframe].cen.x,
// m->head.frames[m->cframe].cen.y,
// m->head.frames[m->cframe].cen.z);
// glVertex3f(m->head.frames[m->cframe].cen.x,
// m->head.frames[m->cframe].cen.y + m->head.frames[m->cframe].r,
// m->head.frames[m->cframe].cen.z);
// glColor4f(0.0, 0.0, 1.0, 1.0);
// glVertex3f(m->head.frames[m->cframe].cen.x,
// m->head.frames[m->cframe].cen.y,
// m->head.frames[m->cframe].cen.z);
// glVertex3f(m->head.frames[m->cframe].cen.x,
// m->head.frames[m->cframe].cen.y,
// m->head.frames[m->cframe].cen.z + m->head.frames[m->cframe].r);
// glEnd();
// glEnable(GL_TEXTURE_2D);
polymer_getscratchmaterial(&mdspritematerial);
color = mdspritematerial.diffusemodulation;
color[0] = color[1] = color[2] =
(GLubyte)(((float)(numshades-min(max((tspr->shade * shadescale)+m->shadeoff,0),numshades)))/((float)numshades) * 0xFF);
usinghighpal = (pr_highpalookups &&
prhighpalookups[curbasepal][tspr->pal].map);
// tinting
polytintflags_t const tintflags = hictinting[tspr->pal].f;
if (!usinghighpal && !(tintflags & HICTINT_PRECOMPUTED))
{
if (!(m->flags&1))
hictinting_apply_ub(color, tspr->pal);
else globalnoeffect=1; //mdloadskin reads this
}
// global tinting
if (!usinghighpal && have_basepal_tint())
hictinting_apply_ub(color, MAXPALOOKUPS-1);
globaltinting_apply_ub(color);
if (tspr->cstat & 2)
{
if (!(tspr->cstat&512))
color[3] = 0xAA;
else
color[3] = 0x55;
} else
color[3] = 0xFF;
{
double f = color[3] * (1.0f - spriteext[tspr->owner].alpha);
color[3] = (GLubyte)f;
}
if (searchit == 2)
{
color[0] = 0x03;
color[1] = ((GLubyte *)(&tspr->owner))[0];
color[2] = ((GLubyte *)(&tspr->owner))[1];
color[3] = 0xFF;
}
if (pr_gpusmoothing)
mdspritematerial.frameprogress = m->interpol;
mdspritematerial.mdspritespace = GL_TRUE;
modellightcount = 0;
curpriority = 0;
// light culling
if (lightcount && (!depth || mirrors[depth-1].plane))
{
sradius = (m->head.frames[m->cframe].r * (1 - m->interpol)) +
(m->head.frames[m->nframe].r * m->interpol);
sradius *= max(scale * tspr->xrepeat, scale * tspr->yrepeat);
sradius /= 1000.0f;
spos[0] = (m->head.frames[m->cframe].cen.x * (1 - m->interpol)) +
(m->head.frames[m->nframe].cen.x * m->interpol);
spos[1] = (m->head.frames[m->cframe].cen.y * (1 - m->interpol)) +
(m->head.frames[m->nframe].cen.y * m->interpol);
spos[2] = (m->head.frames[m->cframe].cen.z * (1 - m->interpol)) +
(m->head.frames[m->nframe].cen.z * m->interpol);
polymer_transformpoint(spos, tspos, spritemodelview);
polymer_transformpoint(tspos, spos, rootmodelviewmatrix);
while (curpriority < pr_maxlightpriority)
{
i = j = 0;
while (j < lightcount)
{
while (!prlights[i].flags.active)
i++;
if (prlights[i].priority != curpriority)
{
i++;
j++;
continue;
}
lradius = prlights[i].range / 1000.0f;
lpos[0] = (float)prlights[i].y;
lpos[1] = -(float)prlights[i].z / 16.0f;
lpos[2] = -(float)prlights[i].x;
polymer_transformpoint(lpos, tlpos, rootmodelviewmatrix);
vec[0] = tlpos[0] - spos[0];
vec[0] *= vec[0];
vec[1] = tlpos[1] - spos[1];
vec[1] *= vec[1];
vec[2] = tlpos[2] - spos[2];
vec[2] *= vec[2];
if ((vec[0] + vec[1] + vec[2]) <= ((sradius+lradius) * (sradius+lradius)))
modellights[modellightcount++] = i;
i++;
j++;
}
curpriority++;
}
}
for (surfi=0;surfi<m->head.numsurfs;surfi++)
{
s = &m->head.surfs[surfi];
v0 = &s->geometry[m->cframe*s->numverts*15];
v1 = &s->geometry[m->nframe*s->numverts*15];
// debug code for drawing model normals
// glDisable(GL_TEXTURE_2D);
// glBegin(GL_LINES);
// glColor4f(1.0, 1.0, 1.0, 1.0);
//
// int i = 0;
// while (i < s->numverts)
// {
// glVertex3f(v0[(i * 6) + 0],
// v0[(i * 6) + 1],
// v0[(i * 6) + 2]);
// glVertex3f(v0[(i * 6) + 0] + v0[(i * 6) + 3] * 100,
// v0[(i * 6) + 1] + v0[(i * 6) + 4] * 100,
// v0[(i * 6) + 2] + v0[(i * 6) + 5] * 100);
// i++;
// }
// glEnd();
// glEnable(GL_TEXTURE_2D);
targetpal = tspr->pal;
foundpalskin = 0;
for (sk = m->skinmap; sk; sk = sk->next)
if ((int32_t)sk->palette == tspr->pal &&
sk->skinnum == tile2model[Ptile2tile(tspr->picnum,lpal)].skinnum &&
sk->surfnum == surfi)
{
if (sk->specpower != 1.0)
mdspritematerial.specmaterial[0] = sk->specpower;
mdspritematerial.specmaterial[1] = sk->specfactor;
foundpalskin = 1;
}
// If we have a global palette tint, the palskin won't do us any good
if (curbasepal)
foundpalskin = 0;
if (!foundpalskin && usinghighpal) {
// We don't have a specific skin defined for this palette
// Use the base skin instead and plug in our highpalookup map
targetpal = 0;
mdspritematerial.highpalookupmap = prhighpalookups[curbasepal][tspr->pal].map;
}
mdspritematerial.diffusemap =
mdloadskin((md2model_t *)m,tile2model[Ptile2tile(tspr->picnum,lpal)].skinnum,targetpal,surfi);
if (!mdspritematerial.diffusemap)
continue;
if (!(tspr->extra&TSPR_EXTRA_MDHACK))
{
mdspritematerial.detailmap =
mdloadskin((md2model_t *)m,tile2model[Ptile2tile(tspr->picnum,lpal)].skinnum,DETAILPAL,surfi);
for (sk = m->skinmap; sk; sk = sk->next)
if ((int32_t)sk->palette == DETAILPAL &&
sk->skinnum == tile2model[Ptile2tile(tspr->picnum,lpal)].skinnum &&
sk->surfnum == surfi)
mdspritematerial.detailscale[0] = mdspritematerial.detailscale[1] = sk->param;
mdspritematerial.specmap =
mdloadskin((md2model_t *)m,tile2model[Ptile2tile(tspr->picnum,lpal)].skinnum,SPECULARPAL,surfi);
mdspritematerial.normalmap =
mdloadskin((md2model_t *)m,tile2model[Ptile2tile(tspr->picnum,lpal)].skinnum,NORMALPAL,surfi);
for (sk = m->skinmap; sk; sk = sk->next)
if ((int32_t)sk->palette == NORMALPAL &&
sk->skinnum == tile2model[Ptile2tile(tspr->picnum,lpal)].skinnum &&
sk->surfnum == surfi) {
mdspritematerial.normalbias[0] = sk->specpower;
mdspritematerial.normalbias[1] = sk->specfactor;
}
mdspritematerial.glowmap =
mdloadskin((md2model_t *)m,tile2model[Ptile2tile(tspr->picnum,lpal)].skinnum,GLOWPAL,surfi);
}
glEnableClientState(GL_NORMAL_ARRAY);
if (pr_vbos > 1)
{
glBindBuffer(GL_ARRAY_BUFFER, m->texcoords[surfi]);
glTexCoordPointer(2, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, m->geometry[surfi]);
glVertexPointer(3, GL_FLOAT, sizeof(float) * 15, (GLfloat*)(m->cframe * s->numverts * sizeof(float) * 15));
glNormalPointer(GL_FLOAT, sizeof(float) * 15, (GLfloat*)(m->cframe * s->numverts * sizeof(float) * 15) + 3);
mdspritematerial.tbn = (GLfloat*)(m->cframe * s->numverts * sizeof(float) * 15) + 6;
if (pr_gpusmoothing) {
mdspritematerial.nextframedata = (GLfloat*)(m->nframe * s->numverts * sizeof(float) * 15);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->indices[surfi]);
curlight = 0;
do {
materialbits = polymer_bindmaterial(&mdspritematerial, modellights, modellightcount);
glDrawElements(GL_TRIANGLES, s->numtris * 3, GL_UNSIGNED_INT, 0);
polymer_unbindmaterial(materialbits);
} while ((++curlight < modellightcount) && (curlight < pr_maxlightpasses));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
else
{
glVertexPointer(3, GL_FLOAT, sizeof(float) * 15, v0);
glNormalPointer(GL_FLOAT, sizeof(float) * 15, v0 + 3);
glTexCoordPointer(2, GL_FLOAT, 0, s->uv);
mdspritematerial.tbn = v0 + 6;
if (pr_gpusmoothing) {
mdspritematerial.nextframedata = (GLfloat*)(v1);
}
curlight = 0;
do {
materialbits = polymer_bindmaterial(&mdspritematerial, modellights, modellightcount);
glDrawElements(GL_TRIANGLES, s->numtris * 3, GL_UNSIGNED_INT, s->tris);
polymer_unbindmaterial(materialbits);
} while ((++curlight < modellightcount) && (curlight < pr_maxlightpasses));
}
glDisableClientState(GL_NORMAL_ARRAY);
}
glPopMatrix();
if (!(tspr->cstat & 4) != !(tspr->cstat & 8)) {
SWITCH_CULL_DIRECTION;
}
globalnoeffect = 0;
}
static void polymer_loadmodelvbos(md3model_t* m)
{
int32_t i;
md3surf_t *s;
m->indices = (GLuint *)Xmalloc(m->head.numsurfs * sizeof(GLuint));
m->texcoords = (GLuint *)Xmalloc(m->head.numsurfs * sizeof(GLuint));
m->geometry = (GLuint *)Xmalloc(m->head.numsurfs * sizeof(GLuint));
glGenBuffers(m->head.numsurfs, m->indices);
glGenBuffers(m->head.numsurfs, m->texcoords);
glGenBuffers(m->head.numsurfs, m->geometry);
i = 0;
while (i < m->head.numsurfs)
{
s = &m->head.surfs[i];
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->indices[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, s->numtris * sizeof(md3tri_t), s->tris, modelvbousage);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, m->texcoords[i]);
glBufferData(GL_ARRAY_BUFFER, s->numverts * sizeof(md3uv_t), s->uv, modelvbousage);
glBindBuffer(GL_ARRAY_BUFFER, m->geometry[i]);
glBufferData(GL_ARRAY_BUFFER, s->numframes * s->numverts * sizeof(float) * (15), s->geometry, modelvbousage);
glBindBuffer(GL_ARRAY_BUFFER, 0);
i++;
}
}
// MATERIALS
static void polymer_getscratchmaterial(_prmaterial* material)
{
// this function returns a material that won't validate any bits
// make sure to keep it up to date with the validation logic in bindmaterial
// PR_BIT_ANIM_INTERPOLATION
material->frameprogress = 0.0f;
material->nextframedata = (float*)-1;
// PR_BIT_NORMAL_MAP
material->normalmap = 0;
material->normalbias[0] = material->normalbias[1] = 0.0f;
material->tbn = NULL;
// PR_BIT_ART_MAP
material->artmap = 0;
material->basepalmap = 0;
material->lookupmap = 0;
// PR_BIT_DIFFUSE_MAP
material->diffusemap = 0;
material->diffusescale[0] = material->diffusescale[1] = 1.0f;
// PR_BIT_HIGHPALOOKUP_MAP
material->highpalookupmap = 0;
// PR_BIT_DIFFUSE_DETAIL_MAP
material->detailmap = 0;
material->detailscale[0] = material->detailscale[1] = 1.0f;
// PR_BIT_DIFFUSE_MODULATION
material->diffusemodulation[0] =
material->diffusemodulation[1] =
material->diffusemodulation[2] =
material->diffusemodulation[3] = 0xFF;
// PR_BIT_SPECULAR_MAP
material->specmap = 0;
// PR_BIT_SPECULAR_MATERIAL
material->specmaterial[0] = 15.0f;
material->specmaterial[1] = 1.0f;
// PR_BIT_MIRROR_MAP
material->mirrormap = 0;
// PR_BIT_GLOW_MAP
material->glowmap = 0;
// PR_BIT_PROJECTION_MAP
material->mdspritespace = GL_FALSE;
}
static void polymer_setupartmap(int16_t tilenum, char pal)
{
if (!prartmaps[tilenum]) {
char *tilebuffer = (char *) waloff[tilenum];
char *tempbuffer = (char *) Xmalloc(tilesiz[tilenum].x * tilesiz[tilenum].y);
int i, j, k;
i = k = 0;
while (i < tilesiz[tilenum].y) {
j = 0;
while (j < tilesiz[tilenum].x) {
tempbuffer[k] = tilebuffer[(j * tilesiz[tilenum].y) + i];
k++;
j++;
}
i++;
}
glGenTextures(1, &prartmaps[tilenum]);
glBindTexture(GL_TEXTURE_2D, prartmaps[tilenum]);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RED,
tilesiz[tilenum].x,
tilesiz[tilenum].y,
0,
GL_RED,
GL_UNSIGNED_BYTE,
tempbuffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindTexture(GL_TEXTURE_2D, 0);
Bfree(tempbuffer);
}
if (!prbasepalmaps[curbasepal]) {
glGenTextures(1, &prbasepalmaps[curbasepal]);
glBindTexture(GL_TEXTURE_2D, prbasepalmaps[curbasepal]);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB,
256,
1,
0,
GL_RGB,
GL_UNSIGNED_BYTE,
basepaltable[curbasepal]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP);
glBindTexture(GL_TEXTURE_2D, 0);
}
if (!prlookups[pal]) {
glGenTextures(1, &prlookups[pal]);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, prlookups[pal]);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
0,
GL_RED,
256,
numshades,
0,
GL_RED,
GL_UNSIGNED_BYTE,
palookup[pal]);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
}
}
static _prbucket* polymer_getbuildmaterial(_prmaterial* material, int16_t tilenum, char pal, int8_t shade, int8_t vis, int32_t cmeth)
{
// find corresponding bucket; XXX key that with pr_buckets later, need to be tied to restartvid
_prbucket *bucketptr = polymer_findbucket(tilenum, pal);
polymer_getscratchmaterial(material);
if (!waloff[tilenum])
tileLoad(tilenum);
// PR_BIT_DIFFUSE_MAP
pthtyp *pth = texcache_fetch(tilenum, pal, 0, cmeth);
if (pth)
{
material->diffusemap = pth->glpic;
if (pth->hicr)
{
material->diffusescale[0] = pth->hicr->scale.x;
material->diffusescale[1] = pth->hicr->scale.y;
}
}
int32_t usinghighpal = 0;
// Lazily fill in all the textures we need, move this to precaching later
if (pr_artmapping && !(globalflags & GLOBAL_NO_GL_TILESHADES) && polymer_eligible_for_artmap(tilenum, pth))
{
polytintflags_t const tintflags = hictinting[pal].f;
if (tintflags & (HICTINT_USEONART|HICTINT_ALWAYSUSEART))
{
if (!(tintflags & HICTINT_APPLYOVERPALSWAP))
pal = 0;
}
if (!prartmaps[tilenum] || !prbasepalmaps[curbasepal] || !prlookups[pal])
polymer_setupartmap(tilenum, pal);
material->artmap = prartmaps[tilenum];
material->basepalmap = prbasepalmaps[curbasepal];
material->lookupmap = prlookups[pal];
if (!material->basepalmap || !material->lookupmap) {
material->artmap = 0;
}
material->shadeoffset = shade;
material->visibility = (uint8_t)(vis+16);
globaltinting_apply_ub(material->diffusemodulation);
// all the stuff below is mutually exclusive with artmapping
goto done;
}
// PR_BIT_HIGHPALOOKUP_MAP
if (pr_highpalookups && prhighpalookups[curbasepal][pal].map &&
hicfindsubst(tilenum, 0) &&
(curbasepal || (hicfindsubst(tilenum, pal)->palnum != pal)))
{
material->highpalookupmap = prhighpalookups[curbasepal][pal].map;
pal = 0;
usinghighpal = 1;
}
if (pth)
{
if (pth->hicr)
{
// PR_BIT_SPECULAR_MATERIAL
if (pth->hicr->specpower != 1.0f)
material->specmaterial[0] = pth->hicr->specpower;
material->specmaterial[1] = pth->hicr->specfactor;
}
// PR_BIT_DIFFUSE_MODULATION
material->diffusemodulation[0] =
material->diffusemodulation[1] =
material->diffusemodulation[2] =
(GLubyte)(getshadefactor(shade) * 0xFF);
// tinting
polytintflags_t const tintflags = hictinting[pal].f;
if (!(tintflags & HICTINT_PRECOMPUTED))
{
if (pth->flags & PTH_HIGHTILE)
{
if (pth->palnum != pal || (pth->effects & HICTINT_IN_MEMORY) || (tintflags & HICTINT_APPLYOVERALTPAL))
hictinting_apply_ub(material->diffusemodulation, pal);
}
else if (tintflags & (HICTINT_USEONART|HICTINT_ALWAYSUSEART))
hictinting_apply_ub(material->diffusemodulation, pal);
}
// global tinting
if ((pth->flags & PTH_HIGHTILE) && !usinghighpal && have_basepal_tint())
hictinting_apply_ub(material->diffusemodulation, MAXPALOOKUPS-1);
globaltinting_apply_ub(material->diffusemodulation);
// PR_BIT_GLOW_MAP
if (r_fullbrights && pth->flags & PTH_HASFULLBRIGHT)
material->glowmap = pth->ofb->glpic;
}
// PR_BIT_DIFFUSE_DETAIL_MAP
if (hicfindsubst(tilenum, DETAILPAL, 1) && (pth = texcache_fetch(tilenum, DETAILPAL, 0, DAMETH_NOMASK)) &&
pth->hicr && (pth->hicr->palnum == DETAILPAL))
{
material->detailmap = pth->glpic;
material->detailscale[0] = pth->hicr->scale.x;
material->detailscale[1] = pth->hicr->scale.y;
}
// PR_BIT_GLOW_MAP
if (hicfindsubst(tilenum, GLOWPAL, 1) && (pth = texcache_fetch(tilenum, GLOWPAL, 0, DAMETH_MASK)) &&
pth->hicr && (pth->hicr->palnum == GLOWPAL))
material->glowmap = pth->glpic;
// PR_BIT_SPECULAR_MAP
if (hicfindsubst(tilenum, SPECULARPAL, 1) && (pth = texcache_fetch(tilenum, SPECULARPAL, 0, DAMETH_NOMASK)) &&
pth->hicr && (pth->hicr->palnum == SPECULARPAL))
material->specmap = pth->glpic;
// PR_BIT_NORMAL_MAP
if (hicfindsubst(tilenum, NORMALPAL, 1) && (pth = texcache_fetch(tilenum, NORMALPAL, 0, DAMETH_NOMASK)) &&
pth->hicr && (pth->hicr->palnum == NORMALPAL))
{
material->normalmap = pth->glpic;
material->normalbias[0] = pth->hicr->specpower;
material->normalbias[1] = pth->hicr->specfactor;
}
done:
if (bucketptr->invalidmaterial != 0)
{
bucketptr->material = *material;
bucketptr->invalidmaterial = 0;
}
return bucketptr;
}
static int32_t polymer_bindmaterial(const _prmaterial *material, int16_t* lights, int matlightcount)
{
int32_t programbits;
int32_t texunit;
programbits = 0;
// --------- bit validation
// PR_BIT_ANIM_INTERPOLATION
if (material->nextframedata != ((float*)-1))
programbits |= prprogrambits[PR_BIT_ANIM_INTERPOLATION].bit;
// PR_BIT_LIGHTING_PASS
if (curlight && matlightcount)
programbits |= prprogrambits[PR_BIT_LIGHTING_PASS].bit;
// PR_BIT_NORMAL_MAP
if (pr_normalmapping && material->normalmap)
programbits |= prprogrambits[PR_BIT_NORMAL_MAP].bit;
// PR_BIT_ART_MAP
if (pr_artmapping && material->artmap &&
!(globalflags & GLOBAL_NO_GL_TILESHADES) &&
(overridematerial & prprogrambits[PR_BIT_ART_MAP].bit)) {
programbits |= prprogrambits[PR_BIT_ART_MAP].bit;
} else
// PR_BIT_DIFFUSE_MAP
if (material->diffusemap) {
programbits |= prprogrambits[PR_BIT_DIFFUSE_MAP].bit;
programbits |= prprogrambits[PR_BIT_DIFFUSE_MAP2].bit;
}
// PR_BIT_HIGHPALOOKUP_MAP
if (material->highpalookupmap)
programbits |= prprogrambits[PR_BIT_HIGHPALOOKUP_MAP].bit;
// PR_BIT_DIFFUSE_DETAIL_MAP
if (r_detailmapping && material->detailmap)
programbits |= prprogrambits[PR_BIT_DIFFUSE_DETAIL_MAP].bit;
// PR_BIT_DIFFUSE_MODULATION
programbits |= prprogrambits[PR_BIT_DIFFUSE_MODULATION].bit;
// PR_BIT_SPECULAR_MAP
if (pr_specularmapping && material->specmap)
programbits |= prprogrambits[PR_BIT_SPECULAR_MAP].bit;
// PR_BIT_SPECULAR_MATERIAL
if ((material->specmaterial[0] != 15.0) || (material->specmaterial[1] != 1.0) || pr_overridespecular)
programbits |= prprogrambits[PR_BIT_SPECULAR_MATERIAL].bit;
// PR_BIT_MIRROR_MAP
if (!curlight && material->mirrormap)
programbits |= prprogrambits[PR_BIT_MIRROR_MAP].bit;
// PR_BIT_FOG
if (!material->artmap && !curlight && !material->mirrormap)
programbits |= prprogrambits[PR_BIT_FOG].bit;
// PR_BIT_GLOW_MAP
if (!curlight && r_glowmapping && material->glowmap)
programbits |= prprogrambits[PR_BIT_GLOW_MAP].bit;
// PR_BIT_POINT_LIGHT
if (matlightcount) {
programbits |= prprogrambits[PR_BIT_POINT_LIGHT].bit;
// PR_BIT_SPOT_LIGHT
if (prlights[lights[curlight]].radius) {
programbits |= prprogrambits[PR_BIT_SPOT_LIGHT].bit;
// PR_BIT_SHADOW_MAP
if (prlights[lights[curlight]].rtindex != -1) {
programbits |= prprogrambits[PR_BIT_SHADOW_MAP].bit;
programbits |= prprogrambits[PR_BIT_PROJECTION_MAP].bit;
}
// PR_BIT_LIGHT_MAP
if (prlights[lights[curlight]].lightmap) {
programbits |= prprogrambits[PR_BIT_LIGHT_MAP].bit;
programbits |= prprogrambits[PR_BIT_PROJECTION_MAP].bit;
}
}
}
// material override
programbits &= overridematerial;
programbits |= prprogrambits[PR_BIT_HEADER].bit;
programbits |= prprogrambits[PR_BIT_FOOTER].bit;
// --------- program compiling
if (!prprograms[programbits].handle)
polymer_compileprogram(programbits);
useShaderProgram(prprograms[programbits].handle);
// --------- bit setup
texunit = 0;
// PR_BIT_ANIM_INTERPOLATION
if (programbits & prprogrambits[PR_BIT_ANIM_INTERPOLATION].bit)
{
glEnableVertexAttribArray(prprograms[programbits].attrib_nextFrameData);
if (prprograms[programbits].attrib_nextFrameNormal != -1)
glEnableVertexAttribArray(prprograms[programbits].attrib_nextFrameNormal);
glVertexAttribPointer(prprograms[programbits].attrib_nextFrameData,
3, GL_FLOAT, GL_FALSE,
sizeof(float) * 15,
material->nextframedata);
if (prprograms[programbits].attrib_nextFrameNormal != -1)
glVertexAttribPointer(prprograms[programbits].attrib_nextFrameNormal,
3, GL_FLOAT, GL_FALSE,
sizeof(float) * 15,
material->nextframedata + 3);
glUniform1f(prprograms[programbits].uniform_frameProgress, material->frameprogress);
}
// PR_BIT_LIGHTING_PASS
if (programbits & prprogrambits[PR_BIT_LIGHTING_PASS].bit)
{
glPushAttrib(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
if (prlights[lights[curlight]].publicflags.negative) {
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
}
}
// PR_BIT_NORMAL_MAP
if (programbits & prprogrambits[PR_BIT_NORMAL_MAP].bit)
{
float pos[3], bias[2];
pos[0] = fglobalposy;
pos[1] = fglobalposz * (-1.f/16.f);
pos[2] = -fglobalposx;
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, material->normalmap);
if (material->mdspritespace == GL_TRUE) {
float mdspritespacepos[3];
polymer_transformpoint(pos, mdspritespacepos, (float *)mdspritespace);
glUniform3fv(prprograms[programbits].uniform_eyePosition, 1, mdspritespacepos);
} else
glUniform3fv(prprograms[programbits].uniform_eyePosition, 1, pos);
glUniform1i(prprograms[programbits].uniform_normalMap, texunit);
if (pr_overrideparallax) {
bias[0] = pr_parallaxscale;
bias[1] = pr_parallaxbias;
glUniform2fv(prprograms[programbits].uniform_normalBias, 1, bias);
} else
glUniform2fv(prprograms[programbits].uniform_normalBias, 1, material->normalbias);
if (material->tbn) {
glEnableVertexAttribArray(prprograms[programbits].attrib_T);
glEnableVertexAttribArray(prprograms[programbits].attrib_B);
glEnableVertexAttribArray(prprograms[programbits].attrib_N);
glVertexAttribPointer(prprograms[programbits].attrib_T,
3, GL_FLOAT, GL_FALSE,
sizeof(float) * 15,
material->tbn);
glVertexAttribPointer(prprograms[programbits].attrib_B,
3, GL_FLOAT, GL_FALSE,
sizeof(float) * 15,
material->tbn + 3);
glVertexAttribPointer(prprograms[programbits].attrib_N,
3, GL_FLOAT, GL_FALSE,
sizeof(float) * 15,
material->tbn + 6);
}
texunit++;
}
// PR_BIT_ART_MAP
if (programbits & prprogrambits[PR_BIT_ART_MAP].bit)
{
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, material->artmap);
glUniform1i(prprograms[programbits].uniform_artMap, texunit);
texunit++;
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, material->basepalmap);
glUniform1i(prprograms[programbits].uniform_basePalMap, texunit);
texunit++;
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, material->lookupmap);
glUniform1i(prprograms[programbits].uniform_lookupMap, texunit);
texunit++;
glUniform1f(prprograms[programbits].uniform_shadeOffset, (GLfloat)material->shadeoffset);
if (r_usenewshading == 4)
{
// the fog in Polymer is a sphere insted of a plane, the furthest visible point should be the same as Polymost
glUniform1f(prprograms[programbits].uniform_visibility, globalvisibility / 262144.f * material->visibility);
}
else
{
static constexpr float material_visibility_divisor = 16.f;
// NOTE: the denominator was 1.024, but we increase it towards a bit
// farther far clipoff distance to account for the fact that the
// distance to the fragment is the common Euclidean one, as opposed to
// the "ortho" distance of Build.
static constexpr float factor_new = 1.f / ((2048.f * (1.07f / 1.024f) * (150.f / 230.f) / 35.f) * material_visibility_divisor);
static constexpr float factor_old = 1.f / ((2048.f * (1.07f / 1.024f) / 35.f) * material_visibility_divisor);
glUniform1f(prprograms[programbits].uniform_visibility, globalvisibility * material->visibility * r_usenewshading > 1 ? factor_new : factor_old);
}
}
// PR_BIT_DIFFUSE_MAP
if (programbits & prprogrambits[PR_BIT_DIFFUSE_MAP].bit)
{
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, material->diffusemap);
glUniform1i(prprograms[programbits].uniform_diffuseMap, texunit);
glUniform2fv(prprograms[programbits].uniform_diffuseScale, 1, material->diffusescale);
texunit++;
}
// PR_BIT_HIGHPALOOKUP_MAP
if (programbits & prprogrambits[PR_BIT_HIGHPALOOKUP_MAP].bit)
{
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, material->highpalookupmap);
glUniform1i(prprograms[programbits].uniform_highPalookupMap, texunit);
texunit++;
}
// PR_BIT_DIFFUSE_DETAIL_MAP
if (programbits & prprogrambits[PR_BIT_DIFFUSE_DETAIL_MAP].bit)
{
float scale[2];
// scale by the diffuse map scale if we're not doing normal mapping
if (!(programbits & prprogrambits[PR_BIT_NORMAL_MAP].bit))
{
scale[0] = material->diffusescale[0] * material->detailscale[0];
scale[1] = material->diffusescale[1] * material->detailscale[1];
} else {
scale[0] = material->detailscale[0];
scale[1] = material->detailscale[1];
}
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, material->detailmap);
glUniform1i(prprograms[programbits].uniform_detailMap, texunit);
glUniform2fv(prprograms[programbits].uniform_detailScale, 1, scale);
texunit++;
}
// PR_BIT_DIFFUSE_MODULATION
if (programbits & prprogrambits[PR_BIT_DIFFUSE_MODULATION].bit)
{
glColor4ub(material->diffusemodulation[0],
material->diffusemodulation[1],
material->diffusemodulation[2],
material->diffusemodulation[3]);
}
// PR_BIT_SPECULAR_MAP
if (programbits & prprogrambits[PR_BIT_SPECULAR_MAP].bit)
{
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, material->specmap);
glUniform1i(prprograms[programbits].uniform_specMap, texunit);
texunit++;
}
// PR_BIT_SPECULAR_MATERIAL
if (programbits & prprogrambits[PR_BIT_SPECULAR_MATERIAL].bit)
{
float specmaterial[2];
if (pr_overridespecular) {
specmaterial[0] = pr_specularpower;
specmaterial[1] = pr_specularfactor;
glUniform2fv(prprograms[programbits].uniform_specMaterial, 1, specmaterial);
} else
glUniform2fv(prprograms[programbits].uniform_specMaterial, 1, material->specmaterial);
}
// PR_BIT_MIRROR_MAP
if (programbits & prprogrambits[PR_BIT_MIRROR_MAP].bit)
{
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, material->mirrormap);
glUniform1i(prprograms[programbits].uniform_mirrorMap, texunit);
texunit++;
}
#ifdef PR_LINEAR_FOG
if (programbits & prprogrambits[PR_BIT_FOG].bit)
{
glUniform1i(prprograms[programbits].uniform_linearFog, r_usenewshading >= 2);
}
#endif
// PR_BIT_GLOW_MAP
if (programbits & prprogrambits[PR_BIT_GLOW_MAP].bit)
{
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, material->glowmap);
glUniform1i(prprograms[programbits].uniform_glowMap, texunit);
texunit++;
}
// PR_BIT_POINT_LIGHT
if (programbits & prprogrambits[PR_BIT_POINT_LIGHT].bit)
{
float inpos[4], pos[4];
float range[2];
float color[4];
inpos[0] = (float)prlights[lights[curlight]].y;
inpos[1] = -(float)prlights[lights[curlight]].z / 16.0f;
inpos[2] = -(float)prlights[lights[curlight]].x;
polymer_transformpoint(inpos, pos, curmodelviewmatrix);
// PR_BIT_SPOT_LIGHT
if (programbits & prprogrambits[PR_BIT_SPOT_LIGHT].bit)
{
float sinang, cosang, sinhorizang, coshorizangs;
float indir[3], dir[3];
cosang = (float)(sintable[(-prlights[lights[curlight]].angle+1024)&2047]) / 16383.0f;
sinang = (float)(sintable[(-prlights[lights[curlight]].angle+512)&2047]) / 16383.0f;
coshorizangs = (float)(sintable[(getangle(128, prlights[lights[curlight]].horiz-100)+1024)&2047]) / 16383.0f;
sinhorizang = (float)(sintable[(getangle(128, prlights[lights[curlight]].horiz-100)+512)&2047]) / 16383.0f;
indir[0] = inpos[0] + sinhorizang * cosang;
indir[1] = inpos[1] - coshorizangs;
indir[2] = inpos[2] - sinhorizang * sinang;
polymer_transformpoint(indir, dir, curmodelviewmatrix);
dir[0] -= pos[0];
dir[1] -= pos[1];
dir[2] -= pos[2];
indir[0] = (float)(sintable[(prlights[lights[curlight]].radius+512)&2047]) / 16383.0f;
indir[1] = (float)(sintable[(prlights[lights[curlight]].faderadius+512)&2047]) / 16383.0f;
indir[1] = 1.0 / (indir[1] - indir[0]);
glUniform3fv(prprograms[programbits].uniform_spotDir, 1, dir);
glUniform2fv(prprograms[programbits].uniform_spotRadius, 1, indir);
// PR_BIT_PROJECTION_MAP
if (programbits & prprogrambits[PR_BIT_PROJECTION_MAP].bit)
{
GLfloat matrix[16];
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(shadowBias);
glMultMatrixf(prlights[lights[curlight]].proj);
glMultMatrixf(prlights[lights[curlight]].transform);
if (material->mdspritespace == GL_TRUE)
glMultMatrixf(spritemodelview);
glGetFloatv(GL_TEXTURE_MATRIX, matrix);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glUniformMatrix4fv(prprograms[programbits].uniform_shadowProjMatrix, 1, GL_FALSE, matrix);
// PR_BIT_SHADOW_MAP
if (programbits & prprogrambits[PR_BIT_SHADOW_MAP].bit)
{
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(prrts[prlights[lights[curlight]].rtindex].target, prrts[prlights[lights[curlight]].rtindex].z);
glUniform1i(prprograms[programbits].uniform_shadowMap, texunit);
texunit++;
}
// PR_BIT_LIGHT_MAP
if (programbits & prprogrambits[PR_BIT_LIGHT_MAP].bit)
{
glActiveTexture(texunit + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, prlights[lights[curlight]].lightmap);
glUniform1i(prprograms[programbits].uniform_lightMap, texunit);
texunit++;
}
}
}
range[0] = prlights[lights[curlight]].range / 1000.0f;
range[1] = 1 / (range[0] * range[0]);
color[0] = prlights[lights[curlight]].color[0] / 255.0f;
color[1] = prlights[lights[curlight]].color[1] / 255.0f;
color[2] = prlights[lights[curlight]].color[2] / 255.0f;
// If this isn't a lighting-only pass, just negate the components
if (!curlight && prlights[lights[curlight]].publicflags.negative) {
color[0] = -color[0];
color[1] = -color[1];
color[2] = -color[2];
}
glLightfv(GL_LIGHT0, GL_AMBIENT, pos);
glLightfv(GL_LIGHT0, GL_DIFFUSE, color);
if (material->mdspritespace == GL_TRUE) {
float mdspritespacepos[3];
polymer_transformpoint(inpos, mdspritespacepos, (float *)mdspritespace);
glLightfv(GL_LIGHT0, GL_SPECULAR, mdspritespacepos);
} else {
glLightfv(GL_LIGHT0, GL_SPECULAR, inpos);
}
glLightfv(GL_LIGHT0, GL_LINEAR_ATTENUATION, &range[1]);
}
glActiveTexture(GL_TEXTURE0);
return programbits;
}
static void polymer_unbindmaterial(int32_t programbits)
{
// repair any dirty GL state here
// PR_BIT_ANIM_INTERPOLATION
if (programbits & prprogrambits[PR_BIT_ANIM_INTERPOLATION].bit)
{
if (prprograms[programbits].attrib_nextFrameNormal != -1)
glDisableVertexAttribArray(prprograms[programbits].attrib_nextFrameNormal);
glDisableVertexAttribArray(prprograms[programbits].attrib_nextFrameData);
}
// PR_BIT_LIGHTING_PASS
if (programbits & prprogrambits[PR_BIT_LIGHTING_PASS].bit)
{
glPopAttrib();
}
// PR_BIT_NORMAL_MAP
if (programbits & prprogrambits[PR_BIT_NORMAL_MAP].bit)
{
glDisableVertexAttribArray(prprograms[programbits].attrib_T);
glDisableVertexAttribArray(prprograms[programbits].attrib_B);
glDisableVertexAttribArray(prprograms[programbits].attrib_N);
}
useShaderProgram(0);
}
static void polymer_compileprogram(int32_t programbits)
{
int32_t i, enabledbits;
GLuint vert, frag, program;
const GLchar* source[PR_BIT_COUNT * 2];
GLchar infobuffer[PR_INFO_LOG_BUFFER_SIZE];
GLint linkstatus;
// --------- VERTEX
vert = glCreateShader(GL_VERTEX_SHADER);
enabledbits = i = 0;
while (i < PR_BIT_COUNT)
{
if (programbits & prprogrambits[i].bit)
source[enabledbits++] = prprogrambits[i].vert_def;
i++;
}
i = 0;
while (i < PR_BIT_COUNT)
{
if (programbits & prprogrambits[i].bit)
source[enabledbits++] = prprogrambits[i].vert_prog;
i++;
}
glShaderSource(vert, enabledbits, source, NULL);
glCompileShader(vert);
// --------- FRAGMENT
frag = glCreateShader(GL_FRAGMENT_SHADER);
enabledbits = i = 0;
while (i < PR_BIT_COUNT)
{
if (programbits & prprogrambits[i].bit)
source[enabledbits++] = prprogrambits[i].frag_def;
i++;
}
i = 0;
while (i < PR_BIT_COUNT)
{
if (programbits & prprogrambits[i].bit)
source[enabledbits++] = prprogrambits[i].frag_prog;
i++;
}
glShaderSource(frag, enabledbits, (const GLchar**)source, NULL);
glCompileShader(frag);
// --------- PROGRAM
program = glCreateProgram();
glAttachShader(program, vert);
glAttachShader(program, frag);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &linkstatus);
glGetProgramInfoLog(program, PR_INFO_LOG_BUFFER_SIZE, NULL, infobuffer);
prprograms[programbits].handle = program;
#ifdef DEBUGGINGAIDS
if (pr_verbosity >= 1)
#else
if (pr_verbosity >= 2)
#endif
OSD_Printf("PR : Compiling GPU program with bits (octal) %o...\n", (unsigned)programbits);
if (!linkstatus) {
OSD_Printf("PR : Failed to compile GPU program with bits (octal) %o!\n", (unsigned)programbits);
if (pr_verbosity >= 1) OSD_Printf("PR : Compilation log:\n%s\n", infobuffer);
glGetShaderSource(vert, PR_INFO_LOG_BUFFER_SIZE, NULL, infobuffer);
if (pr_verbosity >= 1) OSD_Printf("PR : Vertex source dump:\n%s\n", infobuffer);
glGetShaderSource(frag, PR_INFO_LOG_BUFFER_SIZE, NULL, infobuffer);
if (pr_verbosity >= 1) OSD_Printf("PR : Fragment source dump:\n%s\n", infobuffer);
}
// --------- ATTRIBUTE/UNIFORM LOCATIONS
// PR_BIT_ANIM_INTERPOLATION
if (programbits & prprogrambits[PR_BIT_ANIM_INTERPOLATION].bit)
{
prprograms[programbits].attrib_nextFrameData = glGetAttribLocation(program, "nextFrameData");
prprograms[programbits].attrib_nextFrameNormal = glGetAttribLocation(program, "nextFrameNormal");
prprograms[programbits].uniform_frameProgress = glGetUniformLocation(program, "frameProgress");
}
// PR_BIT_NORMAL_MAP
if (programbits & prprogrambits[PR_BIT_NORMAL_MAP].bit)
{
prprograms[programbits].attrib_T = glGetAttribLocation(program, "T");
prprograms[programbits].attrib_B = glGetAttribLocation(program, "B");
prprograms[programbits].attrib_N = glGetAttribLocation(program, "N");
prprograms[programbits].uniform_eyePosition = glGetUniformLocation(program, "eyePosition");
prprograms[programbits].uniform_normalMap = glGetUniformLocation(program, "normalMap");
prprograms[programbits].uniform_normalBias = glGetUniformLocation(program, "normalBias");
}
// PR_BIT_ART_MAP
if (programbits & prprogrambits[PR_BIT_ART_MAP].bit)
{
prprograms[programbits].uniform_artMap = glGetUniformLocation(program, "artMap");
prprograms[programbits].uniform_basePalMap = glGetUniformLocation(program, "basePalMap");
prprograms[programbits].uniform_lookupMap = glGetUniformLocation(program, "lookupMap");
prprograms[programbits].uniform_shadeOffset = glGetUniformLocation(program, "shadeOffset");
prprograms[programbits].uniform_visibility = glGetUniformLocation(program, "visibility");
}
// PR_BIT_DIFFUSE_MAP
if (programbits & prprogrambits[PR_BIT_DIFFUSE_MAP].bit)
{
prprograms[programbits].uniform_diffuseMap = glGetUniformLocation(program, "diffuseMap");
prprograms[programbits].uniform_diffuseScale = glGetUniformLocation(program, "diffuseScale");
}
// PR_BIT_HIGHPALOOKUP_MAP
if (programbits & prprogrambits[PR_BIT_HIGHPALOOKUP_MAP].bit)
{
prprograms[programbits].uniform_highPalookupMap = glGetUniformLocation(program, "highPalookupMap");
}
// PR_BIT_DIFFUSE_DETAIL_MAP
if (programbits & prprogrambits[PR_BIT_DIFFUSE_DETAIL_MAP].bit)
{
prprograms[programbits].uniform_detailMap = glGetUniformLocation(program, "detailMap");
prprograms[programbits].uniform_detailScale = glGetUniformLocation(program, "detailScale");
}
// PR_BIT_SPECULAR_MAP
if (programbits & prprogrambits[PR_BIT_SPECULAR_MAP].bit)
{
prprograms[programbits].uniform_specMap = glGetUniformLocation(program, "specMap");
}
// PR_BIT_SPECULAR_MATERIAL
if (programbits & prprogrambits[PR_BIT_SPECULAR_MATERIAL].bit)
{
prprograms[programbits].uniform_specMaterial = glGetUniformLocation(program, "specMaterial");
}
// PR_BIT_MIRROR_MAP
if (programbits & prprogrambits[PR_BIT_MIRROR_MAP].bit)
{
prprograms[programbits].uniform_mirrorMap = glGetUniformLocation(program, "mirrorMap");
}
#ifdef PR_LINEAR_FOG
if (programbits & prprogrambits[PR_BIT_FOG].bit)
{
prprograms[programbits].uniform_linearFog = glGetUniformLocation(program, "linearFog");
}
#endif
// PR_BIT_GLOW_MAP
if (programbits & prprogrambits[PR_BIT_GLOW_MAP].bit)
{
prprograms[programbits].uniform_glowMap = glGetUniformLocation(program, "glowMap");
}
// PR_BIT_PROJECTION_MAP
if (programbits & prprogrambits[PR_BIT_PROJECTION_MAP].bit)
{
prprograms[programbits].uniform_shadowProjMatrix = glGetUniformLocation(program, "shadowProjMatrix");
}
// PR_BIT_SHADOW_MAP
if (programbits & prprogrambits[PR_BIT_SHADOW_MAP].bit)
{
prprograms[programbits].uniform_shadowMap = glGetUniformLocation(program, "shadowMap");
}
// PR_BIT_LIGHT_MAP
if (programbits & prprogrambits[PR_BIT_LIGHT_MAP].bit)
{
prprograms[programbits].uniform_lightMap = glGetUniformLocation(program, "lightMap");
}
// PR_BIT_SPOT_LIGHT
if (programbits & prprogrambits[PR_BIT_SPOT_LIGHT].bit)
{
prprograms[programbits].uniform_spotDir = glGetUniformLocation(program, "spotDir");
prprograms[programbits].uniform_spotRadius = glGetUniformLocation(program, "spotRadius");
}
}
// LIGHTS
static void polymer_removelight(int16_t lighti)
{
_prplanelist* oldhead;
while (prlights[lighti].planelist)
{
polymer_deleteplanelight(prlights[lighti].planelist->plane, lighti);
oldhead = prlights[lighti].planelist;
prlights[lighti].planelist = prlights[lighti].planelist->n;
oldhead->n = plpool;
plpool = oldhead;
plpool->plane = NULL;
}
prlights[lighti].planecount = 0;
prlights[lighti].planelist = NULL;
}
static void polymer_updatelights(void)
{
int32_t i = 0;
do
{
_prlight* light = &prlights[i];
if (light->flags.active && light->flags.invalidate) {
// highly suboptimal
polymer_removelight(i);
if (light->radius)
polymer_processspotlight(light);
polymer_culllight(i);
light->flags.invalidate = 0;
}
if (light->flags.active) {
// get the texture handle for the lightmap
if (light->radius && light->tilenum > 0)
{
int16_t picnum = light->tilenum;
pthtyp* pth;
DO_TILE_ANIM(picnum, 0);
if (!waloff[picnum])
tileLoad(picnum);
pth = NULL;
pth = texcache_fetch(picnum, 0, 0, DAMETH_NOMASK);
if (pth)
light->lightmap = pth->glpic;
}
light->rtindex = -1;
}
}
while (++i < PR_MAXLIGHTS);
}
static inline void polymer_resetplanelights(_prplane* plane)
{
Bmemset(&plane->lights[0], -1, sizeof(plane->lights[0]) * plane->lightcount);
plane->lightcount = 0;
}
static void polymer_addplanelight(_prplane* plane, int16_t lighti)
{
_prplanelist* oldhead;
int32_t i = 0;
if (plane->lightcount)
{
if (plane->lightcount == PR_MAXLIGHTS - 1)
return;
do
{
if (plane->lights[i++] == lighti)
goto out;
}
while (i < plane->lightcount);
i = 0;
while (i < plane->lightcount && prlights[plane->lights[i]].priority < prlights[lighti].priority)
i++;
Bmemmove(&plane->lights[i+1], &plane->lights[i], sizeof(int16_t) * (plane->lightcount - i));
}
plane->lights[i] = lighti;
plane->lightcount++;
out:
oldhead = prlights[lighti].planelist;
while (oldhead != NULL)
{
if (oldhead->plane == plane) return;
oldhead = oldhead->n;
}
oldhead = prlights[lighti].planelist;
if (plpool == NULL)
{
prlights[lighti].planelist = (_prplanelist *) Xmalloc(sizeof(_prplanelist));
prlights[lighti].planelist->n = oldhead;
}
else
{
prlights[lighti].planelist = plpool;
plpool = plpool->n;
prlights[lighti].planelist->n = oldhead;
}
prlights[lighti].planelist->plane = plane;
prlights[lighti].planecount++;
}
static inline void polymer_deleteplanelight(_prplane* plane, int16_t lighti)
{
int32_t i = plane->lightcount-1;
while (i >= 0)
{
if (plane->lights[i] == lighti)
{
Bmemmove(&plane->lights[i], &plane->lights[i+1], sizeof(int16_t) * (plane->lightcount - i));
plane->lightcount--;
return;
}
i--;
}
}
static int32_t polymer_planeinlight(_prplane* plane, _prlight* light)
{
float lightpos[3];
int32_t i, j, k, l;
if (!plane->vertcount)
return 0;
if (light->radius)
return polymer_planeinfrustum(plane, light->frustum);
lightpos[0] = (float)light->y;
lightpos[1] = -(float)light->z / 16.0f;
lightpos[2] = -(float)light->x;
i = 0;
do
{
j = k = l = 0;
do
{
if ((&plane->buffer[j].x)[i] > (lightpos[i] + light->range)) k++;
if ((&plane->buffer[j].x)[i] < (lightpos[i] - light->range)) l++;
}
while (++j < plane->vertcount);
if ((k == plane->vertcount) || (l == plane->vertcount))
return 0;
}
while (++i < 3);
return 1;
}
static void polymer_invalidateplanelights(_prplane* plane)
{
int32_t i = plane->lightcount;
while (i--)
{
if (((unsigned)plane->lights[i] < PR_MAXLIGHTS) && (prlights[plane->lights[i]].flags.active))
prlights[plane->lights[i]].flags.invalidate = 1;
}
}
static void polymer_invalidatesectorlights(int16_t sectnum)
{
int32_t i;
_prsector *s = prsectors[sectnum];
sectortype *sec = &sector[sectnum];
if (!s)
return;
polymer_invalidateplanelights(&s->floor);
polymer_invalidateplanelights(&s->ceil);
i = sec->wallnum;
while (i--)
{
_prwall *w;
if (!(w = prwalls[sec->wallptr + i])) continue;
polymer_invalidateplanelights(&w->wall);
polymer_invalidateplanelights(&w->over);
polymer_invalidateplanelights(&w->mask);
}
}
static void polymer_processspotlight(_prlight* light)
{
float radius, ang, horizang, lightpos[3];
// hack to avoid lights beams perpendicular to walls
if ((light->horiz <= 100) && (light->horiz > 90))
light->horiz = 90;
if ((light->horiz > 100) && (light->horiz < 110))
light->horiz = 110;
lightpos[0] = (float)light->y;
lightpos[1] = -(float)light->z / 16.0f;
lightpos[2] = -(float)light->x;
// calculate the spot light transformations and matrices
radius = (float)(light->radius) * (360.f/2048.f);
ang = (float)(light->angle) * (360.f/2048.f);
horizang = (float)(-getangle(128, light->horiz-100)) * (360.f/2048.f);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
bgluPerspective(radius * 2, 1, 0.1f, light->range * (1.f/1000.f));
glGetFloatv(GL_PROJECTION_MATRIX, light->proj);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glRotatef(horizang, 1.0f, 0.0f, 0.0f);
glRotatef(ang, 0.0f, 1.0f, 0.0f);
glScalef(1.0f / 1000.0f, 1.0f / 1000.0f, 1.0f / 1000.0f);
glTranslatef(-lightpos[0], -lightpos[1], -lightpos[2]);
glGetFloatv(GL_MODELVIEW_MATRIX, light->transform);
glPopMatrix();
polymer_extractfrustum(light->transform, light->proj, light->frustum);
light->rtindex = -1;
light->lightmap = 0;
}
static inline void polymer_culllight(int16_t lighti)
{
_prlight* light = &prlights[lighti];
int32_t front = 0;
int32_t back = 1;
int32_t i;
int32_t j;
int32_t zdiff;
int32_t checkror;
int16_t bunchnum;
int16_t ns;
_prsector *s;
_prwall *w;
sectortype *sec;
Bmemset(drawingstate, 0, sizeof(int16_t) * numsectors);
drawingstate[light->sector] = 1;
sectorqueue[0] = light->sector;
do
{
s = prsectors[sectorqueue[front]];
sec = &sector[sectorqueue[front]];
polymer_pokesector(sectorqueue[front]);
checkror = FALSE;
zdiff = light->z - s->floorz;
if (zdiff < 0)
zdiff = -zdiff;
zdiff >>= 4;
if (!light->radius && !(sec->floorstat & 1)) {
if (zdiff < light->range) {
polymer_addplanelight(&s->floor, lighti);
checkror = TRUE;
}
} else if (polymer_planeinlight(&s->floor, light)) {
polymer_addplanelight(&s->floor, lighti);
checkror = TRUE;
}
#ifdef YAX_ENABLE
// queue ROR neighbors
if (checkror &&
(bunchnum = yax_getbunch(sectorqueue[front], YAX_FLOOR)) >= 0) {
for (SECTORS_OF_BUNCH(bunchnum, YAX_CEILING, ns)) {
if (ns >= 0 && !drawingstate[ns] &&
polymer_planeinlight(&prsectors[ns]->ceil, light)) {
sectorqueue[back++] = ns;
drawingstate[ns] = 1;
}
}
}
#endif
checkror = FALSE;
zdiff = light->z - s->ceilingz;
if (zdiff < 0)
zdiff = -zdiff;
zdiff >>= 4;
if (!light->radius && !(sec->ceilingstat & 1)) {
if (zdiff < light->range) {
polymer_addplanelight(&s->ceil, lighti);
checkror = TRUE;
}
} else if (polymer_planeinlight(&s->ceil, light)) {
polymer_addplanelight(&s->ceil, lighti);
checkror = TRUE;
}
#ifdef YAX_ENABLE
// queue ROR neighbors
if (checkror &&
(bunchnum = yax_getbunch(sectorqueue[front], YAX_CEILING)) >= 0) {
for (SECTORS_OF_BUNCH(bunchnum, YAX_FLOOR, ns)) {
if (ns >= 0 && !drawingstate[ns] &&
polymer_planeinlight(&prsectors[ns]->floor, light)) {
sectorqueue[back++] = ns;
drawingstate[ns] = 1;
}
}
}
#endif
i = 0;
while (i < sec->wallnum)
{
w = prwalls[sec->wallptr + i];
j = 0;
if (polymer_planeinlight(&w->wall, light)) {
polymer_addplanelight(&w->wall, lighti);
j++;
}
if (polymer_planeinlight(&w->over, light)) {
polymer_addplanelight(&w->over, lighti);
j++;
}
// assume the light hits the middle section if it hits the top and bottom
if (wallvisible(light->x, light->y, sec->wallptr + i) &&
(j == 2 || polymer_planeinlight(&w->mask, light))) {
if ((w->mask.vertcount == 4) &&
(w->mask.buffer[0].y >= w->mask.buffer[3].y) &&
(w->mask.buffer[1].y >= w->mask.buffer[2].y))
{
i++;
continue;
}
polymer_addplanelight(&w->mask, lighti);
if ((wall[sec->wallptr + i].nextsector >= 0) &&
(!drawingstate[wall[sec->wallptr + i].nextsector])) {
drawingstate[wall[sec->wallptr + i].nextsector] = 1;
sectorqueue[back] = wall[sec->wallptr + i].nextsector;
back++;
}
}
i++;
}
front++;
}
while (front != back);
i = MAXSPRITES-1;
do
{
_prsprite *s = prsprites[i];
if ((sprite[i].cstat & 48) == 0 || s == NULL || sprite[i].statnum == MAXSTATUS || sprite[i].sectnum == MAXSECTORS)
continue;
if (polymer_planeinlight(&s->plane, light))
polymer_addplanelight(&s->plane, lighti);
}
while (i--);
}
static void polymer_prepareshadows(void)
{
fix16_t oviewangle, oglobalang;
int32_t i, j, k;
int32_t gx, gy, gz;
int32_t oldoverridematerial;
// for wallvisible()
gx = globalposx;
gy = globalposy;
gz = globalposz;
// build globals used by drawmasks
oviewangle = viewangle;
oglobalang = qglobalang;
i = j = k = 0;
while ((k < lightcount) && (j < pr_shadowcount))
{
while (!prlights[i].flags.active)
i++;
if (prlights[i].radius && prlights[i].publicflags.emitshadow &&
prlights[i].flags.isinview)
{
prlights[i].flags.isinview = 0;
prlights[i].rtindex = j + 1;
if (pr_verbosity >= 3) OSD_Printf("PR : Drawing shadow %i...\n", i);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, prrts[prlights[i].rtindex].fbo);
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0, 0, prrts[prlights[i].rtindex].xdim, prrts[prlights[i].rtindex].ydim);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadMatrixf(prlights[i].proj);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(prlights[i].transform);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(5, SHADOW_DEPTH_OFFSET);
set_globalpos(prlights[i].x, prlights[i].y, prlights[i].z);
// build globals used by rotatesprite
viewangle = fix16_from_int(prlights[i].angle);
set_globalang(fix16_from_int(prlights[i].angle));
oldoverridematerial = overridematerial;
// smooth model shadows
overridematerial = prprogrambits[PR_BIT_ANIM_INTERPOLATION].bit;
// used by alpha-testing for sprite silhouette
overridematerial |= prprogrambits[PR_BIT_DIFFUSE_MAP].bit;
overridematerial |= prprogrambits[PR_BIT_DIFFUSE_MAP2].bit;
// to force sprite drawing
mirrors[depth++].plane = NULL;
polymer_displayrooms(prlights[i].sector);
depth--;
overridematerial = oldoverridematerial;
glDisable(GL_POLYGON_OFFSET_FILL);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopAttrib();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
j++;
}
i++;
k++;
}
set_globalpos(gx, gy, gz);
viewangle = oviewangle;
set_globalang(oglobalang);
}
// RENDER TARGETS
static void polymer_initrendertargets(int32_t count)
{
int32_t i;
static int32_t ocount;
if (count == 0) // uninit
{
if (prrts)
{
for (i=0; i<ocount; i++)
{
if (prrts[i].color)
{
glDeleteTextures(1, &prrts[i].color);
prrts[i].color = 0;
}
glDeleteTextures(1, &prrts[i].z);
prrts[i].z = 0;
glDeleteFramebuffersEXT(1, &prrts[i].fbo);
prrts[i].fbo = 0;
}
DO_FREE_AND_NULL(prrts);
}
ocount = 0;
return;
}
ocount = count;
//////////
prrts = (_prrt *)Xcalloc(count, sizeof(_prrt));
i = 0;
while (i < count)
{
if (!i) {
prrts[i].target = GL_TEXTURE_RECTANGLE_ARB;
prrts[i].xdim = xdim;
prrts[i].ydim = ydim;
glGenTextures(1, &prrts[i].color);
glBindTexture(prrts[i].target, prrts[i].color);
glTexImage2D(prrts[i].target, 0, GL_RGB, prrts[i].xdim, prrts[i].ydim, 0, GL_RGB, GL_SHORT, NULL);
glTexParameteri(prrts[i].target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(prrts[i].target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(prrts[i].target, GL_TEXTURE_WRAP_S, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP);
glTexParameteri(prrts[i].target, GL_TEXTURE_WRAP_T, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP);
} else {
prrts[i].target = GL_TEXTURE_2D;
prrts[i].xdim = 128 << pr_shadowdetail;
prrts[i].ydim = 128 << pr_shadowdetail;
prrts[i].color = 0;
if (pr_ati_fboworkaround) {
glGenTextures(1, &prrts[i].color);
glBindTexture(prrts[i].target, prrts[i].color);
glTexImage2D(prrts[i].target, 0, GL_RGB, prrts[i].xdim, prrts[i].ydim, 0, GL_RGB, GL_SHORT, NULL);
glTexParameteri(prrts[i].target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(prrts[i].target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(prrts[i].target, GL_TEXTURE_WRAP_S, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP);
glTexParameteri(prrts[i].target, GL_TEXTURE_WRAP_T, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP);
}
}
glGenTextures(1, &prrts[i].z);
glBindTexture(prrts[i].target, prrts[i].z);
glTexImage2D(prrts[i].target, 0, GL_DEPTH_COMPONENT, prrts[i].xdim, prrts[i].ydim, 0, GL_DEPTH_COMPONENT, GL_SHORT, NULL);
glTexParameteri(prrts[i].target, GL_TEXTURE_MIN_FILTER, pr_shadowfiltering ? GL_LINEAR : GL_NEAREST);
glTexParameteri(prrts[i].target, GL_TEXTURE_MAG_FILTER, pr_shadowfiltering ? GL_LINEAR : GL_NEAREST);
glTexParameteri(prrts[i].target, GL_TEXTURE_WRAP_S, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP);
glTexParameteri(prrts[i].target, GL_TEXTURE_WRAP_T, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP);
glTexParameteri(prrts[i].target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
glTexParameteri(prrts[i].target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glTexParameteri(prrts[i].target, GL_DEPTH_TEXTURE_MODE, GL_ALPHA);
glGenFramebuffersEXT(1, &prrts[i].fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, prrts[i].fbo);
if (prrts[i].color)
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
prrts[i].target, prrts[i].color, 0);
else {
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
}
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, prrts[i].target, prrts[i].z, 0);
if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
{
OSD_Printf("PR : FBO #%d initialization failed.\n", i);
}
glBindTexture(prrts[i].target, 0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
i++;
}
}
// DEBUG OUTPUT
void PR_CALLBACK polymer_debugoutputcallback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam)
{
UNREFERENCED_PARAMETER(source);
UNREFERENCED_PARAMETER(type);
UNREFERENCED_PARAMETER(id);
UNREFERENCED_PARAMETER(severity);
UNREFERENCED_PARAMETER(length);
UNREFERENCED_PARAMETER(userParam);
if (type == GL_DEBUG_TYPE_ERROR_ARB)
{
OSD_Printf("PR : Received OpenGL debug message: %s\n", message);
}
}
#endif