diff --git a/src/regcombine.cpp b/src/regcombine.cpp index 838f878b..1f585c1e 100644 --- a/src/regcombine.cpp +++ b/src/regcombine.cpp @@ -17,7 +17,7 @@ void SetupCombinersBumpMap(CTexture& bumpTexture, CTexture& normalizationTexture, - float* ambientColor) + Color ambientColor) { glEnable(GL_REGISTER_COMBINERS_NV); @@ -33,7 +33,11 @@ void SetupCombinersBumpMap(CTexture& bumpTexture, // Just a single combiner stage required . . . glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1); - glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, ambientColor); + float ambient[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + ambient[0] = ambientColor.red(); + ambient[1] = ambientColor.green(); + ambient[2] = ambientColor.blue(); + glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, ambient); // Compute N dot L in the RGB portion of combiner 0 // Load register A with a normal N from the normal map diff --git a/src/regcombine.h b/src/regcombine.h index ccc503b7..9631e3ca 100644 --- a/src/regcombine.h +++ b/src/regcombine.h @@ -14,10 +14,11 @@ #define _REGCOMBINE_H_ #include "texture.h" +#include "color.h" extern void SetupCombinersBumpMap(CTexture& bumpTexture, CTexture& normalizationTexture, - float* ambientColor); + Color ambientColor); extern void DisableCombiners(); #endif // _REGCOMBINE_H_ diff --git a/src/render.cpp b/src/render.cpp index 1497ec53..89f846ea 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -17,6 +17,7 @@ #include "texfont.h" #include "console.h" #include "gui.h" +#include "regcombine.h" #include "render.h" using namespace std; @@ -55,6 +56,7 @@ Renderer::Renderer() : renderMode(GL_FILL), labelMode(NoLabels), ambientLightLevel(0.1f), + perPixelLightingEnabled(false), console(NULL), nSimultaneousTextures(1), useRegisterCombiners(false), @@ -227,6 +229,9 @@ bool Renderer::init(int winWidth, int winHeight) glEnable(GL_COLOR_MATERIAL); glEnable(GL_LIGHTING); + // LEQUAL rather than LESS required for multipass rendering + glDepthFunc(GL_LEQUAL); + // We need this enabled because we use glScale, but only // with uniform scale factors // TODO: Do a proper check for this extension before enabling @@ -323,6 +328,22 @@ void Renderer::setAmbientLightLevel(float level) } +bool Renderer::getPerPixelLighting() const +{ + return perPixelLightingEnabled; +} + +void Renderer::setPerPixelLighting(bool enable) +{ + perPixelLightingEnabled = enable && perPixelLightingSupported(); +} + +bool Renderer::perPixelLightingSupported() const +{ + return useCubeMaps && useRegisterCombiners; +} + + void Renderer::addLabel(string text, Color color, Point3f pos) { double winX, winY, winZ; @@ -714,6 +735,97 @@ void Renderer::renderBodyAsParticle(Point3f position, } +static void renderBumpMappedMesh(Mesh& mesh, + CTexture& bumpTexture, + Vec3f lightDirection, + Quatf orientation, + Color ambientColor) +{ + // We're doing our own per-pixel lighting, so disable GL's lighting + glDisable(GL_LIGHTING); + + // Render the base texture on the first pass . . . The base + // texture and color should have been set up already by the + // caller. + mesh.render(); + + // The 'default' light vector for the bump map is (0, 0, 1). Determine + // a rotation transformation that will move the sun direction to + // this vector. + Quatf lightOrientation; + { + Vec3f zeroLightDirection(0, 0, 1); + Vec3f axis = lightDirection ^ zeroLightDirection; + float cosAngle = zeroLightDirection * lightDirection; + float angle = 0.0f; + float epsilon = 1e-5f; + + if (cosAngle + 1 < epsilon) + { + axis = Vec3f(0, 1, 0); + angle = (float) PI; + } + else if (cosAngle - 1 > -epsilon) + { + axis = Vec3f(0, 1, 0); + angle = 0.0f; + } + else + { + axis.normalize(); + angle = (float) acos(cosAngle); + } + lightOrientation.setAxisAngle(axis, angle); + } + + // Set up the bump map with one directional light source + glEnable(GL_BLEND); + glBlendFunc(GL_DST_COLOR, GL_ZERO); + + SetupCombinersBumpMap(bumpTexture, *normalizationTex, ambientColor); + + // The second set texture coordinates will contain the light + // direction in tangent space. We'll generate the texture coordinates + // from the surface normals using GL_NORMAL_MAP_EXT and then + // use the texture matrix to rotate them into tangent space. + // This method of generating tangent space light direction vectors + // isn't as general as transforming the light direction by an + // orthonormal basis for each mesh vertex, but it works well enough + // for spheres illuminated by directional light sources. + glActiveTextureARB(GL_TEXTURE1_ARB); + + // Set up GL_NORMAL_MAP_EXT texture coordinate generation. This + // mode is part of the cube map extension. + glEnable(GL_TEXTURE_GEN_R); + glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT); + glEnable(GL_TEXTURE_GEN_S); + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT); + glEnable(GL_TEXTURE_GEN_T); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT); + + // Set up the texture transformation--the light direction and the + // viewer orientation both need to be considered. + glMatrixMode(GL_TEXTURE); + glRotate(lightOrientation * orientation); + glMatrixMode(GL_MODELVIEW); + glActiveTextureARB(GL_TEXTURE0_ARB); + + mesh.render(); + + // Reset the second texture unit + glActiveTextureARB(GL_TEXTURE1_ARB); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glDisable(GL_TEXTURE_GEN_R); + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + + DisableCombiners(); + glDisable(GL_BLEND); +} + + void Renderer::renderPlanet(const Body& body, Point3f pos, Vec3f sunDirection, @@ -740,12 +852,22 @@ void Renderer::renderPlanet(const Body& body, const Surface& surface = body.getSurface(); // Get the texture . . . CTexture* tex = NULL; + CTexture* bumpTex = NULL; if (surface.baseTexture != "") { if (!textureManager->find(surface.baseTexture, &tex)) tex = textureManager->load(surface.baseTexture); } + // If this renderer can support bump mapping then get the bump texture + if ((surface.appearanceFlags & Surface::ApplyBumpMap) != 0 && + (perPixelLightingEnabled && useRegisterCombiners && useCubeMaps) && + surface.bumpTexture != "") + { + if (!textureManager->find(surface.bumpTexture, &bumpTex)) + bumpTex = textureManager->loadBumpMap(surface.bumpTexture); + } + if (tex == NULL) { glDisable(GL_TEXTURE_2D); @@ -814,7 +936,13 @@ void Renderer::renderPlanet(const Body& body, } if (mesh != NULL) - mesh->render(); + { + if (bumpTex != NULL) + renderBumpMappedMesh(*mesh, *bumpTex, sunDirection, orientation, + Color(ambientLightLevel, ambientLightLevel, ambientLightLevel)); + else + mesh->render(); + } // If the planet has a ring system, render it. if (body.getRings() != NULL) diff --git a/src/render.h b/src/render.h index e420ef76..624bd73b 100644 --- a/src/render.h +++ b/src/render.h @@ -61,6 +61,9 @@ class Renderer void clearLabelledStars(); float getAmbientLightLevel() const; void setAmbientLightLevel(float); + bool getPerPixelLighting() const; + void setPerPixelLighting(bool); + bool perPixelLightingSupported() const; typedef struct { string text; @@ -148,6 +151,7 @@ class Renderer int renderMode; int labelMode; float ambientLightLevel; + bool perPixelLightingEnabled; vector renderList; vector starParticles; diff --git a/src/solarsys.cpp b/src/solarsys.cpp index 945d3c81..accc1dca 100644 --- a/src/solarsys.cpp +++ b/src/solarsys.cpp @@ -170,12 +170,16 @@ static Body* CreatePlanet(PlanetarySystem* system, Surface surface; surface.color = Color(1.0f, 1.0f, 1.0f); planetData->getColor("Color", surface.color); - planetData->getString("Texture", surface.baseTexture); - planetData->getString("BumpMap", surface.bumpTexture); + bool applyBaseTexture = planetData->getString("Texture", surface.baseTexture); + bool applyBumpMap = planetData->getString("BumpMap", surface.bumpTexture); bool blendTexture = false; planetData->getBoolean("BlendTexture", blendTexture); if (blendTexture) surface.appearanceFlags |= Surface::BlendTexture; + if (applyBaseTexture) + surface.appearanceFlags |= Surface::ApplyBaseTexture; + if (applyBumpMap) + surface.appearanceFlags |= Surface::ApplyBumpMap; body->setSurface(surface); string mesh(""); diff --git a/src/surface.h b/src/surface.h index a8e5d5d1..60e1f0ab 100644 --- a/src/surface.h +++ b/src/surface.h @@ -24,7 +24,7 @@ class Surface // Appearance flags enum { BlendTexture = 0x1, - ApplyDecalTexture = 0x2, + ApplyBaseTexture = 0x2, ApplyBumpMap = 0x4, }; diff --git a/src/texmanager.cpp b/src/texmanager.cpp index 72e20363..dc799a17 100644 --- a/src/texmanager.cpp +++ b/src/texmanager.cpp @@ -34,3 +34,17 @@ CTexture* TextureManager::load(string name) return tex; } + +CTexture* TextureManager::loadBumpMap(string name) +{ + DPRINTF("Loading bump map: %s\n", name.c_str()); + CTexture* tex = LoadTextureFromFile(baseDir + "\\" + name); + if (tex != NULL) + { + tex->normalMap(5.0f, true); + tex->bindName(); + } + addResource(name, static_cast(tex)); + + return tex; +} diff --git a/src/texmanager.h b/src/texmanager.h index 10762784..5f534d7e 100644 --- a/src/texmanager.h +++ b/src/texmanager.h @@ -25,7 +25,8 @@ class TextureManager : public ResourceManager ~TextureManager(); bool find(string name, CTexture**); - CTexture* load(string name); + CTexture* load(std::string name); + CTexture* loadBumpMap(std::string name); }; #endif // _TEXMANAGER_H_