Index: lightenv.h =================================================================== --- lightenv.h (revision 4868) +++ lightenv.h (working copy) @@ -15,10 +15,14 @@ #include #include +#include #include static const unsigned int MaxLights = 8; +class Body; +class RingSystem; + class DirectionalLight { public: @@ -37,6 +41,8 @@ class EclipseShadow { public: + const Body* caster; + Eigen::Quaternionf casterOrientation; Eigen::Vector3f origin; Eigen::Vector3f direction; float penumbraRadius; @@ -44,18 +50,39 @@ float maxDepth; }; +class RingShadow +{ +public: + RingSystem* ringSystem; + Eigen::Quaternionf casterOrientation; + Eigen::Vector3f origin; + Eigen::Vector3f direction; + float texLod; +}; + class LightingState { public: LightingState() : nLights(0), + shadowingRingSystem(NULL), eyeDir_obj(-Eigen::Vector3f::UnitZ()), eyePos_obj(-Eigen::Vector3f::UnitZ()) - { shadows[0] = NULL; }; + { + shadows[0] = NULL; + for (unsigned int i = 0; i < MaxLights; ++i) + { + ringShadows[i].ringSystem = NULL; + } + }; unsigned int nLights; DirectionalLight lights[MaxLights]; std::vector* shadows[MaxLights]; + RingShadow ringShadows[MaxLights]; + RingSystem* shadowingRingSystem; // NULL when there are no ring shadows + Eigen::Vector3f ringPlaneNormal; + Eigen::Vector3f ringCenter; Eigen::Vector3f eyeDir_obj; Eigen::Vector3f eyePos_obj; Index: rendcontext.cpp =================================================================== --- rendcontext.cpp (revision 4868) +++ rendcontext.cpp (working copy) @@ -11,6 +11,7 @@ #include #include "rendcontext.h" #include "texmanager.h" +#include "body.h" #include #include "vecgl.h" @@ -554,8 +555,8 @@ { if (lightingState.shadows[li] && !lightingState.shadows[li]->empty()) { - unsigned int nShadows = (unsigned int) min((size_t) MaxShaderShadows, lightingState.shadows[li]->size()); - shaderProps.setShadowCountForLight(li, nShadows); + unsigned int nShadows = (unsigned int) min((size_t) MaxShaderEclipseShadows, lightingState.shadows[li]->size()); + shaderProps.setEclipseShadowCountForLight(li, nShadows); totalShadows += nShadows; } } @@ -646,6 +647,39 @@ } } + if (lightingState.shadowingRingSystem) + { + Texture* ringsTex = lightingState.shadowingRingSystem->texture.find(medres); + if (ringsTex != NULL) + { + glActiveTextureARB(GL_TEXTURE0_ARB + nTextures); + ringsTex->bind(); + textures[nTextures++] = ringsTex; + + // Tweak the texture--set clamp to border and a border color with + // a zero alpha. + float bc[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bc); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_BORDER_ARB); + glActiveTextureARB(GL_TEXTURE0_ARB); + + shaderProps.texUsage |= ShaderProperties::RingShadowTexture; + for (unsigned int lightIndex = 0; lightIndex < lightingState.nLights; lightIndex++) + { + if (lightingState.lights[lightIndex].castsShadows && + lightingState.shadowingRingSystem == lightingState.ringShadows[lightIndex].ringSystem) + { + shaderProps.setRingShadowForLight(lightIndex, true); + } + else + { + shaderProps.setRingShadowForLight(lightIndex, false); + } + } + } + } + if (usePointSize) shaderProps.texUsage |= ShaderProperties::PointSprite; if (useColors) @@ -681,7 +715,7 @@ prog->setLightParameters(lightingState, diffuse, m.specular, m.emissive); - if (shaderProps.shadowCounts != 0) + if (shaderProps.hasEclipseShadows() != 0) prog->setEclipseShadowParameters(lightingState, objRadius, objOrientation); // TODO: handle emissive color @@ -710,6 +744,25 @@ prog->pointScale = getPointScale(); } + // Ring shadow parameters + if ((shaderProps.texUsage & ShaderProperties::RingShadowTexture) != 0) + { + const RingSystem* rings = lightingState.shadowingRingSystem; + float ringWidth = rings->outerRadius - rings->innerRadius; + prog->ringRadius = rings->innerRadius / objRadius; + prog->ringWidth = objRadius / ringWidth; + prog->ringPlane = Hyperplane(lightingState.ringPlaneNormal, lightingState.ringCenter / objRadius).coeffs(); + prog->ringCenter = lightingState.ringCenter / objRadius; + + for (unsigned int lightIndex = 0; lightIndex < lightingState.nLights; ++lightIndex) + { + if (shaderProps.hasRingShadowForLight(lightIndex)) + { + prog->ringShadowLOD[lightIndex] = lightingState.ringShadows[lightIndex].texLod; + } + } + } + Mesh::BlendMode newBlendMode = Mesh::InvalidBlend; if (m.opacity != 1.0f || m.blend == Mesh::AdditiveBlend || Index: renderglsl.cpp =================================================================== --- renderglsl.cpp (revision 4868) +++ renderglsl.cpp (working copy) @@ -53,7 +53,6 @@ // Render a planet sphere with GLSL shaders void renderSphere_GLSL(const RenderInfo& ri, const LightingState& ls, - RingSystem* rings, Atmosphere* atmosphere, float cloudTexOffset, float radius, @@ -118,6 +117,7 @@ textures[nTextures++] = ri.overlayTex; } +#if 0 if (rings != NULL && (renderFlags & Renderer::ShowRingShadows) != 0) { Texture* ringsTex = rings->texture.find(textureRes); @@ -138,6 +138,7 @@ shadprop.texUsage |= ShaderProperties::RingShadowTexture; } } +#endif if (atmosphere != NULL) { @@ -188,6 +189,15 @@ glActiveTextureARB(GL_TEXTURE0_ARB + nTextures); cloudTex->bind(); glActiveTextureARB(GL_TEXTURE0_ARB); + + for (unsigned int lightIndex = 0; lightIndex < ls.nLights; lightIndex++) + { + if (ls.lights[lightIndex].castsShadows) + { + shadprop.setCloudShadowForLight(lightIndex, true); + } + } + } } } @@ -196,16 +206,48 @@ // Track the total number of shadows; if there are too many, we'll have // to fall back to multipass. unsigned int totalShadows = 0; + for (unsigned int li = 0; li < ls.nLights; li++) { if (ls.shadows[li] && !ls.shadows[li]->empty()) { - unsigned int nShadows = (unsigned int) min((size_t) MaxShaderShadows, ls.shadows[li]->size()); - shadprop.setShadowCountForLight(li, nShadows); + unsigned int nShadows = (unsigned int) min((size_t) MaxShaderEclipseShadows, ls.shadows[li]->size()); + shadprop.setEclipseShadowCountForLight(li, nShadows); totalShadows += nShadows; } } + if (ls.shadowingRingSystem) + { + Texture* ringsTex = ls.shadowingRingSystem->texture.find(textureRes); + if (ringsTex != NULL) + { + glActiveTextureARB(GL_TEXTURE0_ARB + nTextures); + ringsTex->bind(); + nTextures++; + + // Tweak the texture--set clamp to border and a border color with + // a zero alpha. + float bc[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bc); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_BORDER_ARB); + glActiveTextureARB(GL_TEXTURE0_ARB); + + shadprop.texUsage |= ShaderProperties::RingShadowTexture; + + for (unsigned int lightIndex = 0; lightIndex < ls.nLights; lightIndex++) + { + if (ls.lights[lightIndex].castsShadows && + ls.shadowingRingSystem == ls.ringShadows[lightIndex].ringSystem) + { + shadprop.setRingShadowForLight(lightIndex, true); + } + } + } + } + + // Get a shader for the current rendering configuration CelestiaGLProgram* prog = GetShaderManager().getShader(shadprop); if (prog == NULL) @@ -226,9 +268,18 @@ if (shadprop.texUsage & ShaderProperties::RingShadowTexture) { - float ringWidth = rings->outerRadius - rings->innerRadius; - prog->ringRadius = rings->innerRadius / radius; + float ringWidth = ls.shadowingRingSystem->outerRadius - ls.shadowingRingSystem->innerRadius; + prog->ringRadius = ls.shadowingRingSystem->innerRadius / radius; prog->ringWidth = radius / ringWidth; + prog->ringPlane = Hyperplane(ls.ringPlaneNormal, ls.ringCenter / radius).coeffs(); + prog->ringCenter = ls.ringCenter / radius; + for (unsigned int lightIndex = 0; lightIndex < ls.nLights; ++lightIndex) + { + if (shadprop.hasRingShadowForLight(lightIndex)) + { + prog->ringShadowLOD[lightIndex] = ls.ringShadows[lightIndex].texLod; + } + } } if (shadprop.texUsage & ShaderProperties::CloudShadowTexture) @@ -242,7 +293,7 @@ prog->setAtmosphereParameters(*atmosphere, radius, radius); } - if (shadprop.shadowCounts != 0) + if (shadprop.hasEclipseShadows() != 0) prog->setEclipseShadowParameters(ls, radius, planetOrientation); glColor(ri.color); @@ -357,7 +408,6 @@ Texture* cloudTex, Texture* cloudNormalMap, float texOffset, - RingSystem* rings, float radius, unsigned int textureRes, int renderFlags, @@ -389,6 +439,7 @@ shadprop.texUsage |= ShaderProperties::CompressedNormalTexture; } +#if 0 if (rings != NULL && (renderFlags & Renderer::ShowRingShadows) != 0) { Texture* ringsTex = rings->texture.find(textureRes); @@ -409,7 +460,8 @@ shadprop.texUsage |= ShaderProperties::RingShadowTexture; } } - +#endif + if (atmosphere != NULL) { if (renderFlags & Renderer::ShowAtmospheres) @@ -429,8 +481,8 @@ { if (ls.shadows[li] && !ls.shadows[li]->empty()) { - unsigned int nShadows = (unsigned int) min((size_t) MaxShaderShadows, ls.shadows[li]->size()); - shadprop.setShadowCountForLight(li, nShadows); + unsigned int nShadows = (unsigned int) min((size_t) MaxShaderEclipseShadows, ls.shadows[li]->size()); + shadprop.setEclipseShadowCountForLight(li, nShadows); totalShadows += nShadows; } } @@ -455,13 +507,15 @@ prog->setAtmosphereParameters(*atmosphere, radius, cloudRadius); } +#if 0 if (shadprop.texUsage & ShaderProperties::RingShadowTexture) { float ringWidth = rings->outerRadius - rings->innerRadius; prog->ringRadius = rings->innerRadius / cloudRadius; prog->ringWidth = 1.0f / (ringWidth / cloudRadius); } - +#endif + if (shadprop.shadowCounts != 0) prog->setEclipseShadowParameters(ls, cloudRadius, planetOrientation); @@ -599,7 +653,7 @@ { // Set one shadow (the planet's) per light for (unsigned int li = 0; li < ls.nLights; li++) - shadprop.setShadowCountForLight(li, 1); + shadprop.setEclipseShadowCountForLight(li, 1); } if (ringsTex) Index: shadermanager.h =================================================================== --- shadermanager.h (revision 4868) +++ shadermanager.h (working copy) @@ -27,8 +27,20 @@ bool usesShadows() const; bool usesFragmentLighting() const; bool usesTangentSpaceLighting() const; - unsigned int getShadowCountForLight(unsigned int) const; - void setShadowCountForLight(unsigned int, unsigned int); + + unsigned int getEclipseShadowCountForLight(unsigned int lightIndex) const; + void setEclipseShadowCountForLight(unsigned int lightIndex, unsigned int shadowCount); + bool hasEclipseShadows() const; + bool hasRingShadowForLight(unsigned int lightIndex) const; + void setRingShadowForLight(unsigned int lightIndex, bool enabled); + bool hasRingShadows() const; + void setSelfShadowForLight(unsigned int lightIndex, bool enabled); + bool hasSelfShadowForLight(unsigned int lightIndex) const; + bool hasSelfShadows() const; + void setCloudShadowForLight(unsigned int lightIndex, bool enabled); + bool hasCloudShadowForLight(unsigned int lightIndex) const; + bool hasCloudShadows() const; + bool hasShadowsForLight(unsigned int) const; bool hasSharedTextureCoords() const; bool hasSpecular() const; @@ -74,22 +86,45 @@ VolumetricAbsorptionEffect = 0x0002, VolumetricEmissionEffect = 0x0004, }; - + public: unsigned short nLights; unsigned short texUsage; unsigned short lightModel; - // Two bits per light, up to eight lights + three shadows per light - unsigned short shadowCounts; + // Eight bits per light, up to four lights + // For each light: + // Bits 0-1, eclipse shadow count, from 0-3 + // Bit 2, on if there are ring shadows + // Bit 3, on for self shadowing + // Bit 4, on for cloud shadows + uint32 shadowCounts; // Effects that may be applied with any light model unsigned short effects; + + private: + enum + { + ShadowBitsPerLight = 4, + }; + + enum + { + EclipseShadowMask = 0x3, + RingShadowMask = 0x4, + SelfShadowMask = 0x8, + CloudShadowMask = 0x10, + AnyEclipseShadowMask = 0x03030303, + AnyRingShadowMask = 0x04040404, + AnySelfShadowMask = 0x08080808, + AnyCloudShadowMask = 0x10101010, + }; }; static const unsigned int MaxShaderLights = 4; -static const unsigned int MaxShaderShadows = 3; +static const unsigned int MaxShaderEclipseShadows = 3; struct CelestiaGLProgramLight { Vec3ShaderParameter direction; @@ -135,6 +170,7 @@ Vec3ShaderParameter fragLightColor[MaxShaderLights]; Vec3ShaderParameter fragLightSpecColor[MaxShaderLights]; FloatShaderParameter fragLightBrightness[MaxShaderLights]; + FloatShaderParameter ringShadowLOD[MaxShaderLights]; Vec3ShaderParameter eyePosition; FloatShaderParameter shininess; Vec3ShaderParameter ambientColor; @@ -145,6 +181,8 @@ FloatShaderParameter ringWidth; FloatShaderParameter ringRadius; + Vec4ShaderParameter ringPlane; + Vec3ShaderParameter ringCenter; // Mix of Lambertian and "lunar" (Lommel-Seeliger) photometric models. // 0 = pure Lambertian, 1 = L-S @@ -191,7 +229,7 @@ // Scale factor for point sprites FloatShaderParameter pointScale; - CelestiaGLProgramShadow shadows[MaxShaderLights][MaxShaderShadows]; + CelestiaGLProgramShadow shadows[MaxShaderLights][MaxShaderEclipseShadows]; private: void initParameters(); Index: render.h =================================================================== --- render.h (revision 4868) +++ render.h (working copy) @@ -539,9 +539,9 @@ bool testEclipse(const Body& receiver, const Body& caster, - const DirectionalLight& light, - double now, - vector& shadows); + LightingState& lightingState, + unsigned int lightIndex, + double now); void labelConstellations(const std::vector& asterisms, const Observer& observer); Index: render.cpp =================================================================== --- render.cpp (revision 4868) +++ render.cpp (working copy) @@ -6732,8 +6732,7 @@ RenderInfo ri; float altitude = distance - obj.radius; - float discSizeInPixels = obj.radius / - (max(nearPlaneDistance, altitude) * pixelSize); + float discSizeInPixels = obj.radius / (max(nearPlaneDistance, altitude) * pixelSize); ri.sunDir_eye = Vector3f::UnitY(); ri.sunDir_obj = Vector3f::UnitY(); @@ -6967,7 +6966,7 @@ switch (context->getRenderPath()) { case GLContext::GLPath_GLSL: - renderSphere_GLSL(ri, ls, obj.rings, + renderSphere_GLSL(ri, ls, const_cast(obj.atmosphere), cloudTexOffset, obj.radius, textureResolution, @@ -7185,7 +7184,6 @@ cloudTex, cloudNormalMap, cloudTexOffset, - obj.rings, radius, textureResolution, renderFlags, @@ -7332,10 +7330,14 @@ bool Renderer::testEclipse(const Body& receiver, const Body& caster, - const DirectionalLight& light, - double now, - vector& shadows) + LightingState& lightingState, + unsigned int lightIndex, + double now) { + const DirectionalLight& light = lightingState.lights[lightIndex]; + vector& shadows = *lightingState.shadows[lightIndex]; + bool isReceiverShadowed = false; + // Ignore situations where the shadow casting body is much smaller than // the receiver, as these shadows aren't likely to be relevant. Also, // ignore eclipses where the caster is not an ellipsoid, since we can't @@ -7411,16 +7413,69 @@ shadow.umbraRadius = caster.getRadius() * (appOccluderRadius - appSunRadius) / appOccluderRadius; shadow.maxDepth = std::min(1.0f, square(appOccluderRadius / appSunRadius)); + shadow.caster = &caster; // Ignore transits that don't produce a visible shadow. if (shadow.maxDepth > 1.0f / 256.0f) shadows.push_back(shadow); - return true; + isReceiverShadowed = true; } + + // If the caster has a ring system, see if it casts a shadow on the receiver. + // Ring shadows are only supported in the OpenGL 2.0 path. + if (caster.getRings() && context->getRenderPath() == GLContext::GLPath_GLSL) + { + bool shadowed = false; + //clog << "Testing for ring shadows on " << receiver.getName() << endl; + + // The shadow volume of the rings is an oblique circular cylinder + if (dist < caster.getRings()->outerRadius + receiver.getRadius()) + { + // Possible intersection, but it depends on the orientation of the + // rings. + Quaterniond casterOrientation = caster.getOrientation(now); + Vector3d ringPlaneNormal = casterOrientation * Vector3d::UnitY(); + Vector3d shadowDirection = lightToCasterDir.normalized(); + Vector3d v = ringPlaneNormal.cross(shadowDirection); + if (v.squaredNorm() < 1.0e-6) + { + // Shadow direction is nearly coincident with ring plane normal, so + // the shadow cross section is close to circular. No additional test + // is required. + shadowed = true; + } + else + { + // minDistance is the cross section of the ring shadows in the plane + // perpendicular to the ring plane and containing the light direction. + Vector3d shadowPlaneNormal = v.normalized().cross(shadowDirection); + Hyperplane shadowPlane(shadowPlaneNormal, posCaster - posReceiver); + double minDistance = receiver.getRadius() + + caster.getRings()->outerRadius * ringPlaneNormal.dot(shadowDirection); + if (abs(shadowPlane.signedDistance(Vector3d::Zero())) < minDistance) + { + // TODO: Implement this test and only set shadowed to true if it passes + } + shadowed = true; + } + + if (shadowed) + { + RingShadow& shadow = lightingState.ringShadows[lightIndex]; + shadow.origin = dir.cast(); + shadow.direction = shadowDirection.cast(); + shadow.ringSystem = caster.getRings(); + shadow.casterOrientation = casterOrientation.cast(); + + //clog << "Ring shadows on " << receiver.getName() << ": " << + // shadow.origin.transpose() << endl; + } + } + } } - return false; + return isReceiverShadowed; } @@ -7511,6 +7566,18 @@ } + // Add ring shadow records for each light + if (body.getRings() && ShowRingShadows) + { + for (unsigned int li = 0; li < lights.nLights; li++) + { + lights.ringShadows[li].ringSystem = body.getRings(); + lights.ringShadows[li].casterOrientation = q.cast(); + lights.ringShadows[li].origin = Vector3f::Zero(); + lights.ringShadows[li].direction = -lights.lights[li].position.normalized().cast(); + } + } + // Calculate eclipse circumstances if ((renderFlags & ShowEclipseShadows) != 0 && body.getSystem() != NULL) @@ -7532,9 +7599,7 @@ { for (int i = 0; i < nSatellites; i++) { - testEclipse(body, *satellites->getBody(i), - lights.lights[li], - now, *lights.shadows[li]); + testEclipse(body, *satellites->getBody(i), lights, li, now); } } } @@ -7554,8 +7619,7 @@ Body* planet = system->getPrimaryBody(); while (planet != NULL) { - testEclipse(body, *planet, lights.lights[li], - now, *lights.shadows[li]); + testEclipse(body, *planet, lights, li, now); if (planet->getSystem() != NULL) planet = planet->getSystem()->getPrimaryBody(); else @@ -7567,9 +7631,7 @@ { if (system->getBody(i) != &body) { - testEclipse(body, *system->getBody(i), - lights.lights[li], - now, *lights.shadows[li]); + testEclipse(body, *system->getBody(i), lights, li, now); } } } @@ -7577,6 +7639,63 @@ } } + // Sort out the ring shadows; only one ring shadow source is supported right now. This means + // that exotic cases with shadows from two ring different ring systems aren't handled. + for (unsigned int li = 0; li < lights.nLights; li++) + { + if (lights.ringShadows[li].ringSystem != NULL) + { + RingSystem* rings = lights.ringShadows[li].ringSystem; + + // Use the first set of ring shadows found (shadowing the brightest light + // source.) + if (lights.shadowingRingSystem == NULL) + { + lights.shadowingRingSystem = rings; + lights.ringPlaneNormal = (rp.orientation * lights.ringShadows[li].casterOrientation.conjugate()) * Vector3f::UnitY(); + lights.ringCenter = rp.orientation * lights.ringShadows[li].origin; + } + + // Calculate the texture level of detail to use for the rings + float ringWidth = rings->outerRadius - rings->innerRadius; + float projectedRingSize = std::abs(lights.lights[li].direction_obj.dot(lights.ringPlaneNormal)) * ringWidth; + float projectedRingSizeInPixels = projectedRingSize / (max(nearPlaneDistance, altitude) * pixelSize); + Texture* ringsTex = rings->texture.find(textureResolution); + if (ringsTex) + { + // Calculate the approximate distance from the shadowed object to the rings + Hyperplane ringPlane(lights.ringPlaneNormal, lights.ringCenter); + float cosLightAngle = lights.lights[li].direction_obj.dot(ringPlane.normal()); + float approxRingDistance = rings->innerRadius; + if (abs(cosLightAngle) < 0.99999f) + { + approxRingDistance = abs(ringPlane.offset() / cosLightAngle); + } + if (lights.ringCenter.norm() < rings->innerRadius) + { + approxRingDistance = max(approxRingDistance, rings->innerRadius - lights.ringCenter.norm()); + } + + // Light sources have a finite size, which causes some blurring of the texture. Simulate + // this effect by using a lower LOD. Calculate the LOD based on the size of the smallest + // ring feature relative to the apparent size of the light source. + float ringTextureWidth = ringsTex->getWidth(); + float ringFeatureSize = (projectedRingSize / ringTextureWidth) / approxRingDistance; + float relativeFeatureSize = lights.lights[li].apparentSize / ringFeatureSize; + float areaLightLod = log(max(relativeFeatureSize, 1.0f)) / log(2.0f); + + float texelToPixelRatio = ringTextureWidth / projectedRingSizeInPixels; + float maxLod = log((float) ringsTex->getWidth()); + float lod = max(areaLightLod, log(texelToPixelRatio) / log(2.0f)); + lights.ringShadows[li].texLod = min(maxLod, lod); + } + else + { + lights.ringShadows[li].texLod = 0.0f; + } + } + } + renderObject(pos, distance, now, cameraOrientation, nearPlaneDistance, farPlaneDistance, rp, lights); Index: renderglsl.h =================================================================== --- renderglsl.h (revision 4868) +++ renderglsl.h (working copy) @@ -19,7 +19,6 @@ void renderSphere_GLSL(const RenderInfo& ri, const LightingState& ls, - RingSystem* rings, Atmosphere* atmosphere, float cloudTexOffset, float radius, @@ -45,7 +44,6 @@ Texture* cloudTex, Texture* cloudNormalMap, float texOffset, - RingSystem* rings, float radius, unsigned int textureRes, int renderFlags, Index: shadermanager.cpp =================================================================== --- shadermanager.cpp (revision 4868) +++ shadermanager.cpp (working copy) @@ -28,6 +28,19 @@ ShaderManager g_ShaderManager; +enum ShaderVariableType +{ + Shader_Float, + Shader_Vector2, + Shader_Vector3, + Shader_Vector4, + Shader_Sampler1D, + Shader_Sampler2D, + Shader_Sampler3D, + Shader_SamplerCube, + Shader_Sampler1DShadow, + Shader_Sampler2DShadow, +}; static const char* errorVertexShaderSource = "void main(void) {\n" @@ -61,9 +74,7 @@ bool ShaderProperties::usesShadows() const { - return (texUsage & RingShadowTexture) != 0 || - (texUsage & CloudShadowTexture) != 0 || - shadowCounts != 0; + return shadowCounts != 0; } @@ -78,34 +89,125 @@ unsigned int -ShaderProperties::getShadowCountForLight(unsigned int i) const +ShaderProperties::getEclipseShadowCountForLight(unsigned int lightIndex) const { - return (shadowCounts >> i * 2) & 3; + return (shadowCounts >> lightIndex * ShadowBitsPerLight) & EclipseShadowMask; } +bool +ShaderProperties::hasEclipseShadows() const +{ + return (shadowCounts & AnyEclipseShadowMask) != 0; +} + + void -ShaderProperties::setShadowCountForLight(unsigned int light, unsigned int n) +ShaderProperties::setEclipseShadowCountForLight(unsigned int lightIndex, unsigned int shadowCount) { - assert(n <= MaxShaderShadows); - assert(light < MaxShaderLights); - if (n <= MaxShaderShadows && light < MaxShaderLights) + assert(shadowCount <= MaxShaderEclipseShadows); + assert(lightIndex < MaxShaderLights); + if (shadowCount <= MaxShaderEclipseShadows && lightIndex < MaxShaderLights) { - shadowCounts &= ~(3 << light * 2); - shadowCounts |= n << light * 2; + shadowCounts &= ~(EclipseShadowMask << lightIndex * ShadowBitsPerLight); + shadowCounts |= shadowCount << lightIndex * ShadowBitsPerLight; } } bool +ShaderProperties::hasRingShadowForLight(unsigned int lightIndex) const +{ + return ((shadowCounts >> lightIndex * ShadowBitsPerLight) & RingShadowMask) != 0; +} + + +bool +ShaderProperties::hasRingShadows() const +{ + return (shadowCounts & AnyRingShadowMask) != 0; +} + + +void +ShaderProperties::setRingShadowForLight(unsigned int lightIndex, bool enabled) +{ + assert(lightIndex < MaxShaderLights); + if (lightIndex < MaxShaderLights) + { + shadowCounts &= ~(RingShadowMask << lightIndex * ShadowBitsPerLight); + shadowCounts |= (enabled ? RingShadowMask : 0) << lightIndex * ShadowBitsPerLight; + } +} + + +bool +ShaderProperties::hasSelfShadowForLight(unsigned int lightIndex) const +{ + return ((shadowCounts >> lightIndex * ShadowBitsPerLight) & SelfShadowMask) != 0; +} + + +bool +ShaderProperties::hasSelfShadows() const +{ + return (shadowCounts & AnySelfShadowMask) != 0; +} + + +void +ShaderProperties::setSelfShadowForLight(unsigned int lightIndex, bool enabled) +{ + assert(lightIndex < MaxShaderLights); + if (lightIndex < MaxShaderLights) + { + shadowCounts &= ~(SelfShadowMask << lightIndex * ShadowBitsPerLight); + shadowCounts |= (enabled ? SelfShadowMask : 0) << lightIndex * ShadowBitsPerLight; + } +} + + +bool +ShaderProperties::hasCloudShadowForLight(unsigned int lightIndex) const +{ + return ((shadowCounts >> lightIndex * ShadowBitsPerLight) & CloudShadowMask) != 0; +} + + +bool +ShaderProperties::hasCloudShadows() const +{ + return (shadowCounts & AnyCloudShadowMask) != 0; +} + + +void +ShaderProperties::setCloudShadowForLight(unsigned int lightIndex, bool enabled) +{ + assert(lightIndex < MaxShaderLights); + if (lightIndex < MaxShaderLights) + { + shadowCounts &= ~(SelfShadowMask << lightIndex * ShadowBitsPerLight); + shadowCounts |= (enabled ? CloudShadowMask : 0) << lightIndex * ShadowBitsPerLight; + } +} + + +bool ShaderProperties::hasShadowsForLight(unsigned int light) const { assert(light < MaxShaderLights); - return getShadowCountForLight(light) != 0 || - (texUsage & (RingShadowTexture | CloudShadowTexture)) != 0; + return getEclipseShadowCountForLight(light) != 0 || + hasRingShadowForLight(light) || + hasSelfShadowForLight(light) || + hasCloudShadowForLight(light); } + + + + // Returns true if diffuse, specular, bump, and night textures all use the // same texture coordinate set. bool @@ -186,7 +288,7 @@ ShaderManager::ShaderManager() { -#if defined(_DEBUG) || defined(DEBUG) +#if defined(_DEBUG) || defined(DEBUG) || 1 // Only write to shader log file if this is a debug build if (g_shaderLogFile == NULL) @@ -257,7 +359,45 @@ } #endif +static const string +ShaderTypeString(ShaderVariableType type) +{ + switch (type) + { + case Shader_Float: + return "float"; + case Shader_Vector2: + return "vec2"; + case Shader_Vector3: + return "vec3"; + case Shader_Vector4: + return "vec4"; + case Shader_Sampler1D: + return "sampler1D"; + case Shader_Sampler2D: + return "sampler2D"; + case Shader_Sampler3D: + return "sampler3D"; + case Shader_SamplerCube: + return "samplerCube"; + case Shader_Sampler1DShadow: + return "sampler1DShadow"; + case Shader_Sampler2DShadow: + return "sampler2DShadow"; + default: + return "unknown"; + } +}; + static string +IndexedParameter(const char* name, unsigned int index0) +{ + char buf[64]; + sprintf(buf, "%s%d", name, index0); + return string(buf); +} + +static string IndexedParameter(const char* name, unsigned int index0, unsigned int index1) { char buf[64]; @@ -265,8 +405,401 @@ return string(buf); } +static string +DeclareUniform(const std::string& name, ShaderVariableType type) +{ + return string("uniform ") + ShaderTypeString(type) + " " + name + ";\n"; +} static string +DeclareVarying(const std::string& name, ShaderVariableType type) +{ + return string("varying ") + ShaderTypeString(type) + " " + name + ";\n"; +} + +static string +DeclareAttribute(const std::string& name, ShaderVariableType type) +{ + return string("attribute ") + ShaderTypeString(type) + " " + name + ";\n"; +} + + +class Sh_ExpressionContents +{ +protected: + Sh_ExpressionContents() : m_refCount(0) {} + virtual ~Sh_ExpressionContents() {}; + +public: + virtual string toString() const = 0; + virtual int precedence() const = 0; + string toStringPrecedence(int parentPrecedence) const + { + if (parentPrecedence >= precedence()) + return string("(") + toString() + ")"; + else + return toString(); + } + + int addRef() const + { + return ++m_refCount; + } + + int release() const + { + int n = --m_refCount; + if (m_refCount == 0) + delete this; + return n; + } + +private: + mutable int m_refCount; +}; + + +class Sh_ConstantExpression : public Sh_ExpressionContents +{ +public: + Sh_ConstantExpression(float value) : m_value(value) {} + virtual string toString() const + { + char buf[32]; + sprintf(buf, "%f", m_value); + return string(buf); + } + + virtual int precedence() const { return 100; } + +private: + float m_value; +}; + + +class Sh_Expression +{ +public: + Sh_Expression(const Sh_ExpressionContents* expr) : + m_contents(expr) + { + m_contents->addRef(); + } + + Sh_Expression(const Sh_Expression& other) : + m_contents(other.m_contents) + { + m_contents->addRef(); + } + + Sh_Expression& operator=(const Sh_Expression& other) + { + if (other.m_contents != m_contents) + { + m_contents->release(); + m_contents = other.m_contents; + m_contents->addRef(); + } + + return *this; + } + + Sh_Expression(float f) : + m_contents(new Sh_ConstantExpression(f)) + { + m_contents->addRef(); + } + + ~Sh_Expression() + { + m_contents->release(); + } + + string toString() const + { + return m_contents->toString(); + } + + string toStringPrecedence(int parentPrecedence) const + { + return m_contents->toStringPrecedence(parentPrecedence); + } + + int precedence() const + { + return m_contents->precedence(); + } + + // [] operator acts like swizzle + Sh_Expression operator[](const string& components) const; + +private: + const Sh_ExpressionContents* m_contents; +}; + + +class Sh_VariableExpression : public Sh_ExpressionContents +{ +public: + Sh_VariableExpression(const string& name) : m_name(name) {} + virtual string toString() const + { + return m_name; + } + + virtual int precedence() const { return 100; } + +private: + const string m_name; +}; + +class Sh_SwizzleExpression : public Sh_ExpressionContents +{ +public: + Sh_SwizzleExpression(const Sh_Expression& expr, const string& components) : + m_expr(expr), + m_components(components) + { + } + + virtual string toString() const + { + return m_expr.toStringPrecedence(precedence()) + "." + m_components; + } + + int precedence() const { return 99; } + +private: + const Sh_Expression m_expr; + const string m_components; +}; + +class Sh_BinaryExpression : public Sh_ExpressionContents +{ +public: + Sh_BinaryExpression(const string& op, int precedence, const Sh_Expression& left, const Sh_Expression& right) : + m_op(op), + m_precedence(precedence), + m_left(left), + m_right(right) {}; + + virtual string toString() const + { + return left().toStringPrecedence(precedence()) + op() + right().toStringPrecedence(precedence()); + } + + const Sh_Expression& left() const { return m_left; } + const Sh_Expression& right() const { return m_right; } + string op() const { return m_op; } + int precedence() const { return m_precedence; } + +private: + string m_op; + int m_precedence; + const Sh_Expression m_left; + const Sh_Expression m_right; +}; + +class Sh_AdditionExpression : public Sh_BinaryExpression +{ +public: + Sh_AdditionExpression(const Sh_Expression& left, const Sh_Expression& right) : + Sh_BinaryExpression("+", 1, left, right) {} +}; + +class Sh_SubtractionExpression : public Sh_BinaryExpression +{ +public: + Sh_SubtractionExpression(const Sh_Expression& left, const Sh_Expression& right) : + Sh_BinaryExpression("-", 1, left, right) {} +}; + +class Sh_MultiplicationExpression : public Sh_BinaryExpression +{ +public: + Sh_MultiplicationExpression(const Sh_Expression& left, const Sh_Expression& right) : + Sh_BinaryExpression("*", 2, left, right) {} +}; + +class Sh_DivisionExpression : public Sh_BinaryExpression +{ +public: + Sh_DivisionExpression(const Sh_Expression& left, const Sh_Expression& right) : + Sh_BinaryExpression("/", 2, left, right) {} +}; + +Sh_Expression operator+(const Sh_Expression& left, const Sh_Expression& right) +{ + return new Sh_AdditionExpression(left, right); +} + +Sh_Expression operator-(const Sh_Expression& left, const Sh_Expression& right) +{ + return new Sh_SubtractionExpression(left, right); +} + +Sh_Expression operator*(const Sh_Expression& left, const Sh_Expression& right) +{ + return new Sh_MultiplicationExpression(left, right); +} + +Sh_Expression operator/(const Sh_Expression& left, const Sh_Expression& right) +{ + return new Sh_DivisionExpression(left, right); +} + +Sh_Expression Sh_Expression::operator[](const string& components) const +{ + return new Sh_SwizzleExpression(*this, components); +} + +class Sh_UnaryFunctionExpression : public Sh_ExpressionContents +{ +public: + Sh_UnaryFunctionExpression(const string& name, const Sh_Expression& arg0) : + m_name(name), m_arg0(arg0) {} + + virtual string toString() const + { + return m_name + "(" + m_arg0.toString() + ")"; + } + + virtual int precedence() const { return 100; } + +private: + string m_name; + const Sh_Expression m_arg0; +}; + +class Sh_BinaryFunctionExpression : public Sh_ExpressionContents +{ +public: + Sh_BinaryFunctionExpression(const string& name, const Sh_Expression& arg0, const Sh_Expression& arg1) : + m_name(name), m_arg0(arg0), m_arg1(arg1) {} + + virtual string toString() const + { + return m_name + "(" + m_arg0.toString() + ", " + m_arg1.toString() + ")"; + } + + virtual int precedence() const { return 100; } + +private: + string m_name; + const Sh_Expression m_arg0; + const Sh_Expression m_arg1; +}; + +class Sh_TernaryFunctionExpression : public Sh_ExpressionContents +{ +public: + Sh_TernaryFunctionExpression(const string& name, const Sh_Expression& arg0, const Sh_Expression& arg1, const Sh_Expression& arg2) : + m_name(name), m_arg0(arg0), m_arg1(arg1), m_arg2(arg2) {} + + virtual string toString() const + { + return m_name + "(" + m_arg0.toString() + ", " + m_arg1.toString() + ", " + m_arg2.toString() + ")"; + } + + virtual int precedence() const { return 100; } + +private: + string m_name; + const Sh_Expression m_arg0; + const Sh_Expression m_arg1; + const Sh_Expression m_arg2; +}; + + +Sh_Expression vec2(const Sh_Expression& x, const Sh_Expression& y) +{ + return new Sh_BinaryFunctionExpression("vec2", x, y); +}; + +Sh_Expression vec3(const Sh_Expression& x, const Sh_Expression& y, const Sh_Expression& z) +{ + return new Sh_TernaryFunctionExpression("vec3", x, y, z); +}; + +Sh_Expression dot(const Sh_Expression& v0, const Sh_Expression& v1) +{ + return new Sh_BinaryFunctionExpression("dot", v0, v1); +}; + +Sh_Expression cross(const Sh_Expression& v0, const Sh_Expression& v1) +{ + return new Sh_BinaryFunctionExpression("cross", v0, v1); +}; + +Sh_Expression sqrt(const Sh_Expression& v0) +{ + return new Sh_UnaryFunctionExpression("sqrt", v0); +}; + +Sh_Expression length(const Sh_Expression& v0) +{ + return new Sh_UnaryFunctionExpression("length", v0); +}; + +Sh_Expression normalize(const Sh_Expression& v0) +{ + return new Sh_UnaryFunctionExpression("normalize", v0); +}; + +Sh_Expression texture2D(const Sh_Expression& sampler, const Sh_Expression& texCoord) +{ + return new Sh_BinaryFunctionExpression("texture2D", sampler, texCoord); +}; + +Sh_Expression texture2DLod(const Sh_Expression& sampler, const Sh_Expression& texCoord, const Sh_Expression& lod) +{ + return new Sh_TernaryFunctionExpression("texture2DLod", sampler, texCoord, lod); +}; + +Sh_Expression sampler2D(const string& name) +{ + return new Sh_VariableExpression(name); +} + +Sh_Expression indexedUniform(const string& name, unsigned int index0) +{ + char buf[64]; + sprintf(buf, "%s%u", name.c_str(), index0); + return new Sh_VariableExpression(string(buf)); +} + +Sh_Expression ringShadowTexCoord(unsigned int index) +{ + char buf[64]; + sprintf(buf, "ringShadowTexCoord.%c", "xyzw"[index]); + return new Sh_VariableExpression(string(buf)); +} + +Sh_Expression cloudShadowTexCoord(unsigned int index) +{ + char buf[64]; + sprintf(buf, "cloudShadowTexCoord%d", index); + return new Sh_VariableExpression(string(buf)); +} + +string assign(const string& variableName, const Sh_Expression& expr) +{ + return variableName + " = " + expr.toString() + ";\n"; +} + +string addAssign(const string& variableName, const Sh_Expression& expr) +{ + return variableName + " += " + expr.toString() + ";\n"; +} + +string mulAssign(const string& variableName, const Sh_Expression& expr) +{ + return variableName + " *= " + expr.toString() + ";\n"; +} + + + +static string RingShadowTexCoord(unsigned int index) { char buf[64]; @@ -591,16 +1124,28 @@ source += "shadow = " + SeparateDiffuse(light) + ";\n"; } - if (props.texUsage & ShaderProperties::RingShadowTexture) + if (props.hasRingShadowForLight(light)) { - source += "shadow *= (1.0 - texture2D(ringTex, vec2(" + - RingShadowTexCoord(light) + ", 0.0)).a);\n"; +#if 0 + source += "shadow *= (1.0 - texture2DLod(ringTex, vec2(" + + RingShadowTexCoord(light) + ", 0.0), " + IndexedParameter("ringShadowLOD", light) + ").a);\n"; +#endif +#if 0 + source += "shadow *= (1.0 - " + + texture2DLod(sampler2D("ringTex"), vec2(ringShadowTexCoord(light), 0.0f), indexedUniform("ringShadowLOD", light))["a"].toString() + + ");\n"; +#endif + source += mulAssign("shadow", + (1.0f - texture2DLod(sampler2D("ringTex"), vec2(ringShadowTexCoord(light), 0.0f), indexedUniform("ringShadowLOD", light))["a"])); } - if (props.texUsage & ShaderProperties::CloudShadowTexture) + if (props.hasCloudShadowForLight(light)) { + source += mulAssign("shadow", 1.0f - texture2D(sampler2D("cloudShadowTex"), cloudShadowTexCoord(light))["a"] * 0.75f); +#if 0 source += "shadow *= (1.0 - texture2D(cloudShadowTex, " + CloudShadowTexCoord(light) + ").a * 0.75);\n"; +#endif } return source; @@ -655,7 +1200,7 @@ { string source = BeginLightSourceShadows(props, light); - for (unsigned int i = 0; i < props.getShadowCountForLight(light); i++) + for (unsigned int i = 0; i < props.getEclipseShadowCountForLight(light); i++) source += Shadow(light, i); return source; @@ -939,7 +1484,12 @@ string PointSizeCalculation() { - return string("gl_PointSize = pointScale * pointSize / length(vec3(gl_ModelViewMatrix * gl_Vertex));\n"); + string source; + source += "float ptSize = pointScale * pointSize / length(vec3(gl_ModelViewMatrix * gl_Vertex));\n"; + source += "pointFade = min(1.0, ptSize * ptSize);\n"; + source += "gl_PointSize = ptSize;\n"; + + return source; } @@ -971,8 +1521,9 @@ if (props.texUsage & ShaderProperties::PointSprite) { - source += "uniform float pointScale;\n"; - source += "attribute float pointSize;\n"; + source += DeclareUniform("pointScale", Shader_Float); + source += DeclareAttribute("pointSize", Shader_Float); + source += DeclareVarying("pointFade", Shader_Float); } if (props.usesTangentSpaceLighting()) @@ -1041,24 +1592,31 @@ } // Shadow parameters - if (props.shadowCounts != 0) + if (props.hasEclipseShadows()) { - source += "varying vec3 position_obj;\n"; + source += DeclareVarying("position_obj", Shader_Vector3); } - if (props.texUsage & ShaderProperties::RingShadowTexture) + if (props.hasRingShadows()) { - source += "uniform float ringWidth;\n"; - source += "uniform float ringRadius;\n"; - source += "varying vec4 ringShadowTexCoord;\n"; + source += DeclareUniform("ringWidth", Shader_Float); + source += DeclareUniform("ringRadius", Shader_Float); + source += DeclareUniform("ringPlane", Shader_Vector4); + source += DeclareUniform("ringCenter", Shader_Vector3); + source += DeclareVarying("ringShadowTexCoord", Shader_Vector4); } - if (props.texUsage & ShaderProperties::CloudShadowTexture) + if (props.hasCloudShadows()) { source += "uniform float cloudShadowTexOffset;\n"; source += "uniform float cloudHeight;\n"; for (unsigned int i = 0; i < props.nLights; i++) - source += "varying vec2 " + CloudShadowTexCoord(i) + ";\n"; + { + if (props.hasCloudShadowForLight(i)) + { + source += "varying vec2 " + CloudShadowTexCoord(i) + ";\n"; + } + } } // Begin main() function @@ -1163,58 +1721,65 @@ } // Shadow texture coordinates are generated in the shader - if (props.texUsage & ShaderProperties::RingShadowTexture) + if (props.hasRingShadows()) { source += "vec3 ringShadowProj;\n"; for (unsigned int j = 0; j < props.nLights; j++) { - source += "ringShadowProj = gl_Vertex.xyz + " + - LightProperty(j, "direction") + - " * max(0.0, gl_Vertex.y / -" + - LightProperty(j, "direction") + ".y);\n"; - source += RingShadowTexCoord(j) + - " = (length(ringShadowProj) - ringRadius) * ringWidth;\n"; + if (props.hasRingShadowForLight(j)) + { + source += "ringShadowProj = gl_Vertex.xyz + " + + LightProperty(j, "direction") + + " * max(0.0, -(dot(gl_Vertex.xyz, ringPlane.xyz) + ringPlane.w) / dot(" + + LightProperty(j, "direction") + ", ringPlane.xyz));\n"; + + source += RingShadowTexCoord(j) + + " = (length(ringShadowProj - ringCenter) - ringRadius) * ringWidth;\n"; + } } } - if (props.texUsage & ShaderProperties::CloudShadowTexture) + if (props.hasCloudShadows()) { for (unsigned int j = 0; j < props.nLights; j++) { - source += "{\n"; + if (props.hasCloudShadowForLight(j)) + { + source += "{\n"; - // A cheap way to calculate cloud shadow texture coordinates that doesn't correctly account - // for sun angle. - source += " " + CloudShadowTexCoord(j) + " = vec2(diffTexCoord.x + cloudShadowTexOffset, diffTexCoord.y);\n"; + // A cheap way to calculate cloud shadow texture coordinates that doesn't correctly account + // for sun angle. + source += " " + CloudShadowTexCoord(j) + " = vec2(diffTexCoord.x + cloudShadowTexOffset, diffTexCoord.y);\n"; - // Disabled: there are too many problems with this approach, - // though it should theoretically work. The inverse trig - // approximations produced by the shader compiler are crude - // enough that visual anomalies are apparent. And in the current - // GeForce 8800 driver, this shader produces an internal compiler - // error. Cloud shadows are trivial if the cloud texture is a cube - // map. Also, DX10 capable hardware could efficiently perform - // the rect-to-spherical conversion in the pixel shader with an - // fp32 texture serving as a lookup table. + // Disabled: there are too many problems with this approach, + // though it should theoretically work. The inverse trig + // approximations produced by the shader compiler are crude + // enough that visual anomalies are apparent. And in the current + // GeForce 8800 driver, this shader produces an internal compiler + // error. Cloud shadows are trivial if the cloud texture is a cube + // map. Also, DX10 capable hardware could efficiently perform + // the rect-to-spherical conversion in the pixel shader with an + // fp32 texture serving as a lookup table. #if 0 - // Compute the intersection of the sun direction and the cloud layer (currently assumed to be a sphere) - source += " float rq = dot(" + LightProperty(j, "direction") + ", gl_Vertex.xyz);\n"; - source += " float qq = dot(gl_Vertex.xyz, gl_Vertex.xyz) - cloudHeight * cloudHeight;\n"; - source += " float d = sqrt(rq * rq - qq);\n"; - source += " vec3 cloudSpherePos = (gl_Vertex.xyz + (-rq + d) * " + LightProperty(j, "direction") + ");\n"; - //source += " vec3 cloudSpherePos = gl_Vertex.xyz;\n"; + // Compute the intersection of the sun direction and the cloud layer (currently assumed to be a sphere) + source += " float rq = dot(" + LightProperty(j, "direction") + ", gl_Vertex.xyz);\n"; + source += " float qq = dot(gl_Vertex.xyz, gl_Vertex.xyz) - cloudHeight * cloudHeight;\n"; + source += " float d = sqrt(rq * rq - qq);\n"; + source += " vec3 cloudSpherePos = (gl_Vertex.xyz + (-rq + d) * " + LightProperty(j, "direction") + ");\n"; + //source += " vec3 cloudSpherePos = gl_Vertex.xyz;\n"; - // Find the texture coordinates at this point on the sphere by converting from rectangular to spherical; this is an - // expensive calculation to perform per vertex. - source += " float invPi = 1.0 / 3.1415927;\n"; - source += " " + CloudShadowTexCoord(j) + ".y = 0.5 - asin(cloudSpherePos.y) * invPi;\n"; - source += " float u = fract(atan(cloudSpherePos.x, cloudSpherePos.z) * (invPi * 0.5) + 0.75);\n"; - source += " if (diffTexCoord.x < 0.25 && u > 0.5) u -= 1.0;\n"; - source += " else if (diffTexCoord.x > 0.75 && u < 0.5) u += 1.0;\n"; - source += " " + CloudShadowTexCoord(j) + ".x = u + cloudShadowTexOffset;\n"; + // Find the texture coordinates at this point on the sphere by converting from rectangular to spherical; this is an + // expensive calculation to perform per vertex. + source += " float invPi = 1.0 / 3.1415927;\n"; + source += " " + CloudShadowTexCoord(j) + ".y = 0.5 - asin(cloudSpherePos.y) * invPi;\n"; + source += " float u = fract(atan(cloudSpherePos.x, cloudSpherePos.z) * (invPi * 0.5) + 0.75);\n"; + source += " if (diffTexCoord.x < 0.25 && u > 0.5) u -= 1.0;\n"; + source += " else if (diffTexCoord.x > 0.75 && u < 0.5) u += 1.0;\n"; + source += " " + CloudShadowTexCoord(j) + ".x = u + cloudShadowTexOffset;\n"; #endif - source += "}\n"; + source += "}\n"; + } } } @@ -1229,7 +1794,7 @@ nTexCoords++; } - if (props.shadowCounts != 0) + if (props.hasEclipseShadows() != 0) { source += "position_obj = gl_Vertex.xyz;\n"; } @@ -1384,7 +1949,7 @@ source += "varying vec3 position_obj;\n"; for (unsigned int i = 0; i < props.nLights; i++) { - for (unsigned int j = 0; j < props.getShadowCountForLight(i); j++) + for (unsigned int j = 0; j < props.getEclipseShadowCountForLight(i); j++) { source += "uniform vec4 " + IndexedParameter("shadowTexGenS", i, j) + ";\n"; @@ -1398,19 +1963,31 @@ } } - if (props.texUsage & ShaderProperties::RingShadowTexture) + if (props.hasRingShadows()) { - source += "uniform sampler2D ringTex;\n"; - source += "varying vec4 ringShadowTexCoord;\n"; + source += DeclareUniform("ringTex", Shader_Sampler2D); + source += DeclareVarying("ringShadowTexCoord", Shader_Vector4); + for (unsigned int i = 0; i < props.nLights; i++) + { + if (props.hasRingShadowForLight(i)) + { + source += DeclareUniform(IndexedParameter("ringShadowLOD", i), Shader_Float); + } + } } - if (props.texUsage & ShaderProperties::CloudShadowTexture) + if (props.hasCloudShadows()) { source += "uniform sampler2D cloudShadowTex;\n"; for (unsigned int i = 0; i < props.nLights; i++) source += "varying vec2 " + CloudShadowTexCoord(i) + ";\n"; } + if (props.texUsage & ShaderProperties::PointSprite) + { + source += DeclareVarying("pointFade", Shader_Float); + } + source += "\nvoid main(void)\n{\n"; source += "vec4 color;\n"; @@ -1418,7 +1995,7 @@ { // Temporaries required for shadows source += "float shadow;\n"; - if (props.shadowCounts != 0) + if (props.hasEclipseShadows()) { source += "vec2 shadowCenter;\n"; source += "float shadowR;\n"; @@ -1577,6 +2154,11 @@ source += "color = vec4(1.0, 1.0, 1.0, 1.0);\n"; } + if (props.texUsage & ShaderProperties::PointSprite) + { + source += "color.a *= pointFade;\n"; + } + // Mix in the overlay color with the base color if (props.texUsage & ShaderProperties::OverlayTexture) { @@ -1696,7 +2278,7 @@ if (props.texUsage & ShaderProperties::DiffuseTexture) source += "diffTexCoord = " + TexCoord2D(0) + ";\n"; - if (props.shadowCounts != 0) + if (props.hasEclipseShadows() != 0) { source += "position_obj = gl_Vertex.xyz;\n"; for (unsigned int i = 0; i < props.nLights; i++) @@ -1744,14 +2326,14 @@ } - if (props.shadowCounts != 0) + if (props.hasEclipseShadows()) { source += "varying vec3 position_obj;\n"; source += "varying vec4 shadowDepths;\n"; for (unsigned int i = 0; i < props.nLights; i++) { - for (unsigned int j = 0; j < props.getShadowCountForLight(i); j++) + for (unsigned int j = 0; j < props.getEclipseShadowCountForLight(i); j++) { source += "uniform vec4 " + IndexedParameter("shadowTexGenS", i, j) + ";\n"; @@ -1768,7 +2350,7 @@ source += "\nvoid main(void)\n{\n"; source += "vec4 color;\n"; - if (props.usesShadows()) + if (props.hasEclipseShadows()) { // Temporaries required for shadows source += "float shadow;\n"; @@ -1779,7 +2361,7 @@ // Sum the contributions from each light source for (unsigned i = 0; i < props.nLights; i++) { - if (props.usesShadows()) + if (props.getEclipseShadowCountForLight(i) > 0) { source += "shadow = 1.0;\n"; source += Shadow(i, 0); @@ -1947,6 +2529,7 @@ { source += "uniform float pointScale;\n"; source += "attribute float pointSize;\n"; + source += "varying float pointFade;\n"; } // Begin main() function @@ -2004,17 +2587,30 @@ source += "uniform sampler2D diffTex;\n"; } + if (props.texUsage & ShaderProperties::PointSprite) + { + source += "varying float pointFade;\n"; + } + // Begin main() source += "\nvoid main(void)\n"; source += "{\n"; + string colorSource = "gl_Color"; + if (props.texUsage & ShaderProperties::PointSprite) + { + source += " vec4 color = gl_Color;\n"; + source += " color.a *= pointFade;\n"; + colorSource = "color"; + } + if (props.texUsage & ShaderProperties::DiffuseTexture) { - source += " gl_FragColor = gl_Color * texture2D(diffTex, gl_TexCoord[0].st);\n"; + source += " gl_FragColor = " + colorSource + " * texture2D(diffTex, gl_TexCoord[0].st);\n"; } else { - source += " gl_FragColor = gl_Color;\n"; + source += " gl_FragColor = " + colorSource + " ;\n"; } source += "}\n"; @@ -2151,7 +2747,7 @@ source << "varying vec3 position_obj;\n"; for (unsigned int i = 0; i < props.nLights; i++) { - for (unsigned int j = 0; j < props.getShadowCountForLight(i); j++) + for (unsigned int j = 0; j < props.getEclipseShadowCountForLight(i); j++) { source << "uniform vec4 " << IndexedParameter("shadowTexGenS", i, j) << ";\n"; source << "uniform vec4 " << IndexedParameter("shadowTexGenT", i, j) << ";\n"; @@ -2335,8 +2931,9 @@ fragLightSpecColor[i] = vec3Param(FragLightProperty(i, "specColor")); if (props.texUsage & ShaderProperties::NightTexture) fragLightBrightness[i] = floatParam(FragLightProperty(i, "brightness")); - - for (unsigned int j = 0; j < props.getShadowCountForLight(i); j++) + if (props.hasRingShadowForLight(i)) + ringShadowLOD[i] = floatParam(IndexedParameter("ringShadowLOD", i)); + for (unsigned int j = 0; j < props.getEclipseShadowCountForLight(i); j++) { shadows[i][j].texGenS = vec4Param(IndexedParameter("shadowTexGenS", i, j)); @@ -2369,6 +2966,8 @@ { ringWidth = floatParam("ringWidth"); ringRadius = floatParam("ringRadius"); + ringPlane = vec4Param("ringPlane"); + ringCenter = vec3Param("ringCenter"); } textureOffset = floatParam("textureOffset"); @@ -2547,7 +3146,7 @@ { if (shadows != NULL) { - unsigned int nShadows = min((size_t) MaxShaderShadows, ls.shadows[li]->size()); + unsigned int nShadows = min((size_t) MaxShaderEclipseShadows, ls.shadows[li]->size()); for (unsigned int i = 0; i < nShadows; i++) {