void Vehicle::SetupLights(int front, int rear, BoundingBox box) { if (front) { for (int f{0}; f < front; ++f){ Pair<SharedPtr<Node>, SharedPtr<Light>> light; light.first_ = rootNode_->CreateChild("HeadLight"); light.first_->SetDirection(Vector3(0.0f, -0.23f, 0.666f)); if (front == 1) { light.first_->SetPosition(Vector3(0.5f * (box.min_.x_ + box.max_.x_), box.min_.y_, box.max_.z_)); } else { light.first_->SetPosition(Vector3(box.min_.x_ + f * (box.Size().x_ / (front - 1)), box.min_.y_, box.max_.z_)); } light.second_ = light.first_->CreateComponent<Light>(); light.second_->SetLightType(LIGHT_SPOT); light.second_->SetColor(Color(1.0f, 0.9f, 0.8f)); light.second_->SetRange(8.0f); light.second_->SetFov(60.0f); light.second_->SetBrightness(5.0f); light.second_->SetCastShadows(true); light.second_->SetShadowResolution(0.25f); light.second_->SetShadowBias(BiasParameters(0.00001f, 0.5f)); light.second_->SetShadowCascade(CascadeParameters(0.23f, 2.0f, 3.0f, 5.0f, 0.5f)); headLights_.Push(light); } } if (rear) { for (int r{0}; r < rear; ++r){ Pair<SharedPtr<Node>, SharedPtr<Light>> light; light.first_ = rootNode_->CreateChild("TailLight"); light.first_->SetDirection(Vector3(0.0f, -0.6f, -0.5f)); if (front == 1) { light.first_->SetPosition(Vector3(0.5f * (box.min_.x_ + box.max_.x_), box.max_.y_, box.min_.z_)); } else { light.first_->SetPosition(Vector3(box.min_.x_ + r * (box.Size().x_ / (rear - 1)), box.max_.y_, box.min_.z_)); light.first_->Rotate(Quaternion(30.0f - r * (60.0f / (rear - 1)), Vector3::UP), TS_WORLD); } light.second_ = light.first_->CreateComponent<Light>(); light.second_->SetLightType(LIGHT_SPOT); light.second_->SetColor(Color::RED); light.second_->SetRange(3.0f); light.second_->SetFov(120.0f); light.second_->SetBrightness(2.0f); light.second_->SetCastShadows(true); light.second_->SetShadowResolution(0.25f); tailLights_.Push(light); } } }
void Octant::Initialize(const BoundingBox& box) { worldBoundingBox_ = box; center_ = box.Center(); halfSize_ = 0.5f * box.Size(); cullingBox_ = BoundingBox(worldBoundingBox_.min_ - halfSize_, worldBoundingBox_.max_ + halfSize_); }
bool Octant::CheckDrawableFit(const BoundingBox& box) const { Vector3 boxSize = box.Size(); // If max split level, size always OK, otherwise check that box is at least half size of octant if (level_ >= root_->GetNumLevels() || boxSize.x_ >= halfSize_.x_ || boxSize.y_ >= halfSize_.y_ || boxSize.z_ >= halfSize_.z_) return true; // Also check if the box can not fit a child octant's culling box, in that case size OK (must insert here) else { if (box.min_.x_ <= worldBoundingBox_.min_.x_ - 0.5f * halfSize_.x_ || box.max_.x_ >= worldBoundingBox_.max_.x_ + 0.5f * halfSize_.x_ || box.min_.y_ <= worldBoundingBox_.min_.y_ - 0.5f * halfSize_.y_ || box.max_.y_ >= worldBoundingBox_.max_.y_ + 0.5f * halfSize_.y_ || box.min_.z_ <= worldBoundingBox_.min_.z_ - 0.5f * halfSize_.z_ || box.max_.z_ >= worldBoundingBox_.max_.z_ + 0.5f * halfSize_.z_) return true; } // Bounding box too small, should create a child octant return false; }
void AnimatedModel::UpdateBatches(const FrameInfo& frame) { const Matrix3x4& worldTransform = node_->GetWorldTransform(); const BoundingBox& worldBoundingBox = GetWorldBoundingBox(); distance_ = frame.camera_->GetDistance(worldBoundingBox.Center()); // Note: per-geometry distances do not take skinning into account. Especially in case of a ragdoll they may be // much off base if the node's own transform is not updated if (batches_.Size() == 1) batches_[0].distance_ = distance_; else { for (unsigned i = 0; i < batches_.Size(); ++i) batches_[i].distance_ = frame.camera_->GetDistance(worldTransform * geometryData_[i].center_); } // Use a transformed version of the model's bounding box instead of world bounding box for LOD scale // determination so that animation does not change the scale BoundingBox transformedBoundingBox = boundingBox_.Transformed(worldTransform); float scale = transformedBoundingBox.Size().DotProduct(DOT_SCALE); float newLodDistance = frame.camera_->GetLodDistance(distance_, scale, lodBias_); // If model is rendered from several views, use the minimum LOD distance for animation LOD if (frame.frameNumber_ != animationLodFrameNumber_) { animationLodDistance_ = newLodDistance; animationLodFrameNumber_ = frame.frameNumber_; } else animationLodDistance_ = Min(animationLodDistance_, newLodDistance); if (newLodDistance != lodDistance_) { lodDistance_ = newLodDistance; CalculateLodLevels(); } }
void Light::SetupShadowViews(Camera* mainCamera, Vector<AutoPtr<ShadowView> >& shadowViews, size_t& useIndex) { size_t numViews = NumShadowViews(); if (!numViews) return; if (shadowViews.Size() < useIndex + numViews) shadowViews.Resize(useIndex + numViews); int numVerticalSplits = (lightType == LIGHT_POINT || (lightType == LIGHT_DIRECTIONAL && NumShadowSplits() > 2)) ? 2 : 1; int actualShadowMapSize = shadowRect.Height() / numVerticalSplits; for (size_t i = 0; i < numViews; ++i) { if (!shadowViews[useIndex + i]) shadowViews[useIndex + i] = new ShadowView(); ShadowView* view = shadowViews[useIndex + i].Get(); view->Clear(); view->light = this; Camera& shadowCamera = view->shadowCamera; switch (lightType) { case LIGHT_DIRECTIONAL: { IntVector2 topLeft(shadowRect.left, shadowRect.top); if (i & 1) topLeft.x += actualShadowMapSize; if (i & 2) topLeft.y += actualShadowMapSize; view->viewport = IntRect(topLeft.x, topLeft.y, topLeft.x + actualShadowMapSize, topLeft.y + actualShadowMapSize); float splitStart = Max(mainCamera->NearClip(), (i == 0) ? 0.0f : ShadowSplit(i - 1)); float splitEnd = Min(mainCamera->FarClip(), ShadowSplit(i)); float extrusionDistance = mainCamera->FarClip(); // Calculate initial position & rotation shadowCamera.SetTransform(mainCamera->WorldPosition() - extrusionDistance * WorldDirection(), WorldRotation()); // Calculate main camera shadowed frustum in light's view space Frustum splitFrustum = mainCamera->WorldSplitFrustum(splitStart, splitEnd); const Matrix3x4& lightView = shadowCamera.ViewMatrix(); Frustum lightViewFrustum = splitFrustum.Transformed(lightView); // Fit the frustum inside a bounding box BoundingBox shadowBox; shadowBox.Define(lightViewFrustum); // If shadow camera is far away from the frustum, can bring it closer for better depth precision /// \todo The minimum distance is somewhat arbitrary float minDistance = mainCamera->FarClip() * 0.25f; if (shadowBox.min.z > minDistance) { float move = shadowBox.min.z - minDistance; shadowCamera.Translate(Vector3(0.0f, 0.f, move)); shadowBox.min.z -= move, shadowBox.max.z -= move; } shadowCamera.SetOrthographic(true); shadowCamera.SetFarClip(shadowBox.max.z); Vector3 center = shadowBox.Center(); Vector3 size = shadowBox.Size(); shadowCamera.SetOrthoSize(Vector2(size.x, size.y)); shadowCamera.SetZoom(1.0f); // Center shadow camera to the view space bounding box Vector3 pos(shadowCamera.WorldPosition()); Quaternion rot(shadowCamera.WorldRotation()); Vector3 adjust(center.x, center.y, 0.0f); shadowCamera.Translate(rot * adjust, TS_WORLD); // Snap to whole texels { Vector3 viewPos(rot.Inverse() * shadowCamera.WorldPosition()); float invSize = 1.0f / actualShadowMapSize; Vector2 texelSize(size.x * invSize, size.y * invSize); Vector3 snap(-fmodf(viewPos.x, texelSize.x), -fmodf(viewPos.y, texelSize.y), 0.0f); shadowCamera.Translate(rot * snap, TS_WORLD); } } break; case LIGHT_POINT: { static const Quaternion pointLightFaceRotations[] = { Quaternion(0.0f, 90.0f, 0.0f), Quaternion(0.0f, -90.0f, 0.0f), Quaternion(-90.0f, 0.0f, 0.0f), Quaternion(90.0f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 0.0f), Quaternion(0.0f, 180.0f, 0.0f) }; IntVector2 topLeft(shadowRect.left, shadowRect.top); if (i & 1) topLeft.y += actualShadowMapSize; topLeft.x += ((unsigned)i >> 1) * actualShadowMapSize; view->viewport = IntRect(topLeft.x, topLeft.y, topLeft.x + actualShadowMapSize, topLeft.y + actualShadowMapSize); shadowCamera.SetTransform(WorldPosition(), pointLightFaceRotations[i]); shadowCamera.SetFov(90.0f); // Adjust zoom to avoid edge sampling artifacts (there is a matching adjustment in the shadow sampling) shadowCamera.SetZoom(0.99f); shadowCamera.SetFarClip(Range()); shadowCamera.SetNearClip(Range() * 0.01f); shadowCamera.SetOrthographic(false); shadowCamera.SetAspectRatio(1.0f); } break; case LIGHT_SPOT: view->viewport = shadowRect; shadowCamera.SetTransform(WorldPosition(), WorldRotation()); shadowCamera.SetFov(fov); shadowCamera.SetZoom(1.0f); shadowCamera.SetFarClip(Range()); shadowCamera.SetNearClip(Range() * 0.01f); shadowCamera.SetOrthographic(false); shadowCamera.SetAspectRatio(1.0f); break; } } // Setup shadow matrices now as camera positions have been finalized if (lightType != LIGHT_POINT) { shadowMatrices.Resize(numViews); for (size_t i = 0; i < numViews; ++i) { ShadowView* view = shadowViews[useIndex + i].Get(); Camera& shadowCamera = view->shadowCamera; float width = (float)shadowMap->Width(); float height = (float)shadowMap->Height(); Vector3 offset((float)view->viewport.left / width, (float)view->viewport.top / height, 0.0f); Vector3 scale(0.5f * (float)view->viewport.Width() / width, 0.5f * (float)view->viewport.Height() / height, 1.0f); offset.x += scale.x; offset.y += scale.y; scale.y = -scale.y; // OpenGL has different depth range #ifdef TURSO3D_OPENGL offset.z = 0.5f; scale.z = 0.5f; #endif Matrix4 texAdjust(Matrix4::IDENTITY); texAdjust.SetTranslation(offset); texAdjust.SetScale(scale); shadowMatrices[i] = texAdjust * shadowCamera.ProjectionMatrix() * shadowCamera.ViewMatrix(); } } else { // Point lights use an extra constant instead shadowMatrices.Clear(); Vector2 textureSize((float)shadowMap->Width(), (float)shadowMap->Height()); pointShadowParameters = Vector4(actualShadowMapSize / textureSize.x, actualShadowMapSize / textureSize.y, (float)shadowRect.left / textureSize.x, (float)shadowRect.top / textureSize.y); } // Calculate shadow mapping constants Camera& shadowCamera = shadowViews[useIndex]->shadowCamera; float nearClip = shadowCamera.NearClip(); float farClip = shadowCamera.FarClip(); float q = farClip / (farClip - nearClip); float r = -q * nearClip; shadowParameters = Vector4(0.5f / (float)shadowMap->Width(), 0.5f / (float)shadowMap->Height(), q, r); useIndex += numViews; }