Просмотр исходного кода

Rewrote the zone ambient color gradient system. Now zones only define one ambient color, but look for neighbor zones' ambient colors when gradient mode is enabled.
Fixed missing script bindings to the zone ambient gradient & camera override flags.
Added a default bounding box size for newly created zones.
Added documentation on zones.

Lasse Öörni 14 лет назад
Родитель
Сommit
8f882f8225
5 измененных файлов с 183 добавлено и 46 удалено
  1. 13 2
      Docs/Reference.dox
  2. 5 2
      Docs/ScriptAPI.dox
  3. 7 4
      Engine/Engine/GraphicsAPI.cpp
  4. 132 24
      Engine/Graphics/Zone.cpp
  5. 26 14
      Engine/Graphics/Zone.h

+ 13 - 2
Docs/Reference.dox

@@ -433,7 +433,7 @@ Note that many more optimization opportunities are possible at the content level
 
 \section Rendering_Further Further details
 
-See also \ref Materials "Materials", \ref Lights "Lights and shadows", \ref Particles "Particle systems" and \ref AuxiliaryViews "Auxiliary views".
+See also \ref Materials "Materials", \ref Lights "Lights and shadows", \ref Particles "Particle systems", \ref Zones "Zones", and \ref AuxiliaryViews "Auxiliary views".
 
 See \ref ForwardPrepass "Forward and light pre-pass rendering" for detailed discussion on the two rendering modes.
 
@@ -464,7 +464,7 @@ If the scene contains a large number of complex objects lit by multiple lights,
 
 Forward rendering also enables hardware multisampling and using different shading models in different materials if needed, while neither is possible in light pre-pass rendering.
 
-Finally note that due to OpenGL frame buffer object limitations an extra framebuffer blit has to happen at the end of light pre-pass rendering, which costs some performance. Also, because multiple render targets on OpenGL must have the same format, an R32F texture can not be used for linear depth, but instead 24-bit depth is manually encoded and decoded into RGB channels.
+Finally note that due to OpenGL framebuffer object limitations an extra framebuffer blit has to happen at the end of light pre-pass rendering, which costs some performance. Also, because multiple render targets on OpenGL must have the same format, an R32F texture can not be used for linear depth, but instead 24-bit depth is manually encoded and decoded into RGB channels.
 
 
 \page APIDifferences Differences between Direct3D9 and OpenGL
@@ -658,6 +658,17 @@ Most of the parameters can take either a single value, or minimum and maximum va
 Note: zero active or inactive time period means infinite. Instead of defining a single color element, several colorfade elements can be defined in time order to describe how the particles change color over time.
 
 
+\page Zones Zones
+
+A Zone controls ambient lighting and fogging. Each geometry object determines the zone it is inside (by testing against the zone's oriented bounding box) and uses that zone's ambient light color, fog color and fog start/end distance for rendering. For the case of multiple overlapping zones, zones also have an integer priority value, and objects will choose the highest priority zone they touch.
+
+The viewport will be initially cleared to the fog color of the zone found at the camera's far clip distance. If no zone is found either for the far clip or an object, a default zone with black ambient and fog color will be used.
+
+Zones have two special flags: override mode and ambient gradient. If the camera is inside a zone with override mode enabled, all rendered objects will use that zone's ambient and fog settings, instead of the zone they belong to. This can be used for example to implement an underwater effect. When ambient gradient mode is enabled, the zone's own ambient color value is not used, but instead it will look for two highest-priority neighbor zones that touch it at the minimum and maximum Z face of its oriented bounding box: any objects inside will then get a per-vertex ambient color fade between the neighbor zones' ambient colors.
+
+Zones also define a lightmask and a shadowmask (with all bits set by default.) An object's final lightmask for light culling is determined by ANDing the object lightmask and the zone lightmask. The final shadowmask is also calculated in the same way.
+
+
 \page AuxiliaryViews Auxiliary views
 
 Auxiliary views are viewports defined into a RenderSurface. These will be rendered whenever the texture containing the surface is visible, and can be typically used to implement for example reflections. The texture in question must have been created in rendertarget mode, see Texture's \ref Texture2D::SetSize "SetSize()" function.

+ 5 - 2
Docs/ScriptAPI.dox

@@ -1719,12 +1719,15 @@ Properties:<br>
 - uint maxLights
 - BoundingBox& worldBoundingBox (readonly)
 - BoundingBox& boundingBox
-- Color& ambientStartColor
-- Color& ambientEndColor
+- Color& ambientColor
+- Color& ambientStartColor (readonly)
+- Color& ambientEndColor (readonly)
 - Color& fogColor
 - float fogStart
 - float fogEnd
 - int priority
+- bool override
+- bool ambientGradient
 
 
 StaticModel

+ 7 - 4
Engine/Engine/GraphicsAPI.cpp

@@ -509,10 +509,9 @@ static void RegisterZone(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Zone", "void set_boundingBox(const BoundingBox&in)", asMETHOD(Zone, SetBoundingBox), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "const BoundingBox& get_boundingBox() const", asMETHOD(Zone, GetBoundingBox), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "void set_ambientColor(const Color&in)", asMETHOD(Zone, SetAmbientColor), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Zone", "void set_ambientStartColor(const Color&in)", asMETHOD(Zone, SetAmbientStartColor), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Zone", "const Color& get_ambientStartColor() const", asMETHOD(Zone, GetAmbientStartColor), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Zone", "void set_ambientEndColor(const Color&in)", asMETHOD(Zone, SetAmbientEndColor), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Zone", "const Color& get_ambientEndColor() const", asMETHOD(Zone, GetAmbientEndColor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Zone", "const Color& get_ambientColor() const", asMETHOD(Zone, GetAmbientColor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Zone", "const Color& get_ambientStartColor()", asMETHOD(Zone, GetAmbientStartColor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Zone", "const Color& get_ambientEndColor()", asMETHOD(Zone, GetAmbientEndColor), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "void set_fogColor(const Color&in)", asMETHOD(Zone, SetFogColor), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "const Color& get_fogColor() const", asMETHOD(Zone, GetFogColor), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "void set_fogStart(float)", asMETHOD(Zone, SetFogStart), asCALL_THISCALL);
@@ -521,6 +520,10 @@ static void RegisterZone(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Zone", "float get_fogEnd() const", asMETHOD(Zone, GetFogEnd), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "void set_priority(int)", asMETHOD(Zone, SetPriority), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "int get_priority() const", asMETHOD(Zone, GetPriority), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Zone", "void set_override(bool)", asMETHOD(Zone, SetOverride), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Zone", "bool get_override() const", asMETHOD(Zone, GetOverride), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Zone", "void set_ambientGradient(bool)", asMETHOD(Zone, SetAmbientGradient), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Zone", "bool get_ambientGradient() const", asMETHOD(Zone, GetAmbientGradient), asCALL_THISCALL);
 }
 
 static void RegisterStaticModel(asIScriptEngine* engine)

+ 132 - 24
Engine/Graphics/Zone.cpp

@@ -28,6 +28,8 @@
 #include "XMLElement.h"
 #include "Zone.h"
 
+static const Vector3 DEFAULT_BOUNDING_BOX_MIN(-10.0f, -10.0f, -10.0f);
+static const Vector3 DEFAULT_BOUNDING_BOX_MAX(10.0f, 10.0f, 10.0f);
 static const Color DEFAULT_AMBIENT_COLOR(0.1f, 0.1f, 0.1f);
 static const Color DEFAULT_FOG_COLOR(0.0f, 0.0f, 0.0f);
 static const float DEFAULT_FOG_START = 250.0f;
@@ -37,13 +39,14 @@ OBJECTTYPESTATIC(Zone);
 
 Zone::Zone(Context* context) :
     Drawable(context),
-    ambientStartColor_(DEFAULT_AMBIENT_COLOR),
-    ambientEndColor_(DEFAULT_AMBIENT_COLOR),
+    boundingBox_(DEFAULT_BOUNDING_BOX_MIN, DEFAULT_BOUNDING_BOX_MAX),
+    ambientColor_(DEFAULT_AMBIENT_COLOR),
     fogColor_(DEFAULT_FOG_COLOR),
     fogStart_(DEFAULT_FOG_START),
     fogEnd_(DEFAULT_FOG_END),
     priority_(0),
-    override_(false)
+    override_(false),
+    ambientGradient_(false)
 {
     drawableFlags_ =  DRAWABLE_ZONE;
 }
@@ -56,15 +59,15 @@ void Zone::RegisterObject(Context* context)
 {
     context->RegisterFactory<Zone>();
     
-    ATTRIBUTE(Zone, VAR_VECTOR3, "Bounding Box Min", boundingBox_.min_, Vector3::ZERO, AM_DEFAULT);
-    ATTRIBUTE(Zone, VAR_VECTOR3, "Bounding Box Max", boundingBox_.max_, Vector3::ZERO, AM_DEFAULT);
-    ATTRIBUTE(Zone, VAR_COLOR, "Ambient Color (Min Z)", ambientStartColor_, DEFAULT_AMBIENT_COLOR, AM_DEFAULT);
-    ATTRIBUTE(Zone, VAR_COLOR, "Ambient Color (Max Z)", ambientEndColor_, DEFAULT_AMBIENT_COLOR, AM_DEFAULT);
+    ATTRIBUTE(Zone, VAR_VECTOR3, "Bounding Box Min", boundingBox_.min_, DEFAULT_BOUNDING_BOX_MIN, AM_DEFAULT);
+    ATTRIBUTE(Zone, VAR_VECTOR3, "Bounding Box Max", boundingBox_.max_, DEFAULT_BOUNDING_BOX_MAX, AM_DEFAULT);
+    ATTRIBUTE(Zone, VAR_COLOR, "Ambient Color", ambientColor_, DEFAULT_AMBIENT_COLOR, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_COLOR, "Fog Color", fogColor_, DEFAULT_FOG_COLOR, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_FLOAT, "Fog Start", fogStart_, DEFAULT_FOG_START, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_FLOAT, "Fog End", fogEnd_, DEFAULT_FOG_END, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_BOOL, "Is Visible", visible_, true, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_BOOL, "Override Mode", override_, 0, AM_DEFAULT);
+    ATTRIBUTE(Zone, VAR_BOOL, "Ambient Gradient", ambientGradient_, 0, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_INT, "Priority", priority_, 0, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_INT, "Light Mask", lightMask_, DEFAULT_LIGHTMASK, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_INT, "Shadow Mask", shadowMask_, DEFAULT_SHADOWMASK, AM_DEFAULT);
@@ -75,11 +78,12 @@ void Zone::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 {
     Serializable::OnSetAttribute(attr, src);
     
-    // If bounding box, override mode or priority changes, dirty the drawable as applicable
+    // If bounding box, override mode, visibility or priority changes, dirty the drawable as applicable
     switch (attr.offset_)
     {
     case offsetof(Zone, boundingBox_.min_):
     case offsetof(Zone, boundingBox_.max_):
+    case offsetof(Zone, visible_):
     case offsetof(Zone, priority_):
         OnMarkedDirty(node_);
         break;
@@ -99,17 +103,7 @@ void Zone::SetBoundingBox(const BoundingBox& box)
 
 void Zone::SetAmbientColor(const Color& color)
 {
-    ambientStartColor_ = ambientEndColor_ = Color(color, 1.0f);
-}
-
-void Zone::SetAmbientStartColor(const Color& color)
-{
-    ambientStartColor_ = Color(color, 1.0f);
-}
-
-void Zone::SetAmbientEndColor(const Color& color)
-{
-    ambientEndColor_ = Color(color, 1.0f);
+    ambientColor_ = Color(color, 1.0f);
 }
 
 void Zone::SetFogColor(const Color& color)
@@ -138,6 +132,38 @@ void Zone::SetPriority(int priority)
     priority_ = priority;
 }
 
+void Zone::SetOverride(bool enable)
+{
+    override_ = enable;
+}
+
+void Zone::SetAmbientGradient(bool enable)
+{
+    ambientGradient_ = enable;
+}
+
+const Color& Zone::GetAmbientStartColor()
+{
+    if (!ambientGradient_)
+        return ambientColor_;
+    
+    if (!lastAmbientStartZone_ || !lastAmbientEndZone_)
+        UpdateAmbientGradient();
+    
+    return ambientStartColor_;
+}
+
+const Color& Zone::GetAmbientEndColor()
+{
+    if (!ambientGradient_)
+        return ambientColor_;
+    
+    if (!lastAmbientStartZone_ || !lastAmbientEndZone_)
+        UpdateAmbientGradient();
+    
+    return ambientEndColor_;
+}
+
 bool Zone::IsInside(const Vector3& point)
 {
     // Use an oriented bounding box test
@@ -150,21 +176,103 @@ void Zone::OnMarkedDirty(Node* node)
 {
     Drawable::OnMarkedDirty(node);
     
-    // When marked dirty, clear the cached zone from all drawables inside the zone bounding box
-    if (octant_ && lastBoundingBox_.defined_)
+    // When marked dirty, clear the cached zone from all drawables inside the zone bounding box,
+    // and mark gradient dirty in all neighbor zones
+    if (octant_ && lastWorldBoundingBox_.defined_)
     {
         PODVector<Drawable*> result;
-        BoxOctreeQuery query(result, lastBoundingBox_, DRAWABLE_GEOMETRY);
+        BoxOctreeQuery query(result, lastWorldBoundingBox_, DRAWABLE_GEOMETRY | DRAWABLE_ZONE);
         octant_->GetRoot()->GetDrawables(query);
         
         for (PODVector<Drawable*>::Iterator i = result.Begin(); i != result.End(); ++i)
-            (*i)->SetZone(0);
+        {
+            Drawable* drawable = *i;
+            unsigned drawableFlags = drawable->GetDrawableFlags();
+            if (drawableFlags & DRAWABLE_GEOMETRY)
+                (*i)->SetZone(0);
+            if (drawableFlags & DRAWABLE_ZONE)
+            {
+                Zone* zone = static_cast<Zone*>(drawable);
+                zone->lastAmbientStartZone_.Reset();
+                zone->lastAmbientEndZone_.Reset();
+            }
+        }
     }
     
-    lastBoundingBox_ = GetWorldBoundingBox();
+    lastWorldBoundingBox_ = GetWorldBoundingBox();
+    lastAmbientStartZone_.Reset();
+    lastAmbientEndZone_.Reset();
 }
 
 void Zone::OnWorldBoundingBoxUpdate()
 {
     worldBoundingBox_ = boundingBox_.Transformed(GetWorldTransform());
 }
+
+void Zone::UpdateAmbientGradient()
+{
+    // In case no neighbor zones are found, reset ambient start/end with own ambient color
+    ambientStartColor_ = ambientColor_;
+    ambientEndColor_ = ambientColor_;
+    lastAmbientStartZone_ = this;
+    lastAmbientEndZone_ = this;
+    
+    if (octant_)
+    {
+        const Matrix3x4& worldTransform = GetWorldTransform();
+        Vector3 center = boundingBox_.Center();
+        Vector3 minZPosition = worldTransform * Vector3(center.x_, center.y_, boundingBox_.min_.z_);
+        Vector3 maxZPosition = worldTransform * Vector3(center.x_, center.y_, boundingBox_.max_.z_);
+        
+        PODVector<Zone*> result;
+        {
+            PointOctreeQuery query(reinterpret_cast<PODVector<Drawable*>&>(result), minZPosition, DRAWABLE_ZONE);
+            octant_->GetRoot()->GetDrawables(query);
+        }
+        
+        // Gradient start position: get the highest priority zone that is not this zone
+        int bestPriority = M_MIN_INT;
+        Zone* bestZone = 0;
+        for (PODVector<Zone*>::ConstIterator i = result.Begin(); i != result.End(); ++i)
+        {
+            Zone* zone = *i;
+            int priority = zone->GetPriority();
+            if (zone != this && priority > bestPriority && zone->IsInside(minZPosition))
+            {
+                bestZone = zone;
+                bestPriority = priority;
+            }
+        }
+        
+        if (bestZone)
+        {
+            ambientStartColor_ = bestZone->GetAmbientColor();
+            lastAmbientStartZone_ = bestZone;
+        }
+        
+        // Do the same for gradient end position
+        {
+            PointOctreeQuery query(reinterpret_cast<PODVector<Drawable*>&>(result), maxZPosition, DRAWABLE_ZONE);
+            octant_->GetRoot()->GetDrawables(query);
+        }
+        bestPriority = M_MIN_INT;
+        bestZone = 0;
+        
+        for (PODVector<Zone*>::ConstIterator i = result.Begin(); i != result.End(); ++i)
+        {
+            Zone* zone = *i;
+            int priority = zone->GetPriority();
+            if (zone != this && priority > bestPriority && zone->IsInside(maxZPosition))
+            {
+                bestZone = zone;
+                bestPriority = priority;
+            }
+        }
+        
+        if (bestZone)
+        {
+            ambientEndColor_ = bestZone->GetAmbientColor();
+            lastAmbientEndZone_ = bestZone;
+        }
+    }
+}

+ 26 - 14
Engine/Graphics/Zone.h

@@ -44,31 +44,31 @@ public:
     /// Add debug geometry to the debug renderer.
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     
-    /// %Set bounding box. Will be used as an oriented bounding box to test whether the camera is inside.
+    /// %Set bounding box. Will be used as an oriented bounding box to test whether objects or the camera are inside.
     void SetBoundingBox(const BoundingBox& box);
-    /// %Set ambient color, both start and end.
+    /// %Set ambient color
     void SetAmbientColor(const Color& color);
-    /// %Set ambient start color at bounding box Z minimum.
-    void SetAmbientStartColor(const Color& color);
-    /// %Set ambient end color at bounding box Z maximum.
-    void SetAmbientEndColor(const Color& color);
     /// %Set fog color.
     void SetFogColor(const Color& color);
     /// %Set fog start distance.
     void SetFogStart(float start);
     /// %Set fog end distance.
     void SetFogEnd(float end);
-    /// %Set zone priority. If camera is inside several zones, the one with highest priority is used.
+    /// %Set zone priority. If an object or camera is inside several zones, the one with highest priority is used.
     void SetPriority(int priority);
     /// %Set override mode. If camera is inside an override zone, it will also be used for all drawables.
     void SetOverride(bool enable);
+    /// %Set ambient gradient mode. In gradient mode ambient color is interpolated from neighbor zones.
+    void SetAmbientGradient(bool enable);
     
     /// Return bounding box.
     const BoundingBox& GetBoundingBox() const { return boundingBox_; }
+    /// Return zone's own ambient color, disregarding gradient mode.
+    const Color& GetAmbientColor() const { return ambientColor_; }
     /// Return ambient start color.
-    const Color& GetAmbientStartColor() const { return ambientStartColor_; }
+    const Color& GetAmbientStartColor();
     /// Return ambient end color.
-    const Color& GetAmbientEndColor() const { return ambientEndColor_; }
+    const Color& GetAmbientEndColor();
     /// Return fog color.
     const Color& GetFogColor() const { return fogColor_; }
     /// Return fog start distance.
@@ -79,6 +79,8 @@ public:
     int GetPriority() const { return priority_; }
     /// Return override mode.
     bool GetOverride() const { return override_; }
+    /// Return whether ambient gradient mode is enabled.
+    bool GetAmbientGradient() const { return ambientGradient_; }
     
     /// Check whether a point is inside.
     virtual bool IsInside(const Vector3& point);
@@ -88,14 +90,22 @@ protected:
     virtual void OnMarkedDirty(Node* node);
     /// Recalculate the world-space bounding box.
     virtual void OnWorldBoundingBoxUpdate();
+    /// Recalculate the ambient gradient colors from neighbor zones.
+    void UpdateAmbientGradient();
     
+    /// Last zone used for ambient gradient start color.
+    WeakPtr<Zone> lastAmbientStartZone_;
+    /// Last zone used for ambient gradient end color.
+    WeakPtr<Zone> lastAmbientEndZone_;
     /// Bounding box.
     BoundingBox boundingBox_;
-    /// Last bounding box.
-    BoundingBox lastBoundingBox_;
-    /// Ambient start color.
+    /// Last world-space bounding box.
+    BoundingBox lastWorldBoundingBox_;
+    /// Ambient color.
+    Color ambientColor_;
+    /// Cached ambient start color.
     Color ambientStartColor_;
-    /// Ambient end color.
+    /// Cached ambient end color.
     Color ambientEndColor_;
     /// Fog color.
     Color fogColor_;
@@ -105,6 +115,8 @@ protected:
     float fogEnd_;
     /// Zone priority.
     int priority_;
-    /// Override mode.
+    /// Override mode flag.
     bool override_;
+    /// Ambient gradient mode flag.
+    bool ambientGradient_;
 };