536 lines
26 KiB
C++
536 lines
26 KiB
C++
#include <scenes/MainScene.h>
|
|
|
|
#include <destrum/Assets/AssetManager.h>
|
|
#include <destrum/Components/Animator.h>
|
|
#include <destrum/Components/LightComponent.h>
|
|
#include <destrum/Components/MeshRendererComponent.h>
|
|
#include <destrum/Components/Physics/BoxCollider.h>
|
|
#include <destrum/Components/Physics/CapsuleCollider.h>
|
|
#include <destrum/Components/Physics/Rigidbody.h>
|
|
#include <destrum/FS/AssetFS.h>
|
|
#include <destrum/Graphics/RenderResources.h>
|
|
#include <destrum/Graphics/Resources/PrimitiveMeshes.h>
|
|
#include <destrum/ObjectModel/GameObject.h>
|
|
#include <destrum/Scene/ModelSpawner.h>
|
|
#include <destrum/Scene/Scene.h>
|
|
#include <destrum/Util/ModelDocUtils.h>
|
|
|
|
#include <components/FirstPersonController.h>
|
|
#include <components/Door.h>
|
|
#include <components/InteractableButton.h>
|
|
#include <components/LightToggle.h>
|
|
#include <components/MovingPlatform.h>
|
|
#include <components/TriggerLoggerComponent.h>
|
|
|
|
#include <glm/gtc/constants.hpp>
|
|
#include <glm/gtc/quaternion.hpp>
|
|
|
|
#include <spdlog/spdlog.h>
|
|
|
|
#include <cmath>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
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<LightComponent>(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<LightComponent>(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<MeshRendererComponent>();
|
|
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<BoxCollider>(glm::vec3{GroundHalfExtent, 0.5f, GroundHalfExtent});
|
|
groundObj->AddComponent<Rigidbody>()->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<MeshRendererComponent>();
|
|
boxMeshComp->SetMeshID(resources.meshes().addMesh(gfxDevice, PrimitiveMeshes::MakeBox(size)));
|
|
boxMeshComp->SetMaterialID(materialID);
|
|
|
|
box->AddComponent<BoxCollider>(size * 0.5f);
|
|
auto* boxRb = box->AddComponent<Rigidbody>();
|
|
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<float>(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<Door>()->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<InteractableButton>()->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<MovingPlatform>()->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<GameObject*> 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<WallOpening>& opening = std::nullopt)
|
|
{
|
|
constexpr float thickness = 0.3f;
|
|
std::vector<GameObject*> 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<MeshRendererComponent>();
|
|
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<LightComponent>(LightType::Point);
|
|
light->SetColor(glm::vec3{1.0f, 0.82f, 0.55f});
|
|
light->SetIntensity(4.0f);
|
|
light->SetRange(7.0f);
|
|
|
|
lightObj->AddComponent<LightToggle>();
|
|
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<GameObject*>& 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<InteractableButton>();
|
|
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<CapsuleCollider>(0.15f * scale, 2.0f * scale);
|
|
treeObj->AddComponent<Rigidbody>()->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<MeshRendererComponent>();
|
|
charMeshComp->SetMeshID(resources.meshes().addMesh(gfxDevice, charPrimitive.mesh));
|
|
charMeshComp->SetMaterialID(charMaterialID);
|
|
|
|
auto* animator = charObj->AddComponent<Animator>();
|
|
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<SkeletalAnimation>(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<BoxCollider>(glm::vec3{3.0f, 3.0f, 3.0f})->SetTrigger(true);
|
|
triggerObj->AddComponent<Rigidbody>()->SetType(RigidbodyType::Static);
|
|
triggerObj->AddComponent<TriggerLoggerComponent>();
|
|
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<CapsuleCollider>(0.4f, 1.8f);
|
|
playerObj->AddComponent<Rigidbody>();
|
|
|
|
// Facing the parkour course
|
|
auto* controller = playerObj->AddComponent<FirstPersonController>();
|
|
controller->SetYaw(glm::half_pi<float>());
|
|
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;
|
|
}
|