diff options
Diffstat (limited to 'dep/g3dlite/source/GLight.cpp')
-rw-r--r-- | dep/g3dlite/source/GLight.cpp | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/dep/g3dlite/source/GLight.cpp b/dep/g3dlite/source/GLight.cpp new file mode 100644 index 00000000000..553b493c1a3 --- /dev/null +++ b/dep/g3dlite/source/GLight.cpp @@ -0,0 +1,275 @@ +/** + @file GLight.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2003-11-12 + @edited 2009-11-16 +*/ +#include "G3D/GLight.h" +#include "G3D/Sphere.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Any.h" +#include "G3D/stringutils.h" + +namespace G3D { + +GLight::GLight(const Any& any) { + any.verifyName("GLight"); + + if (any.type() == Any::TABLE) { + *this = GLight(); + Vector3 spotTarget; + bool hasSpotTarget = false; + for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) { + const std::string& key = toLower(it->key); + if (key == "position") { + position = it->value; + } else if (key == "rightdirection") { + rightDirection = it->value; + } else if (key == "spotdirection") { + spotDirection = Vector3(it->value).directionOrZero(); + } else if (key == "spottarget") { + spotTarget = it->value; + hasSpotTarget = true; + } else if (key == "spotcutoff") { + spotCutoff = it->value.number(); + } else if (key == "spotsquare") { + spotSquare = it->value.boolean(); + } else if (key == "attenuation") { + attenuation[0] = it->value[0].number(); + attenuation[1] = it->value[1].number(); + attenuation[2] = it->value[2].number(); + } else if (key == "color") { + color = it->value; + } else if (key == "enabled") { + enabled = it->value.boolean(); + } else if (key == "specular") { + specular = it->value.boolean(); + } else if (key == "diffuse") { + diffuse = it->value.boolean(); + } else { + any.verify(false, "Illegal key: " + it->key); + } + } + if (hasSpotTarget) { + spotDirection = (spotTarget - position.xyz()).direction(); + } + } else if (toLower(any.name()) == "glight::directional") { + + *this = directional(any[0], any[1], + (any.size() > 2) ? any[2] : Any(true), + (any.size() > 3) ? any[3] : Any(true)); + + } else if (toLower(any.name()) == "glight::point") { + + *this = point(any[0], any[1], + (any.size() > 2) ? any[2] : Any(1), + (any.size() > 3) ? any[3] : Any(0), + (any.size() > 4) ? any[4] : Any(0.5f), + (any.size() > 5) ? any[5] : Any(true), + (any.size() > 6) ? any[6] : Any(true)); + + } else if (toLower(any.name()) == "glight::spot") { + + *this = spot(any[0], any[1], any[2], any[3], + (any.size() > 4) ? any[4] : Any(1), + (any.size() > 5) ? any[5] : Any(0), + (any.size() > 6) ? any[6] : Any(0), + (any.size() > 7) ? any[7] : Any(true), + (any.size() > 8) ? any[8] : Any(true)); + } else { + any.verify(false, "Unrecognized name"); + } +} + + +GLight::operator Any() const { + Any a(Any::TABLE, "GLight"); + a.set("position", position.operator Any()); + a.set("rightDirection", rightDirection.operator Any()); + a.set("spotDirection", spotDirection.operator Any()); + a.set("spotCutoff", spotCutoff); + a.set("spotSquare", spotSquare); + + Any att(Any::ARRAY); + att.append(attenuation[0], attenuation[1], attenuation[2]); + a.set("attenuation", att); + a.set("color", color.operator Any()); + a.set("enabled", enabled); + a.set("specular", specular); + a.set("diffuse", diffuse); + return a; +} + + +GLight::GLight() : + position(0, 0, 0, 0), + rightDirection(0,0,0), + spotDirection(0, 0, -1), + spotCutoff(180), + spotSquare(false), + color(Color3::white()), + enabled(false), + specular(true), + diffuse(true) { + + attenuation[0] = 1.0; + attenuation[1] = 0.0; + attenuation[2] = 0.0; +} + + +GLight GLight::directional(const Vector3& toLight, const Color3& color, bool s, bool d) { + GLight L; + L.position = Vector4(toLight.direction(), 0); + L.color = color; + L.specular = s; + L.diffuse = d; + return L; +} + + +GLight GLight::point(const Vector3& pos, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) { + GLight L; + L.position = Vector4(pos, 1); + L.color = color; + L.attenuation[0] = constAtt; + L.attenuation[1] = linAtt; + L.attenuation[2] = quadAtt; + L.specular = s; + L.diffuse = d; + return L; +} + + +GLight GLight::spot(const Vector3& pos, const Vector3& pointDirection, float cutOffAngleDegrees, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) { + GLight L; + L.position = Vector4(pos, 1.0f); + L.spotDirection = pointDirection.direction(); + debugAssert(cutOffAngleDegrees <= 90); + L.spotCutoff = cutOffAngleDegrees; + L.color = color; + L.attenuation[0] = constAtt; + L.attenuation[1] = linAtt; + L.attenuation[2] = quadAtt; + L.specular = s; + L.diffuse = d; + return L; +} + + +bool GLight::operator==(const GLight& other) const { + return (position == other.position) && + (rightDirection == other.rightDirection) && + (spotDirection == other.spotDirection) && + (spotCutoff == other.spotCutoff) && + (spotSquare == other.spotSquare) && + (attenuation[0] == other.attenuation[0]) && + (attenuation[1] == other.attenuation[1]) && + (attenuation[2] == other.attenuation[2]) && + (color == other.color) && + (enabled == other.enabled) && + (specular == other.specular) && + (diffuse == other.diffuse); +} + + +bool GLight::operator!=(const GLight& other) const { + return !(*this == other); +} + + +Sphere GLight::effectSphere(float cutoff) const { + if (position.w == 0) { + // Directional light + return Sphere(Vector3::zero(), finf()); + } else { + // Avoid divide by zero + cutoff = max(cutoff, 0.00001f); + float maxIntensity = max(color.r, max(color.g, color.b)); + + float radius = finf(); + + if (attenuation[2] != 0) { + + // Solve I / attenuation.dot(1, r, r^2) < cutoff for r + // + // a[0] + a[1] r + a[2] r^2 > I/cutoff + // + + float a = attenuation[2]; + float b = attenuation[1]; + float c = attenuation[0] - maxIntensity / cutoff; + + float discrim = square(b) - 4 * a * c; + + if (discrim >= 0) { + discrim = sqrt(discrim); + + float r1 = (-b + discrim) / (2 * a); + float r2 = (-b - discrim) / (2 * a); + + if (r1 < 0) { + if (r2 > 0) { + radius = r2; + } + } else if (r2 > 0) { + radius = min(r1, r2); + } else { + radius = r1; + } + } + + } else if (attenuation[1] != 0) { + + // Solve I / attenuation.dot(1, r) < cutoff for r + // + // r * a[1] + a[0] = I / cutoff + // r = (I / cutoff - a[0]) / a[1] + + float radius = (maxIntensity / cutoff - attenuation[0]) / attenuation[1]; + radius = max(radius, 0.0f); + } + + return Sphere(position.xyz(), radius); + + } +} + + +CoordinateFrame GLight::frame() const { + CoordinateFrame f; + if (rightDirection == Vector3::zero()) { + // No specified right direction; choose one automatically + if (position.w == 0) { + // Directional light + f.lookAt(-position.xyz()); + } else { + // Spot light + f.lookAt(spotDirection); + } + } else { + const Vector3& Z = -spotDirection.direction(); + Vector3 X = rightDirection.direction(); + + // Ensure the vectors are not too close together + while (abs(X.dot(Z)) > 0.9f) { + X = Vector3::random(); + } + + // Ensure perpendicular + X -= Z * Z.dot(X); + const Vector3& Y = Z.cross(X); + + f.rotation.setColumn(Vector3::X_AXIS, X); + f.rotation.setColumn(Vector3::Y_AXIS, Y); + f.rotation.setColumn(Vector3::Z_AXIS, Z); + } + f.translation = position.xyz(); + + return f; +} + + +} // G3D |