void BillboardSet::UpdateBatches(const FrameInfo& frame) { // If beginning a new frame, assume no sorting first if (frame.frameNumber_ != sortFrameNumber_) { sortThisFrame_ = false; sortFrameNumber_ = frame.frameNumber_; } Vector3 worldPos = node_->GetWorldPosition(); Vector3 offset = (worldPos - frame.camera_->GetNode()->GetWorldPosition()); // Sort if position relative to camera has changed if (offset != previousOffset_ & sorted_) sortThisFrame_ = true; distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center()); // Calculate scaled distance for animation LOD float scale = GetWorldBoundingBox().Size().DotProduct(DOT_SCALE); // If there are no billboards, the size becomes zero, and LOD'ed updates no longer happen. Disable LOD in that case if (scale > M_EPSILON) lodDistance_ = frame.camera_->GetLodDistance(distance_, scale, lodBias_); else lodDistance_ = 0.0f; batches_[0].distance_ = distance_; batches_[0].numWorldTransforms_ = 2; // Billboard positioning transforms_[0] = relative_ ? node_->GetWorldTransform() : Matrix3x4::IDENTITY; // Billboard rotation transforms_[1] = Matrix3x4(Vector3::ZERO, faceCameraMode_ != FC_NONE ? frame.camera_->GetFaceCameraRotation( node_->GetWorldPosition(), node_->GetWorldRotation(), faceCameraMode_) : node_->GetWorldRotation(), Vector3::ONE); }
void BillboardSet::UpdateBatches(const FrameInfo& frame) { // Check if position relative to camera has changed, and re-sort in that case const Matrix3x4& worldTransform = node_->GetWorldTransform(); Vector3 worldPos = worldTransform.Translation(); Vector3 offset = (worldPos - frame.camera_->GetNode()->GetWorldPosition()); if (offset != previousOffset_) { previousOffset_ = offset; if (sorted_) { // Sort billboards only once per frame. This means that secondary views will get // the same sorting as the main view if (frame.frameNumber_ != sortFrameNumber_) { sortFrameNumber_ = frame.frameNumber_; bufferDirty_ = true; } } } distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center()); // Calculate scaled distance for animation LOD float scale = GetWorldBoundingBox().Size().DotProduct(DOT_SCALE); // If there are no billboards, the size becomes zero, and LOD'ed updates no longer happen. Disable LOD in that case if (scale > M_EPSILON) lodDistance_ = frame.camera_->GetLodDistance(distance_, scale, lodBias_); else lodDistance_ = 0.0f; batches_[0].distance_ = distance_; batches_[0].worldTransform_ = relative_ ? &node_->GetWorldTransform() : &Matrix3x4::IDENTITY; }
csScreenBoxResult csMeshWrapper::GetScreenBoundingBox (iCamera *camera) { csScreenBoxResult rc; // Calculate camera space bbox. csReversibleTransform tr_o2c = camera->GetTransform (); if (!movable.IsFullTransformIdentity ()) tr_o2c /= movable.GetFullTransform (); rc.cbox = GetTransformedBoundingBox (tr_o2c); // Calculate screen space bbox. float minz, maxz; const csBox3& wbox = GetWorldBoundingBox(); if(!wbox.ProjectBox(camera->GetTransform(), camera->GetProjectionMatrix(), rc.sbox, minz, maxz, engine->G3D->GetWidth(), engine->G3D->GetHeight())) { rc.distance = -1; } else { rc.distance = rc.cbox.MaxZ (); } return rc; }
Air::U1 MeshEntity::RayCast( const Ray& ray ,float* pOutDistance) { #if 1 if(!GetWorldBoundingBox().RayCast(ray.GetOrigin(),ray.GetDirection())){//.Intersect(GetWorldBoundingBox())){ return false; } #endif Matrix matWorld = *GetWorldMatrix(); Matrix matWorldInv = matWorld; matWorldInv.Inverse(); Float3 vStart = ray.m_vStart; Float3 vLookAt = vStart + ray.m_vDirection; vStart = matWorldInv*vStart; vLookAt = matWorldInv*vLookAt; Float3 vDir = (vLookAt - vStart); vDir.Normalize(); Ray objSpaceRay(vStart,vDir); float fDistance = 999999.0f; U1 bHit = m_pMesh->RayCast(objSpaceRay,&fDistance); if(bHit && pOutDistance!=NULL){ Float3 vObjSpaceHitPostion = vStart + vDir*fDistance; Float3 vWorldSpaceHiPosition = matWorld*vObjSpaceHitPostion; *pOutDistance = (vWorldSpaceHiPosition - ray.m_vStart).Length(); } return bHit; }
void Text3D::UpdateBatches(const FrameInfo& frame) { distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center()); if (faceCameraMode_ != FC_NONE) { Vector3 worldPosition = node_->GetWorldPosition(); customWorldTransform_ = Matrix3x4(worldPosition, frame.camera_->GetFaceCameraRotation( worldPosition, node_->GetWorldRotation(), faceCameraMode_), node_->GetWorldScale()); worldBoundingBoxDirty_ = true; } for (unsigned i = 0; i < batches_.Size(); ++i) { batches_[i].distance_ = distance_; batches_[i].worldTransform_ = faceCameraMode_ != FC_NONE ? &customWorldTransform_ : &node_->GetWorldTransform(); } for (unsigned i = 0; i < uiBatches_.Size(); ++i) { if (uiBatches_[i].texture_ && uiBatches_[i].texture_->IsDataLost()) { fontDataLost_ = true; break; } } }
void StaticModelGroup::UpdateBatches(const FrameInfo& frame) { // Getting the world bounding box ensures the transforms are updated const BoundingBox& worldBoundingBox = GetWorldBoundingBox(); const Matrix3x4& worldTransform = node_->GetWorldTransform(); distance_ = frame.camera_->GetDistance(worldBoundingBox.Center()); if (batches_.Size() > 1) { for (unsigned i = 0; i < batches_.Size(); ++i) { batches_[i].distance_ = frame.camera_->GetDistance(worldTransform * geometryData_[i].center_); batches_[i].worldTransform_ = numWorldTransforms_ ? &worldTransforms_[0] : &Matrix3x4::IDENTITY; batches_[i].numWorldTransforms_ = numWorldTransforms_; } } else if (batches_.Size() == 1) { batches_[0].distance_ = distance_; batches_[0].worldTransform_ = numWorldTransforms_ ? &worldTransforms_[0] : &Matrix3x4::IDENTITY; batches_[0].numWorldTransforms_ = numWorldTransforms_; } float scale = worldBoundingBox.Size().DotProduct(DOT_SCALE); float newLodDistance = frame.camera_->GetLodDistance(distance_, scale, lodBias_); if (newLodDistance != lodDistance_) { lodDistance_ = newLodDistance; CalculateLodLevels(); } }
void Zone::ClearDrawablesZone() { if (octant_ && lastWorldBoundingBox_.defined_) { PODVector<Drawable*> result; BoxOctreeQuery query(result, lastWorldBoundingBox_, DRAWABLE_GEOMETRY | DRAWABLE_ZONE); octant_->GetRoot()->GetDrawables(query); for (PODVector<Drawable*>::Iterator i = result.Begin(); i != result.End(); ++i) { Drawable* drawable = *i; unsigned drawableFlags = drawable->GetDrawableFlags(); if (drawableFlags & DRAWABLE_GEOMETRY) drawable->SetZone(0); else if (drawableFlags & DRAWABLE_ZONE) { Zone* zone = static_cast<Zone*>(drawable); zone->lastAmbientStartZone_.Reset(); zone->lastAmbientEndZone_.Reset(); } } } lastWorldBoundingBox_ = GetWorldBoundingBox(); lastAmbientStartZone_.Reset(); lastAmbientEndZone_.Reset(); }
void StaticModel::UpdateBatches(const FrameInfo& frame) { const BoundingBox& worldBoundingBox = GetWorldBoundingBox(); distance_ = frame.camera_->GetDistance(worldBoundingBox.Center()); if (batches_.Size() == 1) batches_[0].distance_ = distance_; else { const Matrix3x4& worldTransform = node_->GetWorldTransform(); for (unsigned i = 0; i < batches_.Size(); ++i) batches_[i].distance_ = frame.camera_->GetDistance(worldTransform * geometryData_[i].center_); } float scale = worldBoundingBox.Size().DotProduct(DOT_SCALE); float newLodDistance = frame.camera_->GetLodDistance(distance_, scale, lodBias_); if (newLodDistance != lodDistance_) { lodDistance_ = newLodDistance; CalculateLodLevels(); } // ATOMIC BEGIN if (geometryDisabled_) UpdateBatchesHideGeometry(); // ATOMIC END }
void Drawable2D::UpdateBatches(const FrameInfo& frame) { const Matrix3x4& worldTransform = node_->GetWorldTransform(); distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center()); batches_[0].distance_ = distance_; batches_[0].worldTransform_ = &worldTransform; }
void StaticModelGroup::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { // If no bones or no bone-level testing, use the Drawable test RayQueryLevel level = query.level_; if (level < RAY_AABB) { Drawable::ProcessRayQuery(query, results); return; } // Check ray hit distance to AABB before proceeding with more accurate tests // GetWorldBoundingBox() updates the world transforms if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_) return; for (unsigned i = 0; i < numWorldTransforms_; ++i) { // Initial test using AABB float distance = query.ray_.HitDistance(boundingBox_.Transformed(worldTransforms_[i])); // Then proceed to OBB and triangle-level tests if necessary if (level >= RAY_OBB && distance < query.maxDistance_) { Matrix3x4 inverse = worldTransforms_[i].Inverse(); Ray localRay = query.ray_.Transformed(inverse); distance = localRay.HitDistance(boundingBox_); if (level == RAY_TRIANGLE && distance < query.maxDistance_) { distance = M_INFINITY; for (unsigned j = 0; j < batches_.Size(); ++j) { Geometry* geometry = batches_[j].geometry_; if (geometry) { float geometryDistance = geometry->GetHitDistance(localRay); if (geometryDistance < query.maxDistance_ && geometryDistance < distance) distance = geometryDistance; } } } } if (distance < query.maxDistance_) { RayQueryResult result; result.drawable_ = this; result.node_ = node_; result.distance_ = distance; result.subObject_ = i; results.Push(result); } } }
void SceneNode::OnUpdateRenderQueues( const Camera& camera, RenderOrder order, uint32_t buckterFilter, uint32_t filterIgnore ) { const BoundingBoxf& worldBound = GetWorldBoundingBox(); if (!camera.Visible(GetWorldBoundingBox())) return; RenderQueue& renderQueue = mScene->GetRenderQueue(); for (SceneObject* pSceneObject : mAttachedObjects) { if (pSceneObject->IsActive() && pSceneObject->Renderable()) pSceneObject->OnUpdateRenderQueue(&renderQueue, camera, order, buckterFilter, filterIgnore); } // recursively call children for (Node* node : mChildren) { SceneNode* child = static_cast<SceneNode*>(node); child->OnUpdateRenderQueues(camera, order, buckterFilter, filterIgnore); } }
void Light::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { // Do not record a raycast result for a directional light, as it would block all other results if (lightType_ == LIGHT_DIRECTIONAL) return; float distance = query.maxDistance_; switch (query.level_) { case RAY_AABB: Drawable::ProcessRayQuery(query, results); return; case RAY_OBB: { Matrix3x4 inverse(node_->GetWorldTransform().Inverse()); Ray localRay = query.ray_.Transformed(inverse); distance = localRay.HitDistance(GetWorldBoundingBox().Transformed(inverse)); if (distance >= query.maxDistance_) return; } break; case RAY_TRIANGLE: if (lightType_ == LIGHT_SPOT) { distance = query.ray_.HitDistance(GetFrustum()); if (distance >= query.maxDistance_) return; } else { distance = query.ray_.HitDistance(Sphere(node_->GetWorldPosition(), range_)); if (distance >= query.maxDistance_) return; } break; case RAY_TRIANGLE_UV: LOGWARNING("RAY_TRIANGLE_UV query level is not supported for Light component"); return; } // If the code reaches here then we have a hit RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = -query.ray_.direction_; result.distance_ = distance; result.drawable_ = this; result.node_ = node_; result.subObject_ = M_MAX_UNSIGNED; results.Push(result); }
void Drawable::LimitLights() { // Maximum lights value 0 means unlimited if (!maxLights_ || lights_.Size() <= maxLights_) return; // If more lights than allowed, move to vertex lights and cut the list const BoundingBox& box = GetWorldBoundingBox(); for (unsigned i = 0; i < lights_.Size(); ++i) lights_[i]->SetIntensitySortValue(box); Sort(lights_.Begin(), lights_.End(), CompareDrawables); vertexLights_.Insert(vertexLights_.End(), lights_.Begin() + maxLights_, lights_.End()); lights_.Resize(maxLights_); }
void Drawable::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { float distance = query.ray_.HitDistance(GetWorldBoundingBox()); if (distance < query.maxDistance_) { RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = -query.ray_.direction_; result.distance_ = distance; result.drawable_ = this; result.node_ = GetNode(); result.subObject_ = M_MAX_UNSIGNED; results.Push(result); } }
void Text3D::UpdateBatches(const FrameInfo& frame) { const Matrix3x4& worldTransform = node_->GetWorldTransform(); distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center()); if (faceCamera_) { customWorldTransform_ = Matrix3x4(node_->GetWorldPosition(), frame.camera_->GetNode()->GetWorldRotation(), node_->GetWorldScale()); worldBoundingBoxDirty_ = true; } for (unsigned i = 0; i < batches_.Size(); ++i) { batches_[i].distance_ = distance_; batches_[i].worldTransform_ = faceCamera_ ? &customWorldTransform_ : &worldTransform; } }
bool StaticModelGroup::DrawOcclusion(OcclusionBuffer* buffer) { // Make sure instance transforms are up-to-date GetWorldBoundingBox(); for (unsigned i = 0; i < numWorldTransforms_; ++i) { for (unsigned j = 0; j < batches_.Size(); ++j) { Geometry* geometry = GetLodGeometry(j, occlusionLodLevel_); if (!geometry) continue; // Check that the material is suitable for occlusion (default material always is) and set culling mode Material* material = batches_[j].material_; if (material) { if (!material->GetOcclusion()) continue; buffer->SetCullMode(material->GetCullMode()); } else buffer->SetCullMode(CULL_CCW); const unsigned char* vertexData; unsigned vertexSize; const unsigned char* indexData; unsigned indexSize; unsigned elementMask; geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elementMask); // Check for valid geometry data if (!vertexData || !indexData) continue; unsigned indexStart = geometry->GetIndexStart(); unsigned indexCount = geometry->GetIndexCount(); // Draw and check for running out of triangles if (!buffer->Draw(worldTransforms_[i], vertexData, vertexSize, indexData, indexSize, indexStart, indexCount)) return false; } } return true; }
void Drawable::UpdateBatches(const FrameInfo& frame) { const BoundingBox& worldBoundingBox = GetWorldBoundingBox(); const Matrix3x4& worldTransform = node_->GetWorldTransform(); distance_ = frame.camera_->GetDistance(worldBoundingBox.Center()); for (unsigned i = 0; i < batches_.Size(); ++i) { batches_[i].distance_ = distance_; batches_[i].worldTransform_ = &worldTransform; } float scale = worldBoundingBox.Size().DotProduct(DOT_SCALE); float newLodDistance = frame.camera_->GetLodDistance(distance_, scale, lodBias_); if (newLodDistance != lodDistance_) lodDistance_ = newLodDistance; }
void BillboardSet::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { // If no billboard-level testing, use the Drawable test if (query.level_ < RAY_TRIANGLE) { Drawable::ProcessRayQuery(query, results); return; } // Check ray hit distance to AABB before proceeding with billboard-level tests if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_) return; const Matrix3x4& worldTransform = node_->GetWorldTransform(); Matrix3x4 billboardTransform = relative_ ? worldTransform : Matrix3x4::IDENTITY; Vector3 billboardScale = scaled_ ? worldTransform.Scale() : Vector3::ONE; for (unsigned i = 0; i < billboards_.Size(); ++i) { if (!billboards_[i].enabled_) continue; // Approximate the billboards as spheres for raycasting float size = INV_SQRT_TWO * (billboards_[i].size_.x_ * billboardScale.x_ + billboards_[i].size_.y_ * billboardScale.y_); if (fixedScreenSize_) size *= billboards_[i].screenScaleFactor_; Vector3 center = billboardTransform * billboards_[i].position_; Sphere billboardSphere(center, size); float distance = query.ray_.HitDistance(billboardSphere); if (distance < query.maxDistance_) { // If the code reaches here then we have a hit RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = -query.ray_.direction_; result.distance_ = distance; result.drawable_ = this; result.node_ = node_; result.subObject_ = i; results.Push(result); } } }
/// Gets the center world position of the tree's trunk. /// @return The center world position of the tree's trunk. MATH::Vector2f Tree::GetTrunkCenterWorldPosition() const { // GET THE BOUNDING BOX OF THE TREE. MATH::FloatRectangle world_bounding_box = GetWorldBoundingBox(); // CALCULATE THE CENTER OF THE TREE'S TRUNK. // The center will be set so that it works for all current tree sizes. // It is vertically placed halfway up the pixel grid for the smallest // tree size, which also works well for other tree sizes. const float OFFSET_FROM_TREE_BOTTOM_TO_TRUNK_CENTER_IN_PIXELS = 8.0f; float tree_bottom_world_y_position = world_bounding_box.GetBottomYPosition(); // Y gets smaller going up. float trunk_center_y_position = tree_bottom_world_y_position - OFFSET_FROM_TREE_BOTTOM_TO_TRUNK_CENTER_IN_PIXELS; float trunk_center_x_position = world_bounding_box.GetCenterXPosition(); MATH::Vector2f trunk_center_world_position( trunk_center_x_position, trunk_center_y_position); return trunk_center_world_position; }
void Zone::OnMarkedDirty(Node* node) { // Due to the octree query and weak pointer manipulation, is not safe from worker threads Scene* scene = GetScene(); if (scene && scene->IsThreadedUpdate()) { scene->DelayedMarkedDirty(this); return; } 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<Drawable*> result; BoxOctreeQuery query(result, lastWorldBoundingBox_, DRAWABLE_GEOMETRY | DRAWABLE_ZONE); octant_->GetRoot()->GetDrawables(query); for (PODVector<Drawable*>::Iterator i = result.Begin(); i != result.End(); ++i) { Drawable* drawable = *i; unsigned drawableFlags = drawable->GetDrawableFlags(); if (drawableFlags & DRAWABLE_GEOMETRY) { if (drawable->GetZone() == this) drawable->SetZone(0); } else if (drawableFlags & DRAWABLE_ZONE) { Zone* zone = static_cast<Zone*>(drawable); zone->lastAmbientStartZone_.Reset(); zone->lastAmbientEndZone_.Reset(); } } } lastWorldBoundingBox_ = GetWorldBoundingBox(); lastAmbientStartZone_.Reset(); lastAmbientEndZone_.Reset(); inverseWorldDirty_ = true; }
void Drawable::LimitVertexLights(bool removeConvertedLights) { if (removeConvertedLights) { for (unsigned i = vertexLights_.Size() - 1; i < vertexLights_.Size(); --i) { if (!vertexLights_[i]->GetPerVertex()) vertexLights_.Erase(i); } } if (vertexLights_.Size() <= MAX_VERTEX_LIGHTS) return; const BoundingBox& box = GetWorldBoundingBox(); for (unsigned i = 0; i < vertexLights_.Size(); ++i) vertexLights_[i]->SetIntensitySortValue(box); Sort(vertexLights_.Begin(), vertexLights_.End(), CompareDrawables); vertexLights_.Resize(MAX_VERTEX_LIGHTS); }
void Text3D::UpdateBatches(const FrameInfo& frame) { distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center()); if (faceCameraMode_ != FC_NONE || fixedScreenSize_) CalculateFixedScreenSize(frame); for (unsigned i = 0; i < batches_.Size(); ++i) { batches_[i].distance_ = distance_; batches_[i].worldTransform_ = faceCameraMode_ != FC_NONE ? &customWorldTransform_ : &node_->GetWorldTransform(); } for (unsigned i = 0; i < uiBatches_.Size(); ++i) { if (uiBatches_[i].texture_ && uiBatches_[i].texture_->IsDataLost()) { fontDataLost_ = true; break; } } }
void TerrainPatch::UpdateBatches(const FrameInfo& frame) { const Matrix3x4& worldTransform = node_->GetWorldTransform(); distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center()); float scale = worldTransform.Scale().DotProduct(DOT_SCALE); lodDistance_ = frame.camera_->GetLodDistance(distance_, scale, lodBias_); batches_[0].distance_ = distance_; batches_[0].worldTransform_ = &worldTransform; unsigned newLodLevel = 0; for (unsigned i = 0; i < lodErrors_.Size(); ++i) { if (lodErrors_[i] / lodDistance_ > LOD_CONSTANT) break; else newLodLevel = i; } lodLevel_ = GetCorrectedLodLevel(newLodLevel); }
unsigned StaticModelGroup::GetNumOccluderTriangles() { // Make sure instance transforms are up-to-date GetWorldBoundingBox(); unsigned triangles = 0; for (unsigned i = 0; i < batches_.Size(); ++i) { Geometry* geometry = GetLodGeometry(i, occlusionLodLevel_); if (!geometry) continue; // Check that the material is suitable for occlusion (default material always is) Material* mat = batches_[i].material_; if (mat && !mat->GetOcclusion()) continue; triangles += numWorldTransforms_ * geometry->GetIndexCount() / 3; } return triangles; }
/// Gets the width of the sprite, in pixels. /// @return The width of the sprite. float Sprite::GetWidthInPixels() const { MATH::FloatRectangle bounding_box = GetWorldBoundingBox(); float width = bounding_box.GetWidth(); return width; }
/// Updates the entire overworld for the elapsed time. /// @param[in] elapsed_time - The elapsed time for which to update the world. /// @param[in,out] input_controller - The controller supplying player input. /// @param[in,out] camera - The camera defining the viewable region of the overworld. /// @param[out] message_for_text_box - The message to display in the main text box, /// if one needs to start being displayed; empty if no new message needs to be displayed. void GameplayState::UpdateOverworld( const sf::Time& elapsed_time, INPUT_CONTROL::KeyboardInputController& input_controller, GRAPHICS::Camera& camera, std::string& message_for_text_box) { // INDICATE THAT NO MESSAGE NEEDS TO BE DISPLAYED YET IN THE TEXT BOX. message_for_text_box.clear(); MATH::FloatRectangle camera_bounds = camera.ViewBounds; MATH::Vector2f camera_view_center = camera_bounds.GetCenterPosition(); MAPS::TileMap* current_tile_map = Overworld->GetTileMap(camera_view_center.X, camera_view_center.Y); assert(current_tile_map); // CHECK IF THE PRIMARY ACTION BUTTON WAS PRESSED. if (input_controller.ButtonDown(INPUT_CONTROL::KeyboardInputController::PRIMARY_ACTION_KEY)) { // SWING THE PLAYER'S AXE. // A new axe swing may not be created if the player's // axe is already being swung. std::shared_ptr<EVENTS::AxeSwingEvent> axe_swing = Overworld->NoahPlayer->SwingAxe(); bool axe_swing_occurred = (nullptr != axe_swing); if (axe_swing_occurred) { // Allow the axe to collide with other objects. Overworld->AxeSwings.push_back(axe_swing); } } // CHECK IF THE PLAYER IS ALLOWED TO MOVE. // Noah can't move while the axe is swinging. // Movement is prevented to have the axe's position remain attached to Noah. // We need to keep track if Noah moved this frame so that we can stop any walking animations for him if he didn't move. bool noah_moved_this_frame = false; bool axe_is_swinging = (nullptr != Overworld->NoahPlayer->Inventory->Axe) && Overworld->NoahPlayer->Inventory->Axe->IsSwinging(); bool player_movement_allowed = (!axe_is_swinging); if (player_movement_allowed) { // MOVE NOAH IN RESPONSE TO USER INPUT. const float PLAYER_POSITION_ADJUSTMENT_FOR_SCROLLING_IN_PIXELS = 8.0f; MATH::Vector2f old_noah_position = Overworld->NoahPlayer->GetWorldPosition(); if (input_controller.ButtonDown(INPUT_CONTROL::KeyboardInputController::UP_KEY)) { // TRACK NOAH AS MOVING THIS FRAME. noah_moved_this_frame = true; // START NOAH WALKING UP. Overworld->NoahPlayer->BeginWalking(CORE::Direction::UP, OBJECTS::Noah::WALK_BACK_ANIMATION_NAME); // MOVE NOAH WHILE HANDLING COLLISIONS. MATH::Vector2f new_position = COLLISION::CollisionDetectionAlgorithms::MoveObject( Overworld->NoahPlayer->GetWorldBoundingBox(), CORE::Direction::UP, OBJECTS::Noah::MOVE_SPEED_IN_PIXELS_PER_SECOND, elapsed_time, *Overworld); Overworld->NoahPlayer->SetWorldPosition(new_position); // CHECK IF NOAH MOVED OUT OF THE CAMERA'S VIEW. MATH::FloatRectangle noah_world_bounding_box = Overworld->NoahPlayer->GetWorldBoundingBox(); float camera_top_y_position = camera_bounds.GetTopYPosition(); bool player_moved_out_of_view = (noah_world_bounding_box.GetTopYPosition() < camera_top_y_position); if (player_moved_out_of_view) { // CHECK IF A TOP TILE MAP EXISTS FOR NOAH TO MOVE TO. unsigned int top_tile_map_row_index = current_tile_map->OverworldRowIndex - 1; unsigned int top_tile_map_column_index = current_tile_map->OverworldColumnIndex; MAPS::TileMap* top_tile_map = Overworld->GetTileMap( top_tile_map_row_index, top_tile_map_column_index); bool top_tile_map_exists = (nullptr != top_tile_map); if (top_tile_map_exists) { // MOVE NOAH A FEW MORE PIXELS UP SO THAT HE WILL BE MORE VISIBLE ON THE NEW MAP. MATH::Vector2f noah_world_position = Overworld->NoahPlayer->GetWorldPosition(); noah_world_position.Y -= PLAYER_POSITION_ADJUSTMENT_FOR_SCROLLING_IN_PIXELS; Overworld->NoahPlayer->SetWorldPosition(noah_world_position); // START SCROLLING TO THE TOP TILE MAP. MATH::Vector2f scroll_start_position = current_tile_map->GetCenterWorldPosition(); MATH::Vector2f scroll_end_position = top_tile_map->GetCenterWorldPosition(); camera.StartScrolling(scroll_start_position, scroll_end_position); // DISABLE PLAYER MOVEMENT WHILE SCROLLING IS OCCURRING. input_controller.DisableInput(); } else { // KEEP NOAH WITHIN THE BOUNDS OF THE CURRENT TILE MAP. // Since there is no top tile map to scroll to, this will keep Noah on-screen. MATH::FloatRectangle tile_map_bounding_box = current_tile_map->GetWorldBoundingBox(); float tile_map_top_boundary = tile_map_bounding_box.GetTopYPosition(); // To keep Noah completely on screen, his center position should be half // his height below the top tile map boundary. MATH::Vector2f noah_world_position = old_noah_position; float noah_half_height = noah_world_bounding_box.GetHeight() / 2.0f; noah_world_position.Y = tile_map_top_boundary + noah_half_height; Overworld->NoahPlayer->SetWorldPosition(noah_world_position); } } } if (input_controller.ButtonDown(INPUT_CONTROL::KeyboardInputController::DOWN_KEY)) { // TRACK NOAH AS MOVING THIS FRAME. noah_moved_this_frame = true; // START NOAH WALKING DOWN. Overworld->NoahPlayer->BeginWalking(CORE::Direction::DOWN, OBJECTS::Noah::WALK_FRONT_ANIMATION_NAME); // MOVE NOAH WHILE HANDLING COLLISIONS. MATH::Vector2f new_position = COLLISION::CollisionDetectionAlgorithms::MoveObject( Overworld->NoahPlayer->GetWorldBoundingBox(), CORE::Direction::DOWN, OBJECTS::Noah::MOVE_SPEED_IN_PIXELS_PER_SECOND, elapsed_time, *Overworld); Overworld->NoahPlayer->SetWorldPosition(new_position); // CHECK IF NOAH MOVED OUT OF THE CAMERA'S VIEW. MATH::FloatRectangle noah_world_bounding_box = Overworld->NoahPlayer->GetWorldBoundingBox(); float camera_bottom_y_position = camera_bounds.GetBottomYPosition(); bool player_moved_out_of_view = (noah_world_bounding_box.GetBottomYPosition() > camera_bottom_y_position); if (player_moved_out_of_view) { // CHECK IF A BOTTOM TILE MAP EXISTS FOR NOAH TO MOVE TO. unsigned int bottom_tile_map_row_index = current_tile_map->OverworldRowIndex + 1; unsigned int bottom_tile_map_column_index = current_tile_map->OverworldColumnIndex; MAPS::TileMap* bottom_tile_map = Overworld->GetTileMap( bottom_tile_map_row_index, bottom_tile_map_column_index); bool bottom_tile_map_exists = (nullptr != bottom_tile_map); if (bottom_tile_map_exists) { // MOVE NOAH A FEW MORE PIXELS DOWN SO THAT HE WILL BE MORE VISIBLE ON THE NEW MAP. MATH::Vector2f noah_world_position = Overworld->NoahPlayer->GetWorldPosition(); noah_world_position.Y += PLAYER_POSITION_ADJUSTMENT_FOR_SCROLLING_IN_PIXELS; Overworld->NoahPlayer->SetWorldPosition(noah_world_position); // START SCROLLING TO THE BOTTOM TILE MAP. MATH::Vector2f scroll_start_position = current_tile_map->GetCenterWorldPosition(); MATH::Vector2f scroll_end_position = bottom_tile_map->GetCenterWorldPosition(); camera.StartScrolling(scroll_start_position, scroll_end_position); // DISABLE PLAYER MOVEMENT WHILE SCROLLING IS OCCURRING. input_controller.DisableInput(); } else { // KEEP NOAH WITHIN THE BOUNDS OF THE CURRENT TILE MAP. // Since there is no bottom tile map to scroll to, this will keep Noah on-screen. MATH::FloatRectangle tile_map_bounding_box = current_tile_map->GetWorldBoundingBox(); float tile_map_bottom_boundary = tile_map_bounding_box.GetBottomYPosition(); // To keep Noah completely on screen, his center position should be half // his height above the bottom tile map boundary. MATH::Vector2f noah_world_position = old_noah_position; float noah_half_height = Overworld->NoahPlayer->GetWorldBoundingBox().GetHeight() / 2.0f; noah_world_position.Y = tile_map_bottom_boundary - noah_half_height; Overworld->NoahPlayer->SetWorldPosition(noah_world_position); } } } if (input_controller.ButtonDown(INPUT_CONTROL::KeyboardInputController::LEFT_KEY)) { // TRACK NOAH AS MOVING THIS FRAME. noah_moved_this_frame = true; // START NOAH WALKING LEFT. Overworld->NoahPlayer->BeginWalking(CORE::Direction::LEFT, OBJECTS::Noah::WALK_LEFT_ANIMATION_NAME); // MOVE NOAH WHILE HANDLING COLLISIONS. MATH::Vector2f new_position = COLLISION::CollisionDetectionAlgorithms::MoveObject( Overworld->NoahPlayer->GetWorldBoundingBox(), CORE::Direction::LEFT, OBJECTS::Noah::MOVE_SPEED_IN_PIXELS_PER_SECOND, elapsed_time, *Overworld); Overworld->NoahPlayer->SetWorldPosition(new_position); // CHECK IF NOAH MOVED OUT OF THE CAMERA'S VIEW. MATH::FloatRectangle noah_world_bounding_box = Overworld->NoahPlayer->GetWorldBoundingBox(); float camera_left_x_position = camera_bounds.GetLeftXPosition(); bool player_moved_out_of_view = (noah_world_bounding_box.GetLeftXPosition() < camera_left_x_position); if (player_moved_out_of_view) { // CHECK IF A LEFT TILE MAP EXISTS FOR NOAH TO MOVE TO. unsigned int left_tile_map_row_index = current_tile_map->OverworldRowIndex; unsigned int left_tile_map_column_index = current_tile_map->OverworldColumnIndex - 1; MAPS::TileMap* left_tile_map = Overworld->GetTileMap( left_tile_map_row_index, left_tile_map_column_index); bool left_tile_map_exists = (nullptr != left_tile_map); if (left_tile_map_exists) { // MOVE NOAH A FEW MORE PIXELS LEFT SO THAT HE WILL BE MORE VISIBLE ON THE NEW MAP. MATH::Vector2f noah_world_position = Overworld->NoahPlayer->GetWorldPosition(); noah_world_position.X -= PLAYER_POSITION_ADJUSTMENT_FOR_SCROLLING_IN_PIXELS; Overworld->NoahPlayer->SetWorldPosition(noah_world_position); // START SCROLLING TO THE LEFT TILE MAP. MATH::Vector2f scroll_start_position = current_tile_map->GetCenterWorldPosition(); MATH::Vector2f scroll_end_position = left_tile_map->GetCenterWorldPosition(); camera.StartScrolling(scroll_start_position, scroll_end_position); // DISABLE PLAYER MOVEMENT WHILE SCROLLING IS OCCURRING. input_controller.DisableInput(); } else { // KEEP NOAH WITHIN THE BOUNDS OF THE CURRENT TILE MAP. // Since there is no left tile map to scroll to, this will keep Noah on-screen. MATH::FloatRectangle tile_map_bounding_box = current_tile_map->GetWorldBoundingBox(); float tile_map_left_boundary = tile_map_bounding_box.GetLeftXPosition(); // To keep Noah completely on screen, his center position should be half // his width to the right of the left tile map boundary. MATH::Vector2f noah_world_position = old_noah_position; float noah_half_width = Overworld->NoahPlayer->GetWorldBoundingBox().GetWidth() / 2.0f; noah_world_position.X = tile_map_left_boundary + noah_half_width; Overworld->NoahPlayer->SetWorldPosition(noah_world_position); } } } if (input_controller.ButtonDown(INPUT_CONTROL::KeyboardInputController::RIGHT_KEY)) { // TRACK NOAH AS MOVING THIS FRAME. noah_moved_this_frame = true; // START NOAH WALKING RIGHT. Overworld->NoahPlayer->BeginWalking(CORE::Direction::RIGHT, OBJECTS::Noah::WALK_RIGHT_ANIMATION_NAME); // MOVE NOAH WHILE HANDLING COLLISIONS. MATH::Vector2f new_position = COLLISION::CollisionDetectionAlgorithms::MoveObject( Overworld->NoahPlayer->GetWorldBoundingBox(), CORE::Direction::RIGHT, OBJECTS::Noah::MOVE_SPEED_IN_PIXELS_PER_SECOND, elapsed_time, *Overworld); Overworld->NoahPlayer->SetWorldPosition(new_position); // CHECK IF NOAH MOVED OUT OF THE CAMERA'S VIEW. MATH::FloatRectangle noah_world_bounding_box = Overworld->NoahPlayer->GetWorldBoundingBox(); float camera_right_x_position = camera_bounds.GetRightXPosition(); bool player_moved_out_of_view = (noah_world_bounding_box.GetRightXPosition() > camera_right_x_position); if (player_moved_out_of_view) { // CHECK IF A RIGHT TILE MAP EXISTS FOR NOAH TO MOVE TO. unsigned int right_tile_map_row_index = current_tile_map->OverworldRowIndex; unsigned int right_tile_map_column_index = current_tile_map->OverworldColumnIndex + 1; MAPS::TileMap* right_tile_map = Overworld->GetTileMap( right_tile_map_row_index, right_tile_map_column_index); bool right_tile_map_exists = (nullptr != right_tile_map); if (right_tile_map_exists) { // MOVE NOAH A FEW MORE PIXELS RIGHT SO THAT HE WILL BE MORE VISIBLE ON THE NEW MAP. MATH::Vector2f noah_world_position = Overworld->NoahPlayer->GetWorldPosition(); noah_world_position.X += PLAYER_POSITION_ADJUSTMENT_FOR_SCROLLING_IN_PIXELS; Overworld->NoahPlayer->SetWorldPosition(noah_world_position); // START SCROLLING TO THE RIGHT TILE MAP. MATH::Vector2f scroll_start_position = current_tile_map->GetCenterWorldPosition(); MATH::Vector2f scroll_end_position = right_tile_map->GetCenterWorldPosition(); camera.StartScrolling(scroll_start_position, scroll_end_position); // DISABLE PLAYER MOVEMENT WHILE SCROLLING IS OCCURRING. input_controller.DisableInput(); } else { // KEEP NOAH WITHIN THE BOUNDS OF THE CURRENT TILE MAP. // Since there is no right tile map to scroll to, this will keep Noah on-screen. MATH::FloatRectangle tile_map_bounding_box = current_tile_map->GetWorldBoundingBox(); float tile_map_right_boundary = tile_map_bounding_box.GetRightXPosition(); // To keep Noah completely on screen, his center position should be half // his width to the left of the right tile map boundary. MATH::Vector2f noah_world_position = old_noah_position; float noah_half_width = Overworld->NoahPlayer->GetWorldBoundingBox().GetWidth() / 2.0f; noah_world_position.X = tile_map_right_boundary - noah_half_width; Overworld->NoahPlayer->SetWorldPosition(noah_world_position); } } } } // CHECK IF NOAH MOVED THIS FRAME. if (noah_moved_this_frame) { // UPDATE NOAH'S ANIMATION. Overworld->NoahPlayer->Sprite.Update(elapsed_time); // BUILD A PIECE OF THE ARK IF NOAH STEPPED ONTO AN APPROPRIATE SPOT. MATH::Vector2f noah_world_position = Overworld->NoahPlayer->GetWorldPosition(); MAPS::TileMap* tile_map_underneath_noah = Overworld->GetTileMap(noah_world_position.X, noah_world_position.Y); assert(tile_map_underneath_noah); // An ark piece only needs to be built once. OBJECTS::ArkPiece* ark_piece = tile_map_underneath_noah->GetArkPieceAtWorldPosition(noah_world_position); bool ark_piece_can_be_built = (ark_piece && !ark_piece->Built); if (ark_piece_can_be_built) { /// @todo Check if Noah has wood. // BUILD THE ARK PIECE. ark_piece->Built = true; // When building an ark piece, a dust cloud should appear. std::shared_ptr<GRAPHICS::Texture> dust_cloud_texture = Assets->GetTexture(RESOURCES::DUST_CLOUD_TEXTURE_ID); assert(dust_cloud_texture); OBJECTS::DustCloud dust_cloud(dust_cloud_texture); // The dust cloud should be positioned over the ark piece. MATH::Vector2f dust_cloud_center_world_position = ark_piece->Sprite.GetWorldPosition(); dust_cloud.Sprite.SetWorldPosition(dust_cloud_center_world_position); // The dust cloud should start animating immediately. dust_cloud.Sprite.Play(); // The dust cloud needs to be added to the tile map so that it gets updated. tile_map_underneath_noah->DustClouds.push_back(dust_cloud); /// @todo Play sound effect when building ark piece. } } else { // STOP NOAH'S ANIMATION FROM PLAYING SINCE THE PLAYER DIDN'T MOVE THIS FRAME. Overworld->NoahPlayer->Sprite.ResetAnimation(); } // UPDATE THE CAMERA BASED ON SCROLLING. if (camera.IsScrolling) { // SCROLL BASED ON THE ELAPSED FRAME TIME. camera.Scroll(elapsed_time); // CHECK IF SCROLLING HAS FINISHED. bool scrolling_finished = !camera.IsScrolling; if (scrolling_finished) { // RE-ENABLE PLAYER INPUT. input_controller.EnableInput(); } } else { // POSITION THE CAMERA TO FOCUS ON THE CENTER OF THE CURRENT TILE MAP. MATH::Vector2f center_world_position = current_tile_map->GetCenterWorldPosition(); camera.SetCenter(center_world_position); } // HANDLE OTHER COLLISIONS. COLLISION::CollisionDetectionAlgorithms::HandleAxeSwings(*Overworld, Overworld->AxeSwings, *Assets); for (auto wood_logs = current_tile_map->WoodLogs.begin(); wood_logs != current_tile_map->WoodLogs.end();) { // CHECK IF THE WOOD LOGS INTERSECT WITH NOAH. MATH::FloatRectangle wood_log_bounding_box = wood_logs->GetWorldBoundingBox(); MATH::FloatRectangle noah_bounding_box = Overworld->NoahPlayer->GetWorldBoundingBox(); bool noah_collided_with_wood_logs = noah_bounding_box.Contains( wood_log_bounding_box.GetCenterXPosition(), wood_log_bounding_box.GetCenterYPosition()); if (noah_collided_with_wood_logs) { // ADD THE WOOD TO NOAH'S INVENTORY. // The logs can have a random amount of wood. unsigned int MIN_WOOD_COUNT = 1; unsigned int MAX_WOOD_COUNT = 3; unsigned int random_number_for_wood = RandomNumberGenerator(); unsigned int random_wood_count = (random_number_for_wood % MAX_WOOD_COUNT) + MIN_WOOD_COUNT; Overworld->NoahPlayer->Inventory->AddWood(random_wood_count); // REMOVE THE WOOD LOGS SINCE THEY'VE BEEN COLLECTED BY NOAH. wood_logs = current_tile_map->WoodLogs.erase(wood_logs); // SEE IF A BIBLE VERSE SHOULD BE COLLECTED ALONG WITH THE WOOD. // There should be a random chance that a Bible verse can be collected. const unsigned int EVENLY_DIVISIBLE = 0; const unsigned int BIBLE_VERSE_EXISTS_IF_DIVISIBLE_BY_THIS = 2; unsigned int random_number_for_bible_verse = RandomNumberGenerator(); bool bible_verse_exists_with_wood = ((random_number_for_bible_verse % BIBLE_VERSE_EXISTS_IF_DIVISIBLE_BY_THIS) == EVENLY_DIVISIBLE); if (bible_verse_exists_with_wood) { // CHECK IF ANY BIBLE VERSES REMAIN. // This check helps protect against a mod by 0 below. bool bible_verses_remain_to_be_found = !BibleVersesLeftToFind.empty(); if (bible_verses_remain_to_be_found) { // PLAY THE SOUND EFFECT FOR COLLECTING A BIBLE VERSE. std::shared_ptr<AUDIO::SoundEffect> collected_bible_verse_sound = Assets->GetSound(RESOURCES::COLLECT_BIBLE_VERSE_SOUND_ID); bool collect_bible_verse_sound_loaded = (nullptr != collected_bible_verse_sound); if (collect_bible_verse_sound_loaded) { collected_bible_verse_sound->Play(); } // SELECT A RANDOM BIBLE VERSE. unsigned int random_bible_verse_index = RandomNumberGenerator() % BibleVersesLeftToFind.size(); auto bible_verse = BibleVersesLeftToFind.begin() + random_bible_verse_index; // ADD THE BIBLE VERSE TO THE PLAYER'S INVENTORY. Overworld->NoahPlayer->Inventory->BibleVerses.insert(*bible_verse); // POPULATE THE MESSAGE TO DISPLAY IN THE MAIN TEXT BOX. message_for_text_box = "You got a Bible verse!\n" + bible_verse->ToString(); // REMOVE THE VERSE SINCE IT HAS BEEN FOUND. BibleVersesLeftToFind.erase(bible_verse); } } } else { // MOVE TO CHECKING COLLISIONS WITH THE NEXT SET OF WOOD LOGS. ++wood_logs; } } }
void Drawable::DrawDebugGeometry(DebugRenderer* debug, bool depthTest) { if (debug && IsEnabledEffective()) debug->AddBoundingBox(GetWorldBoundingBox(), Color::GREEN, depthTest); }
/// Gets the height of the sprite, in pixels. /// @return The height of the sprite. float Sprite::GetHeightInPixels() const { MATH::FloatRectangle bounding_box = GetWorldBoundingBox(); float height = bounding_box.GetHeight(); return height; }