// blah #if defined USE_OPENGL && defined POLYMER #include #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_ARB; GLenum modelvbousage = GL_STATIC_DRAW_ARB; // 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 GLsizeiptrARB proneplanesize = sizeof(_prvert) * 4; const GLintptrARB prwalldatasize = sizeof(_prvert)* 4 * 3; // wall, over and mask planes for every wall GLintptrARB prwalldataoffset; GLuint prindexringvbo; GLuint *prindexring; const GLsizeiptrARB prindexringsize = 65535; GLintptrARB 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 // 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. " float shadeLookup = length(horizDistance) / 1.07 * 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 / 35.0;\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 /* 0.65127==150/230, another constant found out by experiment. :/ * (150 is Polymost's old FOGDISTCONST.) */ " } else {\n" " fogFactor = gl_Fog.scale * (gl_Fog.end - fragDepth*0.65217);\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; int16_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 = getticks(); 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) { bglGenTextures(1, &prhighpalookups[i][j].map); bglBindTexture(GL_TEXTURE_3D, prhighpalookups[i][j].map); bglTexImage3D(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 bglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); bglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); bglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP); bglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP); bglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP); bglBindTexture(GL_TEXTURE_3D, 0); } j++; } i++; } #ifndef __APPLE__ if (glinfo.debugoutput) { // Enable everything. bglDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); bglDebugMessageCallbackARB((GLDEBUGPROCARB)polymer_debugoutputcallback, NULL); bglEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); } #endif if (pr_verbosity >= 1) OSD_Printf("PR : Initialization complete in %d ms.\n", getticks()-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) { bglDeleteTextures(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((float)viewingrange/65536.0f)/(PI/4); if (pr_customaspect != 0.0f) aspect = pr_customaspect; else aspect = (float)(windowx2-windowx1+1) / (float)(windowy2-windowy1+1); bglMatrixMode(GL_PROJECTION); bglLoadIdentity(); bgluPerspective(fang / (2048.0f / 360.0f), aspect, 0.01f, 100.0f); } void polymer_glinit(void) { bglClearColor(0.0f, 0.0f, 0.0f, 1.0f); bglClearStencil(0); bglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); bglViewport(windowx1, yres-(windowy2+1),windowx2-windowx1+1, windowy2-windowy1+1); // texturing bglEnable(GL_TEXTURE_2D); bglEnable(GL_DEPTH_TEST); bglDepthFunc(GL_LEQUAL); bglDisable(GL_BLEND); bglDisable(GL_ALPHA_TEST); if (pr_wireframe) bglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); else bglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); polymer_setaspect(pr_fov); bglMatrixMode(GL_MODELVIEW); bglLoadIdentity(); bglEnableClientState(GL_VERTEX_ARRAY); bglEnableClientState(GL_TEXTURE_COORD_ARRAY); bglDisable(GL_FOG); culledface = GL_BACK; bglCullFace(GL_BACK); bglFrontFace(GL_CCW); bglEnable(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 (!loadmaphack(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); bglGenBuffersARB(1, &prmapvbo); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, prmapvbo); bglBufferDataARB(GL_ARRAY_BUFFER_ARB, prwalldataoffset + (numwalls * prwalldatasize), NULL, mapvbousage); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); bglGenBuffersARB(1, &prindexringvbo); bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, prindexringvbo); if (pr_buckets) { bglBufferStorage(GL_ELEMENT_ARRAY_BUFFER, prindexringsize * sizeof(GLuint), NULL, prindexringmapflags | GL_DYNAMIC_STORAGE_BIT); prindexring = (GLuint*)bglMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, prindexringsize * sizeof(GLuint), prindexringmapflags); } bglBindBufferARB(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, int16_t daang, int32_t dahoriz, int16_t dacursectnum) { int16_t cursectnum; int32_t i, cursectflorz, cursectceilz; float skyhoriz, ang, tiltang; float pos[3]; pthtyp* pth; if (getrendermode() == REND_CLASSIC) return; begindrawing(); // TODO: support for screen resizing // frameoffset = frameplace + windowy1*bytesperline + windowx1; if (pr_verbosity >= 3) OSD_Printf("PR : Drawing rooms...\n"); // fogcalc needs this gvisibility = ((float)globalvisibility)*FOGSCALE; ang = (float)(daang) / (2048.0f / 360.0f); horizang = (float)(-getangle(128, dahoriz-100)) / (2048.0f / 360.0f); 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; bglMatrixMode(GL_MODELVIEW); bglLoadIdentity(); bglRotatef(tiltang, 0.0f, 0.0f, -1.0f); bglRotatef(skyhoriz, 1.0f, 0.0f, 0.0f); bglRotatef(ang, 0.0f, 1.0f, 0.0f); bglScalef(1.0f / 1000.0f, 1.0f / 1000.0f, 1.0f / 1000.0f); bglTranslatef(-pos[0], -pos[1], -pos[2]); bglGetFloatv(GL_MODELVIEW_MATRIX, rootskymodelviewmatrix); curskymodelviewmatrix = rootskymodelviewmatrix; bglMatrixMode(GL_MODELVIEW); bglLoadIdentity(); bglRotatef(tiltang, 0.0f, 0.0f, -1.0f); bglRotatef(horizang, 1.0f, 0.0f, 0.0f); bglRotatef(ang, 0.0f, 1.0f, 0.0f); bglScalef(1.0f / 1000.0f, 1.0f / 1000.0f, 1.0f / 1000.0f); bglTranslatef(-pos[0], -pos[1], -pos[2]); bglGetFloatv(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; enddrawing(); return; } // GO! polymer_displayrooms(dacursectnum); curmodelviewmatrix = rootmodelviewmatrix; // build globals used by rotatesprite viewangle = daang; set_globalang(daang); // polymost globals used by polymost_dorotatesprite gcosang = ((float)cosglobalang)/262144.f; gsinang = ((float)singlobalang)/262144.f; gcosang2 = gcosang*((float)viewingrange)/65536.f; gsinang2 = gsinang*((float)viewingrange)/65536.f; if (pr_verbosity >= 3) OSD_Printf("PR : Rooms drawn.\n"); enddrawing(); } void polymer_drawmasks(void) { bglEnable(GL_ALPHA_TEST); bglEnable(GL_BLEND); // bglEnable(GL_POLYGON_OFFSET_FILL); // while (--spritesortcnt) // { // tspriteptr[spritesortcnt] = &tsprite[spritesortcnt]; // polymer_drawsprite(spritesortcnt); // } bglEnable(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); } bglDisable(GL_CULL_FACE); // bglDisable(GL_POLYGON_OFFSET_FILL); bglDisable(GL_BLEND); bglDisable(GL_ALPHA_TEST); } void polymer_editorpick(void) { GLubyte picked[3]; int16_t num; bglReadPixels(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; bglGetDoublev(GL_MODELVIEW_MATRIX, model); bglGetDoublev(GL_PROJECTION_MATRIX, proj); bglGetIntegerv(GL_VIEWPORT, view); bglReadPixels(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; kmaterial.diffusemodulation, sizeof(GLubyte) * 4); plane->material.diffusemodulation[0] = modulation; plane->material.diffusemodulation[1] = ((GLubyte *) data)[0]; plane->material.diffusemodulation[2] = ((GLubyte *) data)[1]; plane->material.diffusemodulation[3] = 0xFF; polymer_drawplane(plane); if (oldcolor) Bmemcpy(plane->material.diffusemodulation, oldcolor, sizeof(GLubyte) * 4); } void polymer_drawmaskwall(int32_t damaskwallcnt) { tsectortype *sec; walltype *wal; _prwall *w; GLubyte oldcolor[4]; if (pr_verbosity >= 3) OSD_Printf("PR : Masked wall %i...\n", damaskwallcnt); sec = (tsectortype *)§or[sectorofwall(maskwall[damaskwallcnt])]; wal = &wall[maskwall[damaskwallcnt]]; w = prwalls[maskwall[damaskwallcnt]]; bglEnable(GL_CULL_FACE); if (searchit == 2) { polymer_drawsearchplane(&w->mask, oldcolor, 0x04, (GLubyte *)&maskwall[damaskwallcnt]); } else { calc_and_apply_fog(wal->picnum, fogpal_shade(sec, wal->shade), sec->visibility, get_floor_fogpal(sec)); polymer_drawplane(&w->mask); } bglDisable(GL_CULL_FACE); } void polymer_drawsprite(int32_t snum) { int32_t i, j, cs; _prsprite *s; tspritetype *const tspr = tspriteptr[snum]; const tsectortype *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 = (tsectortype *)§or[tspr->sectnum]; calc_and_apply_fog(tspr->picnum, fogpal_shade(sec, tspr->shade), sec->visibility, get_floor_fogpal((tsectortype *)§or[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)) { bglEnable(GL_CULL_FACE); SWITCH_CULL_DIRECTION; polymer_drawmdsprite(tspr); SWITCH_CULL_DIRECTION; bglDisable(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) bglPolygonOffset(-1.0f, -1.0f); break; case 2: prsectors[tspr->sectnum]->floorsproffset += 0.5f; if (!depth || mirrors[depth-1].plane) bglPolygonOffset(-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; bglEnable(GL_CULL_FACE); } if ((!depth || mirrors[depth-1].plane) && !pr_ati_nodepthoffset) bglEnable(GL_POLYGON_OFFSET_FILL); polymer_drawplane(&s->plane); if ((!depth || mirrors[depth-1].plane) && !pr_ati_nodepthoffset) bglDisable(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; bglDisable(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]) loadtile(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) { tsectortype *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; tspritetype 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; bglGetFloatv(GL_MODELVIEW_MATRIX, localmodelviewmatrix); bglGetFloatv(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; bglDisable(GL_DEPTH_TEST); bglColor4f(1.0f, 1.0f, 1.0f, 1.0f); polymer_drawsky(cursky, curskypal, curskyshade); bglEnable(GL_DEPTH_TEST); // depth-only occlusion testing pass // overridematerial = 0; prcanbucket = 1; while (front != back) { sec = (tsectortype *)§or[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] = (float)globalposy; pos[1] = -(float)(globalposz) / 16.0f; pos[2] = -(float)globalposx; 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]; bglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); bglDepthMask(GL_FALSE); bglGenQueriesARB(1, &queryid[sec->wallptr + i]); bglBeginQueryARB(GL_SAMPLES_PASSED_ARB, 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; bglEndQueryARB(GL_SAMPLES_PASSED_ARB); bglDepthMask(GL_TRUE); bglColorMask(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)) { bglGetQueryObjectivARB(queryid[sec->wallptr + i], GL_QUERY_RESULT_ARB, &result); bglDeleteQueriesARB(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) { bglDeleteQueriesARB(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 = §or[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) { bglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, prrts[0].fbo); bglPushAttrib(GL_VIEWPORT_BIT); bglViewport(windowx1, yres-(windowy2+1),windowx2-windowx1+1, windowy2-windowy1+1); bglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); Bmemcpy(localskymodelviewmatrix, curskymodelviewmatrix, sizeof(GLfloat) * 16); curskymodelviewmatrix = localskymodelviewmatrix; bglMatrixMode(GL_MODELVIEW); bglPushMatrix(); 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]; bglClipPlane(GL_CLIP_PLANE0, plane); polymer_inb4mirror(mirrorlist[i].plane->buffer, mirrorlist[i].plane->plane); SWITCH_CULL_DIRECTION; //bglEnable(GL_CLIP_PLANE0); if (mirrorlist[i].wallnum >= 0) preparemirror(globalposx, globalposy, globalang, 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); bglDisable(GL_CLIP_PLANE0); SWITCH_CULL_DIRECTION; bglMatrixMode(GL_MODELVIEW); bglPopMatrix(); bglPopAttrib(); bglBindFramebufferEXT(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; bglDisable(GL_CULL_FACE); drawmasks(); bglEnable(GL_CULL_FACE); } return; } static void polymer_emptybuckets(void) { _prbucket *bucket = prbuckethead; if (pr_buckets == 0) return; bglBindBufferARB(GL_ARRAY_BUFFER_ARB, prmapvbo); bglVertexPointer(3, GL_FLOAT, sizeof(_prvert), NULL); bglTexCoordPointer(2, GL_FLOAT, sizeof(_prvert), (GLvoid *)(3 * sizeof(GLfloat))); bglBindBufferARB(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) { bglUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER); prindexring = (GLuint *)bglMapBufferRange(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); bglDrawElements(GL_TRIANGLES, bucket->count, GL_UNSIGNED_INT, bucket->indiceoffset); polymer_unbindmaterial(materialbits); bucket->count = 0; bucket = bucket->next; } bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, 0); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 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 // bglDisable(GL_TEXTURE_2D); // bglBegin(GL_LINES); // bglColor4f(1.0, 0.0, 0.0, 1.0); // bglVertex3f(plane->buffer[0], // plane->buffer[1], // plane->buffer[2]); // bglVertex3f(plane->buffer[0] + plane->t[0] * 50, // plane->buffer[1] + plane->t[1] * 50, // plane->buffer[2] + plane->t[2] * 50); // bglColor4f(0.0, 1.0, 0.0, 1.0); // bglVertex3f(plane->buffer[0], // plane->buffer[1], // plane->buffer[2]); // bglVertex3f(plane->buffer[0] + plane->b[0] * 50, // plane->buffer[1] + plane->b[1] * 50, // plane->buffer[2] + plane->b[2] * 50); // bglColor4f(0.0, 0.0, 1.0, 1.0); // bglVertex3f(plane->buffer[0], // plane->buffer[1], // plane->buffer[2]); // bglVertex3f(plane->buffer[0] + plane->n[0] * 50, // plane->buffer[1] + plane->n[1] * 50, // plane->buffer[2] + plane->n[2] * 50); // bglEnd(); // bglEnable(GL_TEXTURE_2D); // debug code for drawing plane normals // bglDisable(GL_TEXTURE_2D); // bglBegin(GL_LINES); // bglColor4f(1.0, 1.0, 1.0, 1.0); // bglVertex3f(plane->buffer[0], // plane->buffer[1], // plane->buffer[2]); // bglVertex3f(plane->buffer[0] + plane->plane[0] * 50, // plane->buffer[1] + plane->plane[1] * 50, // plane->buffer[2] + plane->plane[2] * 50); // bglEnd(); // bglEnable(GL_TEXTURE_2D); if (pr_buckets && pr_vbos > 0 && prcanbucket && plane->bucket) { polymer_bucketplane(plane); return; } bglNormal3f((float)(plane->plane[0]), (float)(plane->plane[1]), (float)(plane->plane[2])); GLuint planevbo; GLintptrARB 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)) { bglBindBufferARB(GL_ARRAY_BUFFER_ARB, planevbo); bglVertexPointer(3, GL_FLOAT, sizeof(_prvert), (GLvoid *)(geomfbooffset)); bglTexCoordPointer(2, GL_FLOAT, sizeof(_prvert), (GLvoid *)(geomfbooffset + (3 * sizeof(GLfloat)))); if (plane->indices) bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, plane->ivbo); } else { bglVertexPointer(3, GL_FLOAT, sizeof(_prvert), &plane->buffer->x); bglTexCoordPointer(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) { bglVertexAttrib3fvARB(prprograms[materialbits].attrib_T, &plane->tbn[0][0]); bglVertexAttrib3fvARB(prprograms[materialbits].attrib_B, &plane->tbn[1][0]); bglVertexAttrib3fvARB(prprograms[materialbits].attrib_N, &plane->tbn[2][0]); } if (plane->indices) { if (planevbo && (pr_vbos > 0)) bglDrawElements(GL_TRIANGLES, plane->indicescount, GL_UNSIGNED_SHORT, NULL); else bglDrawElements(GL_TRIANGLES, plane->indicescount, GL_UNSIGNED_SHORT, plane->indices); } else bglDrawArrays(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)) { bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); if (plane->indices) bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 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; bglMultMatrixf(reflectionmatrix); bglPushMatrix(); bglLoadMatrixf(curskymodelviewmatrix); bglMultMatrixf(reflectionmatrix); bglGetFloatv(GL_MODELVIEW_MATRIX, curskymodelviewmatrix); bglPopMatrix(); } static void polymer_animatesprites(void) { if (asi.animatesprites) asi.animatesprites(globalposx, globalposy, 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) bglDeleteBuffersARB(1, &prsectors[i]->ceil.vbo); if (prsectors[i]->ceil.ivbo) bglDeleteBuffersARB(1, &prsectors[i]->ceil.ivbo); if (prsectors[i]->floor.vbo) bglDeleteBuffersARB(1, &prsectors[i]->floor.vbo); if (prsectors[i]->floor.ivbo) bglDeleteBuffersARB(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) bglDeleteBuffersARB(1, &prwalls[i]->wall.vbo); if (prwalls[i]->over.vbo) bglDeleteBuffersARB(1, &prwalls[i]->over.vbo); if (prwalls[i]->mask.vbo) bglDeleteBuffersARB(1, &prwalls[i]->mask.vbo); if (prwalls[i]->stuffvbo) bglDeleteBuffersARB(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) bglDeleteBuffersARB(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]) { bglDeleteTextures(1, &prbasepalmaps[i]); prbasepalmaps[i] = 0; } i++; } i = 0; while (i < MAXPALOOKUPS) { if (prlookups[i]) { bglDeleteTextures(1, &prlookups[i]); prlookups[i] = 0; } i++; } } // SECTORS static int32_t polymer_initsector(int16_t sectnum) { tsectortype *sec; _prsector* s; if (pr_verbosity >= 2) OSD_Printf("PR : Initializing sector %i...\n", sectnum); sec = (tsectortype *)§or[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; bglGenBuffersARB(1, &s->floor.vbo); bglGenBuffersARB(1, &s->ceil.vbo); bglGenBuffersARB(1, &s->floor.ivbo); bglGenBuffersARB(1, &s->ceil.ivbo); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, s->floor.vbo); bglBufferDataARB(GL_ARRAY_BUFFER_ARB, sec->wallnum * sizeof(GLfloat) * 5, NULL, mapvbousage); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, s->ceil.vbo); bglBufferDataARB(GL_ARRAY_BUFFER_ARB, sec->wallnum * sizeof(GLfloat) * 5, NULL, mapvbousage); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 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; tsectortype *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 = (tsectortype *)§or[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]) loadtile(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) { /*bglBindBufferARB(GL_ARRAY_BUFFER_ARB, s->floor.vbo); bglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sec->wallnum * sizeof(GLfloat)* 5, s->floor.buffer); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, s->ceil.vbo); bglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 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; GLintptrARB sector_offset = s->floor.mapvbo_vertoffset * sizeof(_prvert); GLsizeiptrARB cur_sector_size = sec->wallnum * sizeof(_prvert); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, prmapvbo); // floor bglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, sector_offset, cur_sector_size, s->floor.buffer); // ceiling bglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, sector_offset + cur_sector_size, cur_sector_size, s->ceil.buffer); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 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) { bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, s->floor.ivbo); bglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, s->indicescount * sizeof(GLushort), NULL, mapvbousage); bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, s->ceil.ivbo); bglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, s->indicescount * sizeof(GLushort), NULL, mapvbousage); s->oldindicescount = s->indicescount; } bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, s->floor.ivbo); bglBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, s->indicescount * sizeof(GLushort), s->floor.indices); bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, s->ceil.ivbo); bglBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, s->indicescount * sizeof(GLushort), s->ceil.indices); bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 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; tsectortype *sec; intptr_t i; if (pr_verbosity >= 2) OSD_Printf("PR : Tesselating floor of sector %i...\n", sectnum); s = prsectors[sectnum]; sec = (tsectortype *)§or[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) { tsectortype *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 = (tsectortype *)§or[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 *) §num); } else { calc_and_apply_fog(sec->floorpicnum, fogpal_shade(sec, sec->floorshade), 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 *) §num); } else { calc_and_apply_fog(sec->ceilingpicnum, fogpal_shade(sec, sec->ceilingshade), 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); bglGenBuffersARB(1, &w->wall.vbo); bglGenBuffersARB(1, &w->over.vbo); bglGenBuffersARB(1, &w->mask.vbo); bglGenBuffersARB(1, &w->stuffvbo); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, w->wall.vbo); bglBufferDataARB(GL_ARRAY_BUFFER_ARB, 4 * sizeof(GLfloat) * 5, NULL, mapvbousage); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, w->over.vbo); bglBufferDataARB(GL_ARRAY_BUFFER_ARB, 4 * sizeof(GLfloat) * 5, NULL, mapvbousage); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, w->mask.vbo); bglBufferDataARB(GL_ARRAY_BUFFER_ARB, 4 * sizeof(GLfloat) * 5, NULL, mapvbousage); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, w->stuffvbo); bglBufferDataARB(GL_ARRAY_BUFFER_ARB, 8 * sizeof(GLfloat) * 5, NULL, mapvbousage); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 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 = §or[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 = §or[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 GLintptrARB thiswalloffset = prwalldataoffset + (prwalldatasize * wallnum); const GLintptrARB thisoveroffset = thiswalloffset + proneplanesize; const GLintptrARB thismaskoffset = thisoveroffset + proneplanesize; bglBindBufferARB(GL_ARRAY_BUFFER_ARB, prmapvbo); bglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, thiswalloffset, proneplanesize, w->wall.buffer); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, prmapvbo); if (w->over.buffer) bglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, thisoveroffset, proneplanesize, w->over.buffer); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, prmapvbo); bglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, thismaskoffset, proneplanesize, w->mask.buffer); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, w->stuffvbo); bglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, 4 * sizeof(GLfloat)* 5, w->bigportal); //bglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 4 * sizeof(GLfloat)* 5, 4 * sizeof(GLfloat)* 3, w->cap); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 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) { tsectortype *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 = (tsectortype *)§or[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, fogpal_shade(sec, wal->shade), 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))) //{ // bglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // if (pr_vbos) // { // bglBindBufferARB(GL_ARRAY_BUFFER_ARB, w->stuffvbo); // bglVertexPointer(3, GL_FLOAT, 0, (const GLvoid*)(4 * sizeof(GLfloat) * 5)); // } // else // bglVertexPointer(3, GL_FLOAT, 0, w->cap); // bglDrawArrays(GL_QUADS, 0, 4); // if (pr_vbos) // bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); // bglColorMask(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 = §or[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; bglMatrixMode(GL_TEXTURE); bglLoadMatrixf(projection); bglMultMatrixf(modelview); bglGetFloatv(GL_TEXTURE_MATRIX, matrix); bglLoadIdentity(); bglMatrixMode(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, tspritetype* 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; tspritetype *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) { bglGenBuffersARB(1, &s->plane.vbo); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, s->plane.vbo); bglBufferDataARB(GL_ARRAY_BUFFER_ARB, 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_drawsearchplane(&s->plane, NULL, 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; bglMatrixMode(GL_MODELVIEW); bglPushMatrix(); bglLoadIdentity(); 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 = (viewangle + 1024) & 2047; } if (flipu) xoff = -xoff; if (yflip && alignmask!=0) yoff = -yoff; } switch (tspr->cstat & SPR_ALIGN_MASK) { case 0: ang = (float)((viewangle) & 2047) / (2048.0f / 360.0f); bglTranslatef(spos[0], spos[1], spos[2]); bglRotatef(-ang, 0.0f, 1.0f, 0.0f); bglRotatef(-horizang, 1.0f, 0.0f, 0.0f); bglTranslatef((float)(-xoff), (float)(yoff), 0.0f); bglScalef((float)(xsize), (float)(ysize), 1.0f); break; case SPR_WALL: ang = (float)((tspr->ang + 1024) & 2047) / (2048.0f / 360.0f); bglTranslatef(spos[0], spos[1], spos[2]); bglRotatef(-ang, 0.0f, 1.0f, 0.0f); bglTranslatef((float)(-xoff), (float)(yoff-centeryoff), 0.0f); bglScalef((float)(xsize), (float)(ysize), 1.0f); break; case SPR_FLOOR: ang = (float)((tspr->ang + 1024) & 2047) / (2048.0f / 360.0f); bglTranslatef(spos[0], spos[1], spos[2]); bglRotatef(-ang, 0.0f, 1.0f, 0.0f); bglTranslatef((float)(-xoff), 1.0f, (float)(yoff)); bglScalef((float)(xsize), 1.0f, (float)(ysize)); inbuffer = horizsprite; break; } bglGetFloatv(GL_MODELVIEW_MATRIX, spritemodelview); bglPopMatrix(); 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)) { bglBindBufferARB(GL_ARRAY_BUFFER_ARB, s->plane.vbo); bglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, 4 * sizeof(_prvert), s->plane.buffer); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); } else if (s->plane.vbo) // clean up the vbo if a wall/floor sprite becomes a face sprite { bglDeleteBuffersARB(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); 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] = (float)globalposy; pos[1] = -(float)(globalposz) / 16.0f; pos[2] = -(float)globalposx; bglPushMatrix(); bglLoadIdentity(); bglLoadMatrixf(curskymodelviewmatrix); bglTranslatef(pos[0], pos[1], pos[2]); bglScalef(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); bglPopMatrix(); } 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); const int32_t numskytilesm1 = (1<= MAXTILES) picnum = MAXTILES-1; DO_TILE_ANIM(picnum, 0); if (!waloff[picnum]) loadtile(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 if (!(hictinting[palnum].f & HICTINT_PRECOMPUTED)) { if (pth->flags & PTH_HIGHTILE) { if (pth->palnum != palnum || (pth->effects & HICTINT_IN_MEMORY) || (hictinting[palnum].f & HICTINT_APPLYOVERALTPAL)) hictinting_apply(glcolors[i], palnum); } else if (hictinting[palnum].f & HICTINT_USEONART) hictinting_apply(glcolors[i], palnum); } // global tinting if ((pth->flags & PTH_HIGHTILE) && have_basepal_tint()) hictinting_apply(glcolors[i], MAXPALOOKUPS-1); } 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]; bglColor4f(glcolors[tileofs][0], glcolors[tileofs][1], glcolors[tileofs][2], 1.0f); bglBindTexture(GL_TEXTURE_2D, glpics[tileofs]); bglGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &oldswrap); bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP); polymer_drawartskyquad(i, (i + 1) & (j - 1), height); bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, oldswrap); i++; } } static void polymer_drawartskyquad(int32_t p1, int32_t p2, GLfloat height) { bglBegin(GL_QUADS); bglTexCoord2f(0.0f, 0.0f); //OSD_Printf("PR: drawing %f %f %f\n", skybox[(p1 * 2) + 1], height, skybox[p1 * 2]); bglVertex3f(artskydata[(p1 * 2) + 1], height, artskydata[p1 * 2]); bglTexCoord2f(0.0f, 1.0f); //OSD_Printf("PR: drawing %f %f %f\n", skybox[(p1 * 2) + 1], -height, skybox[p1 * 2]); bglVertex3f(artskydata[(p1 * 2) + 1], -height, artskydata[p1 * 2]); bglTexCoord2f(1.0f, 1.0f); //OSD_Printf("PR: drawing %f %f %f\n", skybox[(p2 * 2) + 1], -height, skybox[p2 * 2]); bglVertex3f(artskydata[(p2 * 2) + 1], -height, artskydata[p2 * 2]); bglTexCoord2f(1.0f, 0.0f); //OSD_Printf("PR: drawing %f %f %f\n", skybox[(p2 * 2) + 1], height, skybox[p2 * 2]); bglVertex3f(artskydata[(p2 * 2) + 1], height, artskydata[p2 * 2]); bglEnd(); } 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)) { bglGenBuffersARB(1, &skyboxdatavbo); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, skyboxdatavbo); bglBufferDataARB(GL_ARRAY_BUFFER_ARB, 4 * sizeof(GLfloat) * 5 * 6, skyboxdata, modelvbousage); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); } if (pr_vbos > 0) bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 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 if (!(hictinting[palnum].f & HICTINT_PRECOMPUTED)) { if (pth->flags & PTH_HIGHTILE) { if (pth->palnum != palnum || (pth->effects & HICTINT_IN_MEMORY) || (hictinting[palnum].f & HICTINT_APPLYOVERALTPAL)) hictinting_apply(color, palnum); } else if (hictinting[palnum].f & HICTINT_USEONART) hictinting_apply(color, palnum); } // global tinting if ((pth->flags & PTH_HIGHTILE) && have_basepal_tint()) hictinting_apply(color, MAXPALOOKUPS-1); } bglColor4f(color[0], color[1], color[2], 1.0); bglBindTexture(GL_TEXTURE_2D, pth ? pth->glpic : 0); if (pr_vbos > 0) { bglVertexPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), (GLfloat*)(4 * 5 * i * sizeof(GLfloat))); bglTexCoordPointer(2, GL_FLOAT, 5 * sizeof(GLfloat), (GLfloat*)(((4 * 5 * i) + 3) * sizeof(GLfloat))); } else { bglVertexPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), &skyboxdata[4 * 5 * i]); bglTexCoordPointer(2, GL_FLOAT, 5 * sizeof(GLfloat), &skyboxdata[3 + (4 * 5 * i)]); } bglDrawArrays(GL_QUADS, 0, 4); i++; } drawingskybox = 0; if (pr_vbos > 0) bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); return; } // MDSPRITES static void polymer_drawmdsprite(tspritetype *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] = (float)globalposy; spos[1] = -(float)(globalposz) / 16.0f; spos[2] = -(float)globalposx; // 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] = (float)y - globalposy; spos2[1] = -(float)(z - globalposz) / 16.0f; spos2[2] = -(float)(x - globalposx); } 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) / (2048.0f / 360.0f); ang -= 90.0f; if (((tspr->cstat>>4) & 3) == 2) ang -= 90.0f; bglMatrixMode(GL_MODELVIEW); bglPushMatrix(); bglLoadIdentity(); 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) / (2048.0f / 360.0f) - 90.0f; radplayerang = (globalang & 2047) * 2.0f * PI / 2048.0f; cosminusradplayerang = cos(-radplayerang); sinminusradplayerang = sin(-radplayerang); hudzoom = 65536.0 / spriteext[tspr->owner].offset.z; bglTranslatef(spos[0], spos[1], spos[2]); bglRotatef(horizang, -cosminusradplayerang, 0.0f, sinminusradplayerang); bglRotatef(spriteext[tspr->owner].roll / (2048.0f / 360.0f), sinminusradplayerang, 0.0f, cosminusradplayerang); bglRotatef(-playerang, 0.0f, 1.0f, 0.0f); bglScalef(hudzoom, 1.0f, 1.0f); bglRotatef(playerang, 0.0f, 1.0f, 0.0f); bglTranslatef(spos2[0], spos2[1], spos2[2]); bglRotatef(-ang, 0.0f, 1.0f, 0.0f); } else { bglTranslatef(spos[0], spos[1], spos[2]); bglRotatef(-ang, 0.0f, 1.0f, 0.0f); } if (((tspr->cstat>>4) & 3) == 2) { bglTranslatef(0.0f, 0.0, -(float)(tilesiz[tspr->picnum].y * tspr->yrepeat) / 8.0f); bglRotatef(90.0f, 0.0f, 0.0f, 1.0f); } else bglRotatef(-90.0f, 1.0f, 0.0f, 0.0f); if ((tspr->cstat & 128) && (((tspr->cstat>>4) & 3) != 2)) bglTranslatef(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 bglTranslatef(0.0f, 0.0, m->yoffset * 64 * scale * tspr->yrepeat); if (tspr->cstat & 8) { bglTranslatef(0.0f, 0.0, (float)(tilesiz[tspr->picnum].y * tspr->yrepeat) / 4.0f); bglScalef(1.0f, 1.0f, -1.0f); } if (tspr->cstat & 4) bglScalef(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; } bglScalef(scale * tspr->xrepeat, scale * tspr->xrepeat, scale * tspr->yrepeat); bglTranslatef(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) / (2048.0f / 360.0f); rollang = (float)(spriteext[tspr->owner].roll) / (2048.0f / 360.0f); 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); bglTranslatef(-offsets[0], -offsets[1], -offsets[2]); bglRotatef(pitchang, 0.0f, 1.0f, 0.0f); bglRotatef(rollang, -1.0f, 0.0f, 0.0f); bglTranslatef(offsets[0], offsets[1], offsets[2]); } bglGetFloatv(GL_MODELVIEW_MATRIX, spritemodelview); bglPopMatrix(); bglPushMatrix(); bglMultMatrixf(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 // bglDisable(GL_TEXTURE_2D); // bglBegin(GL_LINES); // bglColor4f(1.0, 0.0, 0.0, 1.0); // bglVertex3f(m->head.frames[m->cframe].cen.x, // m->head.frames[m->cframe].cen.y, // m->head.frames[m->cframe].cen.z); // bglVertex3f(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); // bglColor4f(0.0, 1.0, 0.0, 1.0); // bglVertex3f(m->head.frames[m->cframe].cen.x, // m->head.frames[m->cframe].cen.y, // m->head.frames[m->cframe].cen.z); // bglVertex3f(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); // bglColor4f(0.0, 0.0, 1.0, 1.0); // bglVertex3f(m->head.frames[m->cframe].cen.x, // m->head.frames[m->cframe].cen.y, // m->head.frames[m->cframe].cen.z); // bglVertex3f(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); // bglEnd(); // bglEnable(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 if (!usinghighpal && !(hictinting[tspr->pal].f & 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); 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;surfihead.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 // bglDisable(GL_TEXTURE_2D); // bglBegin(GL_LINES); // bglColor4f(1.0, 1.0, 1.0, 1.0); // // int i = 0; // while (i < s->numverts) // { // bglVertex3f(v0[(i * 6) + 0], // v0[(i * 6) + 1], // v0[(i * 6) + 2]); // bglVertex3f(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++; // } // bglEnd(); // bglEnable(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); } bglEnableClientState(GL_NORMAL_ARRAY); if (pr_vbos > 1) { bglBindBufferARB(GL_ARRAY_BUFFER_ARB, m->texcoords[surfi]); bglTexCoordPointer(2, GL_FLOAT, 0, 0); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, m->geometry[surfi]); bglVertexPointer(3, GL_FLOAT, sizeof(float) * 15, (GLfloat*)(m->cframe * s->numverts * sizeof(float) * 15)); bglNormalPointer(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); } bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, m->indices[surfi]); curlight = 0; do { materialbits = polymer_bindmaterial(&mdspritematerial, modellights, modellightcount); bglDrawElements(GL_TRIANGLES, s->numtris * 3, GL_UNSIGNED_INT, 0); polymer_unbindmaterial(materialbits); } while ((++curlight < modellightcount) && (curlight < pr_maxlightpasses)); bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); } else { bglVertexPointer(3, GL_FLOAT, sizeof(float) * 15, v0); bglNormalPointer(GL_FLOAT, sizeof(float) * 15, v0 + 3); bglTexCoordPointer(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); bglDrawElements(GL_TRIANGLES, s->numtris * 3, GL_UNSIGNED_INT, s->tris); polymer_unbindmaterial(materialbits); } while ((++curlight < modellightcount) && (curlight < pr_maxlightpasses)); } bglDisableClientState(GL_NORMAL_ARRAY); } bglPopMatrix(); 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)); bglGenBuffersARB(m->head.numsurfs, m->indices); bglGenBuffersARB(m->head.numsurfs, m->texcoords); bglGenBuffersARB(m->head.numsurfs, m->geometry); i = 0; while (i < m->head.numsurfs) { s = &m->head.surfs[i]; bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, m->indices[i]); bglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, s->numtris * sizeof(md3tri_t), s->tris, modelvbousage); bglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, m->texcoords[i]); bglBufferDataARB(GL_ARRAY_BUFFER_ARB, s->numverts * sizeof(md3uv_t), s->uv, modelvbousage); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, m->geometry[i]); bglBufferDataARB(GL_ARRAY_BUFFER_ARB, s->numframes * s->numverts * sizeof(float) * (15), s->geometry, modelvbousage); bglBindBufferARB(GL_ARRAY_BUFFER_ARB, 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++; } bglGenTextures(1, &prartmaps[tilenum]); bglBindTexture(GL_TEXTURE_2D, prartmaps[tilenum]); bglTexImage2D(GL_TEXTURE_2D, 0, GL_R8, tilesiz[tilenum].x, tilesiz[tilenum].y, 0, GL_RED, GL_UNSIGNED_BYTE, tempbuffer); bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); bglBindTexture(GL_TEXTURE_2D, 0); Bfree(tempbuffer); } if (!prbasepalmaps[curbasepal]) { bglGenTextures(1, &prbasepalmaps[curbasepal]); bglBindTexture(GL_TEXTURE_2D, prbasepalmaps[curbasepal]); bglTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, basepaltable[curbasepal]); bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP); bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP); bglBindTexture(GL_TEXTURE_2D, 0); } if (!prlookups[pal]) { bglGenTextures(1, &prlookups[pal]); bglBindTexture(GL_TEXTURE_RECTANGLE, prlookups[pal]); bglTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_R8, 256, numshades, 0, GL_RED, GL_UNSIGNED_BYTE, palookup[pal]); bglTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST); bglTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST); bglTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP); bglTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP); bglBindTexture(GL_TEXTURE_RECTANGLE, 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]) loadtile(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)) { 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) / 16.0f); // 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 if (!(hictinting[pal].f & HICTINT_PRECOMPUTED)) { if (pth->flags & PTH_HIGHTILE) { if (pth->palnum != pal || (pth->effects & HICTINT_IN_MEMORY) || (hictinting[pal].f & HICTINT_APPLYOVERALTPAL)) hictinting_apply_ub(material->diffusemodulation, pal); } else if (hictinting[pal].f & HICTINT_USEONART) hictinting_apply_ub(material->diffusemodulation, pal); } // global tinting if ((pth->flags & PTH_HIGHTILE) && !usinghighpal && have_basepal_tint()) hictinting_apply_ub(material->diffusemodulation, MAXPALOOKUPS-1); // 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) && (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) && (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) && (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) && (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); bglUseProgramObjectARB(prprograms[programbits].handle); // --------- bit setup texunit = 0; // PR_BIT_ANIM_INTERPOLATION if (programbits & prprogrambits[PR_BIT_ANIM_INTERPOLATION].bit) { bglEnableVertexAttribArrayARB(prprograms[programbits].attrib_nextFrameData); if (prprograms[programbits].attrib_nextFrameNormal != -1) bglEnableVertexAttribArrayARB(prprograms[programbits].attrib_nextFrameNormal); bglVertexAttribPointerARB(prprograms[programbits].attrib_nextFrameData, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 15, material->nextframedata); if (prprograms[programbits].attrib_nextFrameNormal != -1) bglVertexAttribPointerARB(prprograms[programbits].attrib_nextFrameNormal, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 15, material->nextframedata + 3); bglUniform1fARB(prprograms[programbits].uniform_frameProgress, material->frameprogress); } // PR_BIT_LIGHTING_PASS if (programbits & prprogrambits[PR_BIT_LIGHTING_PASS].bit) { bglPushAttrib(GL_COLOR_BUFFER_BIT); bglEnable(GL_BLEND); bglBlendFunc(GL_ONE, GL_ONE); if (prlights[lights[curlight]].publicflags.negative) { bglBlendEquation(GL_FUNC_REVERSE_SUBTRACT); } } // PR_BIT_NORMAL_MAP if (programbits & prprogrambits[PR_BIT_NORMAL_MAP].bit) { float pos[3], bias[2]; pos[0] = (float)globalposy; pos[1] = -(float)(globalposz) / 16.0f; pos[2] = -(float)globalposx; bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(GL_TEXTURE_2D, material->normalmap); if (material->mdspritespace == GL_TRUE) { float mdspritespacepos[3]; polymer_transformpoint(pos, mdspritespacepos, (float *)mdspritespace); bglUniform3fvARB(prprograms[programbits].uniform_eyePosition, 1, mdspritespacepos); } else bglUniform3fvARB(prprograms[programbits].uniform_eyePosition, 1, pos); bglUniform1iARB(prprograms[programbits].uniform_normalMap, texunit); if (pr_overrideparallax) { bias[0] = pr_parallaxscale; bias[1] = pr_parallaxbias; bglUniform2fvARB(prprograms[programbits].uniform_normalBias, 1, bias); } else bglUniform2fvARB(prprograms[programbits].uniform_normalBias, 1, material->normalbias); if (material->tbn) { bglEnableVertexAttribArrayARB(prprograms[programbits].attrib_T); bglEnableVertexAttribArrayARB(prprograms[programbits].attrib_B); bglEnableVertexAttribArrayARB(prprograms[programbits].attrib_N); bglVertexAttribPointerARB(prprograms[programbits].attrib_T, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 15, material->tbn); bglVertexAttribPointerARB(prprograms[programbits].attrib_B, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 15, material->tbn + 3); bglVertexAttribPointerARB(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) { bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(GL_TEXTURE_2D, material->artmap); bglUniform1iARB(prprograms[programbits].uniform_artMap, texunit); texunit++; bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(GL_TEXTURE_2D, material->basepalmap); bglUniform1iARB(prprograms[programbits].uniform_basePalMap, texunit); texunit++; bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(GL_TEXTURE_RECTANGLE, material->lookupmap); bglUniform1iARB(prprograms[programbits].uniform_lookupMap, texunit); texunit++; bglUniform1fARB(prprograms[programbits].uniform_shadeOffset, (GLfloat)material->shadeoffset); bglUniform1fARB(prprograms[programbits].uniform_visibility, globalvisibility/2048.0 * material->visibility); } // PR_BIT_DIFFUSE_MAP if (programbits & prprogrambits[PR_BIT_DIFFUSE_MAP].bit) { bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(GL_TEXTURE_2D, material->diffusemap); bglUniform1iARB(prprograms[programbits].uniform_diffuseMap, texunit); bglUniform2fvARB(prprograms[programbits].uniform_diffuseScale, 1, material->diffusescale); texunit++; } // PR_BIT_HIGHPALOOKUP_MAP if (programbits & prprogrambits[PR_BIT_HIGHPALOOKUP_MAP].bit) { bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(GL_TEXTURE_3D, material->highpalookupmap); bglUniform1iARB(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]; } bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(GL_TEXTURE_2D, material->detailmap); bglUniform1iARB(prprograms[programbits].uniform_detailMap, texunit); bglUniform2fvARB(prprograms[programbits].uniform_detailScale, 1, scale); texunit++; } // PR_BIT_DIFFUSE_MODULATION if (programbits & prprogrambits[PR_BIT_DIFFUSE_MODULATION].bit) { bglColor4ub(material->diffusemodulation[0], material->diffusemodulation[1], material->diffusemodulation[2], material->diffusemodulation[3]); } // PR_BIT_SPECULAR_MAP if (programbits & prprogrambits[PR_BIT_SPECULAR_MAP].bit) { bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(GL_TEXTURE_2D, material->specmap); bglUniform1iARB(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; bglUniform2fvARB(prprograms[programbits].uniform_specMaterial, 1, specmaterial); } else bglUniform2fvARB(prprograms[programbits].uniform_specMaterial, 1, material->specmaterial); } // PR_BIT_MIRROR_MAP if (programbits & prprogrambits[PR_BIT_MIRROR_MAP].bit) { bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(GL_TEXTURE_RECTANGLE, material->mirrormap); bglUniform1iARB(prprograms[programbits].uniform_mirrorMap, texunit); texunit++; } #ifdef PR_LINEAR_FOG if (programbits & prprogrambits[PR_BIT_FOG].bit) { bglUniform1iARB(prprograms[programbits].uniform_linearFog, r_usenewshading >= 2); } #endif // PR_BIT_GLOW_MAP if (programbits & prprogrambits[PR_BIT_GLOW_MAP].bit) { bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(GL_TEXTURE_2D, material->glowmap); bglUniform1iARB(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]); bglUniform3fvARB(prprograms[programbits].uniform_spotDir, 1, dir); bglUniform2fvARB(prprograms[programbits].uniform_spotRadius, 1, indir); // PR_BIT_PROJECTION_MAP if (programbits & prprogrambits[PR_BIT_PROJECTION_MAP].bit) { GLfloat matrix[16]; bglMatrixMode(GL_TEXTURE); bglLoadMatrixf(shadowBias); bglMultMatrixf(prlights[lights[curlight]].proj); bglMultMatrixf(prlights[lights[curlight]].transform); if (material->mdspritespace == GL_TRUE) bglMultMatrixf(spritemodelview); bglGetFloatv(GL_TEXTURE_MATRIX, matrix); bglLoadIdentity(); bglMatrixMode(GL_MODELVIEW); bglUniformMatrix4fvARB(prprograms[programbits].uniform_shadowProjMatrix, 1, GL_FALSE, matrix); // PR_BIT_SHADOW_MAP if (programbits & prprogrambits[PR_BIT_SHADOW_MAP].bit) { bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(prrts[prlights[lights[curlight]].rtindex].target, prrts[prlights[lights[curlight]].rtindex].z); bglUniform1iARB(prprograms[programbits].uniform_shadowMap, texunit); texunit++; } // PR_BIT_LIGHT_MAP if (programbits & prprogrambits[PR_BIT_LIGHT_MAP].bit) { bglActiveTextureARB(texunit + GL_TEXTURE0_ARB); bglBindTexture(GL_TEXTURE_2D, prlights[lights[curlight]].lightmap); bglUniform1iARB(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]; } bglLightfv(GL_LIGHT0, GL_AMBIENT, pos); bglLightfv(GL_LIGHT0, GL_DIFFUSE, color); if (material->mdspritespace == GL_TRUE) { float mdspritespacepos[3]; polymer_transformpoint(inpos, mdspritespacepos, (float *)mdspritespace); bglLightfv(GL_LIGHT0, GL_SPECULAR, mdspritespacepos); } else { bglLightfv(GL_LIGHT0, GL_SPECULAR, inpos); } bglLightfv(GL_LIGHT0, GL_LINEAR_ATTENUATION, &range[1]); } bglActiveTextureARB(GL_TEXTURE0_ARB); 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) bglDisableVertexAttribArrayARB(prprograms[programbits].attrib_nextFrameNormal); bglDisableVertexAttribArrayARB(prprograms[programbits].attrib_nextFrameData); } // PR_BIT_LIGHTING_PASS if (programbits & prprogrambits[PR_BIT_LIGHTING_PASS].bit) { bglPopAttrib(); } // PR_BIT_NORMAL_MAP if (programbits & prprogrambits[PR_BIT_NORMAL_MAP].bit) { bglDisableVertexAttribArrayARB(prprograms[programbits].attrib_T); bglDisableVertexAttribArrayARB(prprograms[programbits].attrib_B); bglDisableVertexAttribArrayARB(prprograms[programbits].attrib_N); } bglUseProgramObjectARB(0); } static void polymer_compileprogram(int32_t programbits) { int32_t i, enabledbits; GLhandleARB vert, frag, program; const GLcharARB* source[PR_BIT_COUNT * 2]; GLcharARB infobuffer[PR_INFO_LOG_BUFFER_SIZE]; GLint linkstatus; // --------- VERTEX vert = bglCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); 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++; } bglShaderSourceARB(vert, enabledbits, source, NULL); bglCompileShaderARB(vert); // --------- FRAGMENT frag = bglCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); 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++; } bglShaderSourceARB(frag, enabledbits, (const GLcharARB**)source, NULL); bglCompileShaderARB(frag); // --------- PROGRAM program = bglCreateProgramObjectARB(); bglAttachObjectARB(program, vert); bglAttachObjectARB(program, frag); bglLinkProgramARB(program); bglGetObjectParameterivARB(program, GL_OBJECT_LINK_STATUS_ARB, &linkstatus); bglGetInfoLogARB(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); bglGetShaderSourceARB(vert, PR_INFO_LOG_BUFFER_SIZE, NULL, infobuffer); if (pr_verbosity >= 1) OSD_Printf("PR : Vertex source dump:\n%s\n", infobuffer); bglGetShaderSourceARB(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 = bglGetAttribLocationARB(program, "nextFrameData"); prprograms[programbits].attrib_nextFrameNormal = bglGetAttribLocationARB(program, "nextFrameNormal"); prprograms[programbits].uniform_frameProgress = bglGetUniformLocationARB(program, "frameProgress"); } // PR_BIT_NORMAL_MAP if (programbits & prprogrambits[PR_BIT_NORMAL_MAP].bit) { prprograms[programbits].attrib_T = bglGetAttribLocationARB(program, "T"); prprograms[programbits].attrib_B = bglGetAttribLocationARB(program, "B"); prprograms[programbits].attrib_N = bglGetAttribLocationARB(program, "N"); prprograms[programbits].uniform_eyePosition = bglGetUniformLocationARB(program, "eyePosition"); prprograms[programbits].uniform_normalMap = bglGetUniformLocationARB(program, "normalMap"); prprograms[programbits].uniform_normalBias = bglGetUniformLocationARB(program, "normalBias"); } // PR_BIT_ART_MAP if (programbits & prprogrambits[PR_BIT_ART_MAP].bit) { prprograms[programbits].uniform_artMap = bglGetUniformLocationARB(program, "artMap"); prprograms[programbits].uniform_basePalMap = bglGetUniformLocationARB(program, "basePalMap"); prprograms[programbits].uniform_lookupMap = bglGetUniformLocationARB(program, "lookupMap"); prprograms[programbits].uniform_shadeOffset = bglGetUniformLocationARB(program, "shadeOffset"); prprograms[programbits].uniform_visibility = bglGetUniformLocationARB(program, "visibility"); } // PR_BIT_DIFFUSE_MAP if (programbits & prprogrambits[PR_BIT_DIFFUSE_MAP].bit) { prprograms[programbits].uniform_diffuseMap = bglGetUniformLocationARB(program, "diffuseMap"); prprograms[programbits].uniform_diffuseScale = bglGetUniformLocationARB(program, "diffuseScale"); } // PR_BIT_HIGHPALOOKUP_MAP if (programbits & prprogrambits[PR_BIT_HIGHPALOOKUP_MAP].bit) { prprograms[programbits].uniform_highPalookupMap = bglGetUniformLocationARB(program, "highPalookupMap"); } // PR_BIT_DIFFUSE_DETAIL_MAP if (programbits & prprogrambits[PR_BIT_DIFFUSE_DETAIL_MAP].bit) { prprograms[programbits].uniform_detailMap = bglGetUniformLocationARB(program, "detailMap"); prprograms[programbits].uniform_detailScale = bglGetUniformLocationARB(program, "detailScale"); } // PR_BIT_SPECULAR_MAP if (programbits & prprogrambits[PR_BIT_SPECULAR_MAP].bit) { prprograms[programbits].uniform_specMap = bglGetUniformLocationARB(program, "specMap"); } // PR_BIT_SPECULAR_MATERIAL if (programbits & prprogrambits[PR_BIT_SPECULAR_MATERIAL].bit) { prprograms[programbits].uniform_specMaterial = bglGetUniformLocationARB(program, "specMaterial"); } // PR_BIT_MIRROR_MAP if (programbits & prprogrambits[PR_BIT_MIRROR_MAP].bit) { prprograms[programbits].uniform_mirrorMap = bglGetUniformLocationARB(program, "mirrorMap"); } #ifdef PR_LINEAR_FOG if (programbits & prprogrambits[PR_BIT_FOG].bit) { prprograms[programbits].uniform_linearFog = bglGetUniformLocationARB(program, "linearFog"); } #endif // PR_BIT_GLOW_MAP if (programbits & prprogrambits[PR_BIT_GLOW_MAP].bit) { prprograms[programbits].uniform_glowMap = bglGetUniformLocationARB(program, "glowMap"); } // PR_BIT_PROJECTION_MAP if (programbits & prprogrambits[PR_BIT_PROJECTION_MAP].bit) { prprograms[programbits].uniform_shadowProjMatrix = bglGetUniformLocationARB(program, "shadowProjMatrix"); } // PR_BIT_SHADOW_MAP if (programbits & prprogrambits[PR_BIT_SHADOW_MAP].bit) { prprograms[programbits].uniform_shadowMap = bglGetUniformLocationARB(program, "shadowMap"); } // PR_BIT_LIGHT_MAP if (programbits & prprogrambits[PR_BIT_LIGHT_MAP].bit) { prprograms[programbits].uniform_lightMap = bglGetUniformLocationARB(program, "lightMap"); } // PR_BIT_SPOT_LIGHT if (programbits & prprogrambits[PR_BIT_SPOT_LIGHT].bit) { prprograms[programbits].uniform_spotDir = bglGetUniformLocationARB(program, "spotDir"); prprograms[programbits].uniform_spotRadius = bglGetUniformLocationARB(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]) loadtile(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 = §or[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) / (2048.0f / 360.0f); ang = (float)(light->angle) / (2048.0f / 360.0f); horizang = (float)(-getangle(128, light->horiz-100)) / (2048.0f / 360.0f); bglMatrixMode(GL_PROJECTION); bglPushMatrix(); bglLoadIdentity(); bgluPerspective(radius * 2, 1, 0.1f, light->range / 1000.0f); bglGetFloatv(GL_PROJECTION_MATRIX, light->proj); bglPopMatrix(); bglMatrixMode(GL_MODELVIEW); bglPushMatrix(); bglLoadIdentity(); bglRotatef(horizang, 1.0f, 0.0f, 0.0f); bglRotatef(ang, 0.0f, 1.0f, 0.0f); bglScalef(1.0f / 1000.0f, 1.0f / 1000.0f, 1.0f / 1000.0f); bglTranslatef(-lightpos[0], -lightpos[1], -lightpos[2]); bglGetFloatv(GL_MODELVIEW_MATRIX, light->transform); bglPopMatrix(); 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 = §or[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) { int16_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 = globalang; 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); bglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, prrts[prlights[i].rtindex].fbo); bglPushAttrib(GL_VIEWPORT_BIT); bglViewport(0, 0, prrts[prlights[i].rtindex].xdim, prrts[prlights[i].rtindex].ydim); bglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); bglMatrixMode(GL_PROJECTION); bglPushMatrix(); bglLoadMatrixf(prlights[i].proj); bglMatrixMode(GL_MODELVIEW); bglLoadMatrixf(prlights[i].transform); bglEnable(GL_POLYGON_OFFSET_FILL); bglPolygonOffset(5, SHADOW_DEPTH_OFFSET); set_globalpos(prlights[i].x, prlights[i].y, prlights[i].z); // build globals used by rotatesprite viewangle = prlights[i].angle; set_globalang(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; bglDisable(GL_POLYGON_OFFSET_FILL); bglMatrixMode(GL_PROJECTION); bglPopMatrix(); bglPopAttrib(); bglBindFramebufferEXT(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