#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { // The visible ground plane is at y = -1 constexpr float GroundHeight = -1.0f; constexpr float GroundHalfExtent = 25.0f; ModelDoc::LoadOptions StaticModelOptions() { ModelDoc::LoadOptions options{}; options.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode; options.loadMaterials = true; options.loadSkeleton = false; options.loadAnimations = false; return options; } void CreateSun(Scene& scene) { // Directional lights shine down their local -Z: tilt it 50 degrees down, then turn it around the up axis GameObject* sunObj = scene.CreateGameObject("Sun"); sunObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 10.f, 0.f)); sunObj->GetTransform().SetWorldRotation( glm::angleAxis(glm::radians(35.f), glm::vec3{0.f, 1.f, 0.f}) * glm::angleAxis(glm::radians(-50.f), glm::vec3{1.f, 0.f, 0.f})); auto* sun = sunObj->AddComponent(LightType::Directional); sun->SetColor(glm::vec3{1.0f, 0.95f, 0.85f}); sun->SetIntensity(3.0f); sun->SetCastShadows(true); } void CreateHelmetSpotlight(Scene& scene) { const glm::vec3 position{2.0f, 3.5f, -2.5f}; const glm::vec3 target{2.0f, GroundHeight, 0.0f}; GameObject* spotObj = scene.CreateGameObject("HelmetSpotlight"); spotObj->GetTransform().SetWorldPosition(position); spotObj->GetTransform().SetWorldRotation(glm::quatLookAt(glm::normalize(target - position), glm::vec3{0.0f, 1.0f, 0.0f})); auto* spot = spotObj->AddComponent(LightType::Spot); spot->SetColor(glm::vec3{1.0f, 0.85f, 0.7f}); spot->SetIntensity(25.0f); spot->SetRange(12.0f); spot->SetConeAngles(glm::radians(20.0f), glm::radians(30.0f)); spot->SetCastShadows(true); } void CreateGround(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources) { const auto planeModel = AssetManager::GetInstance().LoadModel("game://plane.glb", StaticModelOptions()); ModelDocUtils::LogModelDocSummary(planeModel, "plane.glb"); const auto& planePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(planeModel, "game://plane.glb"); const auto planeTexturePath = ModelDocUtils::PickTexturePath( planeModel, planePrimitive, AssetFS::GetInstance().GetCookedPathForFile("engine://grass.jpg")); const MaterialID planeMaterialID = resources.materials().addMaterial({ .baseColor = ModelDocUtils::GetImportedBaseColor(planeModel, planePrimitive), .textureFilteringMode = TextureFilteringMode::Nearest, .diffuseTexture = resources.loadImageFromFile(gfxDevice, planeTexturePath), .name = ModelDocUtils::GetImportedMaterialName(planeModel, planePrimitive, "GroundPlaneMaterial"), }); GameObject* planeObj = scene.CreateGameObject("GroundPlane"); auto* planeMeshComp = planeObj->AddComponent(); planeMeshComp->SetMeshID(resources.meshes().addMesh(gfxDevice, planePrimitive.mesh)); planeMeshComp->SetMaterialID(planeMaterialID); planeObj->GetTransform().SetWorldPosition(glm::vec3(0.f, GroundHeight, 0.f)); planeObj->GetTransform().SetWorldScale(glm::vec3(GroundHalfExtent, 1.f, GroundHalfExtent)); // The plane mesh is flat, so the floor collider sits right below it GameObject* groundObj = scene.CreateGameObject("GroundCollider"); groundObj->GetTransform().SetWorldPosition(glm::vec3(0.f, GroundHeight - 0.5f, 0.f)); groundObj->AddComponent(glm::vec3{GroundHalfExtent, 0.5f, GroundHalfExtent}); groundObj->AddComponent()->SetType(RigidbodyType::Static); scene.GetPhysics().RegisterGameObject(*groundObj); } // Kenney prototype textures, texture_01 is a 1 x 1 meter grid MaterialID AddPrototypeMaterial(GfxDevice& gfxDevice, RenderResources& resources, const std::string& color, float roughness) { Material material = Material::SimpleColor(glm::vec3{1.0f}, "Prototype" + color); material.roughnessFactor = roughness; material.textureFilteringMode = TextureFilteringMode::Anisotropic; material.diffuseTexture = resources.loadImageFromFile( gfxDevice, AssetFS::GetInstance().GetFullPath("engine://kenny/" + color + "/texture_01.png")); return resources.materials().addMaterial(std::move(material)); } GameObject* CreateBox(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources, const std::string& name, const glm::vec3& position, const glm::vec3& size, MaterialID materialID, RigidbodyType type, const glm::quat& rotation = glm::quat{1.0f, 0.0f, 0.0f, 0.0f}) { GameObject* box = scene.CreateGameObject(name); box->GetTransform().SetWorldPosition(position); box->GetTransform().SetWorldRotation(rotation); auto* boxMeshComp = box->AddComponent(); boxMeshComp->SetMeshID(resources.meshes().addMesh(gfxDevice, PrimitiveMeshes::MakeBox(size))); boxMeshComp->SetMaterialID(materialID); box->AddComponent(size * 0.5f); auto* boxRb = box->AddComponent(); boxRb->SetType(type); boxRb->SetMass(10.0f); scene.GetPhysics().RegisterGameObject(*box); return box; } // Every platform is reachable with a jump from the previous one void CreateParkourCourse(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources) { const MaterialID platformMaterial = AddPrototypeMaterial(gfxDevice, resources, "Orange", 0.6f); const MaterialID goalMaterial = AddPrototypeMaterial(gfxDevice, resources, "Green", 0.6f); const MaterialID wallMaterial = AddPrototypeMaterial(gfxDevice, resources, "Dark", 0.9f); const MaterialID crateMaterial = AddPrototypeMaterial(gfxDevice, resources, "Purple", 0.35f); // Platforms stand on the ground and are given by their top height const auto createPlatform = [&](const std::string& name, const glm::vec2& center, float top, const glm::vec2& footprint, MaterialID materialID) { const float height = top - GroundHeight; CreateBox(scene, gfxDevice, resources, name, glm::vec3{center.x, top - height * 0.5f, center.y}, glm::vec3{footprint.x, height, footprint.y}, materialID, RigidbodyType::Static); }; createPlatform("Parkour_Step", {0.0f, 4.0f}, -0.5f, {2.0f, 2.0f}, platformMaterial); createPlatform("Parkour_Block", {0.0f, 7.0f}, 0.4f, {2.0f, 2.0f}, platformMaterial); createPlatform("Parkour_Pillar1", {3.5f, 8.5f}, 1.2f, {1.5f, 1.5f}, platformMaterial); createPlatform("Parkour_Pillar2", {6.5f, 7.5f}, 2.0f, {1.2f, 1.2f}, platformMaterial); CreateBox(scene, gfxDevice, resources, "Parkour_Beam", {6.5f, 1.85f, 4.0f}, {0.6f, 0.3f, 5.0f}, platformMaterial, RigidbodyType::Static); createPlatform("Parkour_Goal", {6.5f, -0.5f}, 2.8f, {2.5f, 2.5f}, goalMaterial); CreateBox(scene, gfxDevice, resources, "Wall", {-5.0f, 0.5f, 0.0f}, {0.5f, 3.0f, 8.0f}, wallMaterial, RigidbodyType::Static); CreateBox(scene, gfxDevice, resources, "LowWall", {-2.5f, -0.6f, -4.0f}, {4.0f, 0.8f, 0.4f}, wallMaterial, RigidbodyType::Static); for (int i = 0; i < 4; ++i) { CreateBox(scene, gfxDevice, resources, "Crate" + std::to_string(i), {-2.5f, -0.6f + 0.81f * static_cast(i), 2.0f}, glm::vec3{0.8f}, crateMaterial, RigidbodyType::Dynamic); } } GameObject* CreateDoorway(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources) { const MaterialID frameMaterial = AddPrototypeMaterial(gfxDevice, resources, "Dark", 0.9f); const MaterialID doorMaterial = AddPrototypeMaterial(gfxDevice, resources, "Purple", 0.5f); constexpr float x = 4.0f; constexpr float z = -4.5f; constexpr float frameHeight = 2.8f; constexpr float doorWidth = 1.6f; constexpr float doorHeight = 2.4f; for (const float side : {-1.0f, 1.0f}) { CreateBox(scene, gfxDevice, resources, side < 0.0f ? "DoorFrame_Left" : "DoorFrame_Right", {x, GroundHeight + frameHeight * 0.5f, z + side * (doorWidth * 0.5f + 0.2f)}, {0.4f, frameHeight, 0.4f}, frameMaterial, RigidbodyType::Static); } CreateBox(scene, gfxDevice, resources, "DoorFrame_Top", {x, GroundHeight + frameHeight + 0.2f, z}, {0.4f, 0.4f, doorWidth + 0.8f}, frameMaterial, RigidbodyType::Static); GameObject* door = CreateBox(scene, gfxDevice, resources, "Door", {x, GroundHeight + doorHeight * 0.5f, z}, {0.3f, doorHeight, doorWidth}, doorMaterial, RigidbodyType::Kinematic); door->AddComponent()->SetOpenOffset({0.0f, 0.0f, -doorWidth - 0.1f}); return door; } void CreateButton(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources, const GameObject& target) { const MaterialID pedestalMaterial = AddPrototypeMaterial(gfxDevice, resources, "Dark", 0.9f); CreateBox(scene, gfxDevice, resources, "ButtonPedestal", {1.5f, -0.45f, -4.5f}, {0.4f, 1.1f, 0.4f}, pedestalMaterial, RigidbodyType::Static); // Every button needs its own material, pressing it recolors the material Material buttonMaterial = Material::SimpleColor(glm::vec3{1.0f}, "ButtonMaterial"); buttonMaterial.roughnessFactor = 0.4f; const MaterialID buttonMaterialID = resources.materials().addMaterial(std::move(buttonMaterial)); GameObject* button = CreateBox(scene, gfxDevice, resources, "Button", {1.5f, 0.15f, -4.5f}, {0.25f, 0.1f, 0.25f}, buttonMaterialID, RigidbodyType::Static); button->AddComponent()->AddTarget(target); } void CreateMovingPlatform(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources) { const MaterialID rampMaterial = AddPrototypeMaterial(gfxDevice, resources, "Green", 0.6f); const MaterialID platformMaterial = AddPrototypeMaterial(gfxDevice, resources, "Purple", 0.5f); constexpr float laneZ = 2.0f; constexpr float platformHeight = 1.0f; constexpr float rampStartX = 9.0f; constexpr float rampLength = 3.0f; constexpr float rampThickness = 0.3f; constexpr float travelDistance = 8.0f; const glm::vec3 platformSize{3.0f, 0.3f, 3.0f}; const float platformTop = GroundHeight + platformHeight; const glm::quat rampRotation = glm::angleAxis(std::atan2(platformHeight, rampLength), glm::vec3{0.0f, 0.0f, 1.0f}); const glm::vec3 rampTopCenter{rampStartX + rampLength * 0.5f, GroundHeight + platformHeight * 0.5f, laneZ}; const glm::vec3 rampCenter = rampTopCenter - rampRotation * glm::vec3{0.0f, rampThickness * 0.5f, 0.0f}; const float rampSlopeLength = glm::length(glm::vec2{rampLength, platformHeight}); CreateBox(scene, gfxDevice, resources, "PlatformRamp", rampCenter, {rampSlopeLength, rampThickness, platformSize.z}, rampMaterial, RigidbodyType::Static, rampRotation); const float platformStartX = rampStartX + rampLength + platformSize.x * 0.5f; GameObject* platform = CreateBox(scene, gfxDevice, resources, "MovingPlatform", {platformStartX, platformTop - platformSize.y * 0.5f, laneZ}, platformSize, platformMaterial, RigidbodyType::Kinematic); platform->AddComponent()->SetTravelOffset({travelDistance, 0.0f, 0.0f}); const float landingStartX = platformStartX + travelDistance + platformSize.x * 0.5f; CreateBox(scene, gfxDevice, resources, "PlatformLanding", {landingStartX + 1.0f, GroundHeight + platformHeight * 0.5f, laneZ}, {2.0f, platformHeight, platformSize.z}, rampMaterial, RigidbodyType::Static); } struct WallOpening { float offset; float width; float bottom; float top; }; std::vector CreateWall(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources, const std::string& name, const glm::vec3& floorCenter, float length, float height, bool alongX, MaterialID materialID, const std::optional& opening = std::nullopt) { constexpr float thickness = 0.3f; std::vector pieces; const auto createPiece = [&](const std::string& suffix, float from, float to, float bottom, float top) { if (to <= from || top <= bottom) { return; } const float along = (from + to) * 0.5f; const glm::vec3 alongOffset = alongX ? glm::vec3{along, 0.0f, 0.0f} : glm::vec3{0.0f, 0.0f, along}; const glm::vec3 center = floorCenter + alongOffset + glm::vec3{0.0f, (bottom + top) * 0.5f, 0.0f}; const glm::vec3 size = alongX ? glm::vec3{to - from, top - bottom, thickness} : glm::vec3{thickness, top - bottom, to - from}; pieces.push_back( CreateBox(scene, gfxDevice, resources, name + suffix, center, size, materialID, RigidbodyType::Static)); }; const float halfLength = length * 0.5f; if (!opening) { createPiece("", -halfLength, halfLength, 0.0f, height); return pieces; } const float openingStart = opening->offset - opening->width * 0.5f; const float openingEnd = opening->offset + opening->width * 0.5f; createPiece("_Left", -halfLength, openingStart, 0.0f, height); createPiece("_Right", openingEnd, halfLength, 0.0f, height); createPiece("_Below", openingStart, openingEnd, 0.0f, opening->bottom); createPiece("_Above", openingStart, openingEnd, opening->top, height); return pieces; } GameObject* CreateCeilingLight(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources, const glm::vec3& position) { GameObject* lightObj = scene.CreateGameObject("CeilingLight"); lightObj->GetTransform().SetWorldPosition(position); // Every bulb needs its own material, the toggle changes its glow Material bulbMaterial = Material::SimpleColor(glm::vec3{1.0f, 0.95f, 0.85f}, "BulbMaterial"); auto* bulbMesh = lightObj->AddComponent(); bulbMesh->SetMeshID(resources.meshes().addMesh(gfxDevice, PrimitiveMeshes::MakeBox({0.35f, 0.12f, 0.35f}))); bulbMesh->SetMaterialID(resources.materials().addMaterial(std::move(bulbMaterial))); auto* light = lightObj->AddComponent(LightType::Point); light->SetColor(glm::vec3{1.0f, 0.82f, 0.55f}); light->SetIntensity(4.0f); light->SetRange(7.0f); lightObj->AddComponent(); return lightObj; } void CreateHouse(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources) { const MaterialID wallMaterial = AddPrototypeMaterial(gfxDevice, resources, "Light", 0.8f); const MaterialID floorMaterial = AddPrototypeMaterial(gfxDevice, resources, "Orange", 0.7f); const MaterialID roofMaterial = AddPrototypeMaterial(gfxDevice, resources, "Red", 0.8f); const glm::vec3 center{0.0f, GroundHeight, -16.0f}; constexpr float width = 8.0f; constexpr float depth = 6.0f; constexpr float wallHeight = 3.0f; constexpr float doorOffset = -1.5f; constexpr float doorWidth = 1.2f; GameObject* house = scene.CreateGameObject("House"); house->GetTransform().SetWorldPosition(center); const auto addToHouse = [&](GameObject* object) { object->GetTransform().SetParent(&house->GetTransform(), true); }; const auto addWallToHouse = [&](const std::vector& pieces) { for (GameObject* piece : pieces) { addToHouse(piece); } }; addToHouse(CreateBox(scene, gfxDevice, resources, "House_Floor", center + glm::vec3{0.0f, 0.03f, 0.0f}, {width, 0.06f, depth}, floorMaterial, RigidbodyType::Static)); addWallToHouse(CreateWall(scene, gfxDevice, resources, "House_FrontWall", center + glm::vec3{0.0f, 0.0f, depth * 0.5f}, width + 0.3f, wallHeight, true, wallMaterial, WallOpening{.offset = doorOffset, .width = doorWidth, .bottom = 0.0f, .top = 2.2f})); addWallToHouse(CreateWall(scene, gfxDevice, resources, "House_BackWall", center - glm::vec3{0.0f, 0.0f, depth * 0.5f}, width + 0.3f, wallHeight, true, wallMaterial, WallOpening{.offset = 1.5f, .width = 1.4f, .bottom = 1.0f, .top = 2.0f})); addWallToHouse(CreateWall(scene, gfxDevice, resources, "House_LeftWall", center - glm::vec3{width * 0.5f, 0.0f, 0.0f}, depth - 0.3f, wallHeight, false, wallMaterial)); addWallToHouse(CreateWall(scene, gfxDevice, resources, "House_RightWall", center + glm::vec3{width * 0.5f, 0.0f, 0.0f}, depth - 0.3f, wallHeight, false, wallMaterial, WallOpening{.offset = 0.0f, .width = 1.6f, .bottom = 1.0f, .top = 2.0f})); addToHouse(CreateBox(scene, gfxDevice, resources, "House_Roof", center + glm::vec3{0.0f, wallHeight + 0.15f, 0.0f}, {width + 0.8f, 0.3f, depth + 0.8f}, roofMaterial, RigidbodyType::Static)); GameObject* ceilingLight = CreateCeilingLight(scene, gfxDevice, resources, center + glm::vec3{0.0f, wallHeight - 0.06f, 0.0f}); addToHouse(ceilingLight); // Inside, next to the door Material switchMaterial = Material::SimpleColor(glm::vec3{0.9f}, "LightSwitchMaterial"); switchMaterial.roughnessFactor = 0.5f; const glm::vec3 switchPosition = center + glm::vec3{doorOffset + doorWidth * 0.5f + 0.4f, 1.3f, depth * 0.5f - 0.15f - 0.03f}; GameObject* lightSwitch = CreateBox(scene, gfxDevice, resources, "LightSwitch", switchPosition, {0.12f, 0.2f, 0.06f}, resources.materials().addMaterial(std::move(switchMaterial)), RigidbodyType::Static); auto* switchButton = lightSwitch->AddComponent(); switchButton->SetCycleColors(false); switchButton->AddTarget(*ceilingLight); addToHouse(lightSwitch); GameObject* sofa = ModelSpawner::SpawnStaticModel( scene, gfxDevice, resources, "game://sofa.glb", StaticModelOptions(), "Sofa" ); sofa->GetTransform().SetWorldPosition({-3.7, -1, -17.5}); sofa->GetTransform().SetWorldRotation({180, 0, 180}); sofa->GetTransform().SetWorldScale({3, 3, 3}); addToHouse(sofa); } void CreateFlightHelmet(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources) { GameObject* helmetObj = ModelSpawner::SpawnStaticModel( scene, gfxDevice, resources, "engine://flight-helmet/FlightHelmet.gltf", StaticModelOptions(), "FlightHelmet"); helmetObj->GetTransform().SetWorldPosition(glm::vec3(2.f, GroundHeight, 0.f)); helmetObj->GetTransform().SetWorldScale(glm::vec3(2.f)); } void CreateTree(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources) { constexpr float scale = 3.0f; GameObject* treeObj = ModelSpawner::SpawnStaticModel( scene, gfxDevice, resources, "engine://tree/base.obj", StaticModelOptions(), "Tree"); treeObj->GetTransform().SetWorldPosition(glm::vec3(-7.f, GroundHeight + scale, 7.f)); treeObj->GetTransform().SetWorldScale(glm::vec3(scale)); // The trunk is about 0.14 units thick and runs up through the foliage, colliders are in world units treeObj->AddComponent(0.15f * scale, 2.0f * scale); treeObj->AddComponent()->SetType(RigidbodyType::Static); scene.GetPhysics().RegisterGameObject(*treeObj); } void CreateCapybara(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources) { constexpr auto modelReference = "engine://cotw-capybara-male/source/capybara.fbx"; ModelDoc::LoadOptions options{}; options.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode; options.loadMaterials = true; options.loadSkeleton = true; options.loadAnimations = true; auto charModel = AssetManager::GetInstance().LoadModel(modelReference, options); ModelDocUtils::LogModelDocSummary(charModel, "capybara.fbx"); const auto& charPrimitive = ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow(charModel, modelReference); // The model stores an absolute texture path from another machine, so fall back to the shipped texture const auto charTexturePath = ModelDocUtils::PickTexturePath( charModel, charPrimitive, AssetFS::GetInstance().GetFullPath( "engine://cotw-capybara-male/textures/capybara_male_light_brown_dif.ddsc.DECA.RE.pngballs")); const MaterialID charMaterialID = resources.materials().addMaterial({ .baseColor = ModelDocUtils::GetImportedBaseColor(charModel, charPrimitive), .diffuseTexture = resources.loadImageFromFile(gfxDevice, charTexturePath), .name = ModelDocUtils::GetImportedMaterialName(charModel, charPrimitive, "CharacterMaterial"), }); GameObject* charObj = scene.CreateGameObject("Character"); auto* charMeshComp = charObj->AddComponent(); charMeshComp->SetMeshID(resources.meshes().addMesh(gfxDevice, charPrimitive.mesh)); charMeshComp->SetMaterialID(charMaterialID); auto* animator = charObj->AddComponent(); animator->setSkeleton(std::move(charModel.skeleton)); for (auto& clip : charModel.animations) { spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration); animator->addClip(std::make_shared(std::move(clip))); } animator->play("capybara_canter_fwd_01|capybara_canter_fwd_01|run"); charObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f)); charObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f); } // Logs everything that enters or leaves it, spheres from the "Spawn ball" button fall through it void CreateTriggerZone(Scene& scene) { GameObject* triggerObj = scene.CreateGameObject("TriggerBox"); triggerObj->AddComponent(glm::vec3{3.0f, 3.0f, 3.0f})->SetTrigger(true); triggerObj->AddComponent()->SetType(RigidbodyType::Static); triggerObj->AddComponent(); triggerObj->GetTransform().SetWorldPosition({0.0f, 0.0f, 0.0f}); } void CreatePlayer(Scene& scene, MainSceneObjects& objects) { GameObject* playerObj = scene.CreateGameObject("Player"); playerObj->GetTransform().SetWorldPosition(glm::vec3(0.0f, 0.0f, -6.0f)); playerObj->AddComponent(0.4f, 1.8f); playerObj->AddComponent(); // Facing the parkour course auto* controller = playerObj->AddComponent(); controller->SetYaw(glm::half_pi()); scene.GetPhysics().RegisterGameObject(*playerObj); objects.player = playerObj; objects.playerController = controller; } void LoadSpawnBallResources(GfxDevice& gfxDevice, RenderResources& resources, MainSceneObjects& objects) { const auto sphereModel = AssetManager::GetInstance().LoadModel("engine://sphere.fbx", StaticModelOptions()); ModelDocUtils::LogModelDocSummary(sphereModel, "sphere.fbx"); const auto& spherePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(sphereModel, "engine://sphere.fbx"); objects.sphereMesh = resources.meshes().addMesh(gfxDevice, spherePrimitive.mesh); objects.sphereMaterial = resources.materials().addSimpleColorMaterial({0.5f, 0.3f, 0.8f}, "Blue"); } } MainSceneObjects BuildMainScene(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources) { MainSceneObjects objects{}; scene.GetEnvironment().fogDensity = 0.01f; CreateSun(scene); CreateGround(scene, gfxDevice, resources); CreateParkourCourse(scene, gfxDevice, resources); CreateFlightHelmet(scene, gfxDevice, resources); CreateHelmetSpotlight(scene); CreateTree(scene, gfxDevice, resources); // CreateCapybara(scene, gfxDevice, resources); CreateTriggerZone(scene); const GameObject* door = CreateDoorway(scene, gfxDevice, resources); CreateButton(scene, gfxDevice, resources, *door); CreateHouse(scene, gfxDevice, resources); CreateMovingPlatform(scene, gfxDevice, resources); CreatePlayer(scene, objects); LoadSpawnBallResources(gfxDevice, resources, objects); return objects; }