// // Urho3D Engine // Copyright (c) 2008-2012 Lasse Öörni // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // #include "Precompiled.h" #include "Context.h" #include "DebugRenderer.h" #include "Octree.h" #include "XMLElement.h" #include "Zone.h" #include "DebugNew.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; static const float DEFAULT_FOG_END = 1000.0f; OBJECTTYPESTATIC(Zone); Zone::Zone(Context* context) : Drawable(context), 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), ambientGradient_(false) { drawableFlags_ = DRAWABLE_ZONE; } Zone::~Zone() { } void Zone::RegisterObject(Context* context) { context->RegisterFactory(); 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); ACCESSOR_ATTRIBUTE(Zone, VAR_INT, "Zone Mask", GetZoneMask, SetZoneMask, unsigned, DEFAULT_ZONEMASK, AM_DEFAULT); } void Zone::OnSetAttribute(const AttributeInfo& attr, const Variant& src) { Serializable::OnSetAttribute(attr, src); // 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; } } void Zone::DrawDebugGeometry(DebugRenderer* debug, bool depthTest) { if (debug) debug->AddBoundingBox(boundingBox_, GetWorldTransform(), Color::GREEN, depthTest); } void Zone::SetBoundingBox(const BoundingBox& box) { boundingBox_ = box; OnMarkedDirty(node_); } void Zone::SetAmbientColor(const Color& color) { ambientColor_ = Color(color, 1.0f); } void Zone::SetFogColor(const Color& color) { fogColor_ = Color(color, 1.0f); } void Zone::SetFogStart(float start) { if (start < 0.0f) start = 0.0f; fogStart_ = start; } void Zone::SetFogEnd(float end) { if (end < 0.0f) end = 0.0f; fogEnd_ = end; } 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 Matrix3x4 inverse(GetWorldTransform().Inverse()); Vector3 localPoint(inverse * point); return boundingBox_.IsInside(localPoint) != OUTSIDE; } void Zone::OnMarkedDirty(Node* node) { Drawable::OnMarkedDirty(node); // 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 result; BoxOctreeQuery query(result, lastWorldBoundingBox_, DRAWABLE_GEOMETRY | DRAWABLE_ZONE); octant_->GetRoot()->GetDrawables(query); for (PODVector::Iterator i = result.Begin(); i != result.End(); ++i) { Drawable* drawable = *i; unsigned drawableFlags = drawable->GetDrawableFlags(); if (drawableFlags & DRAWABLE_GEOMETRY) (*i)->SetZone(0); if (drawableFlags & DRAWABLE_ZONE) { Zone* zone = static_cast(drawable); zone->lastAmbientStartZone_.Reset(); zone->lastAmbientEndZone_.Reset(); } } } 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 result; { PointOctreeQuery query(reinterpret_cast&>(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::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&>(result), maxZPosition, DRAWABLE_ZONE); octant_->GetRoot()->GetDrawables(query); } bestPriority = M_MIN_INT; bestZone = 0; for (PODVector::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; } } }