Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ Tests/TestScripts/**/bin/
Tests/TestScripts/**/obj/
# Coral is a git submodule — its own .gitignore handles bin/ and obj/ internally.
# Listing Coral sub-paths here is redundant and interferes with submodule commits.
EditorFrontend/next-env.d.ts
*.wtex
*.wmat
*.wmesh
164 changes: 164 additions & 0 deletions Axiom/Assets/SceneFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <fstream>
#include <functional>
#include <sstream>
#include <array>
#include <unordered_map>
#include <unordered_set>

Expand Down Expand Up @@ -174,6 +175,64 @@ EditorTransformDetails DecomposeMatrix(const glm::mat4 &Matrix) {
};
}

glm::vec3 AbsVec3(const glm::vec3 &Value) {
return glm::vec3(std::abs(Value.x), std::abs(Value.y), std::abs(Value.z));
}

void ExpandBounds(const glm::vec3 &BoundsMin, const glm::vec3 &BoundsMax,
const glm::mat4 &Transform, glm::vec3 &OutMin,
glm::vec3 &OutMax, bool &HasBounds) {
const std::array<glm::vec3, 8> Corners = {
glm::vec3(BoundsMin.x, BoundsMin.y, BoundsMin.z),
glm::vec3(BoundsMax.x, BoundsMin.y, BoundsMin.z),
glm::vec3(BoundsMin.x, BoundsMax.y, BoundsMin.z),
glm::vec3(BoundsMax.x, BoundsMax.y, BoundsMin.z),
glm::vec3(BoundsMin.x, BoundsMin.y, BoundsMax.z),
glm::vec3(BoundsMax.x, BoundsMin.y, BoundsMax.z),
glm::vec3(BoundsMin.x, BoundsMax.y, BoundsMax.z),
glm::vec3(BoundsMax.x, BoundsMax.y, BoundsMax.z),
};

for (const glm::vec3 &Corner : Corners) {
const glm::vec3 WorldCorner = glm::vec3(Transform * glm::vec4(Corner, 1.0f));
if (!HasBounds) {
OutMin = WorldCorner;
OutMax = WorldCorner;
HasBounds = true;
continue;
}
OutMin = glm::min(OutMin, WorldCorner);
OutMax = glm::max(OutMax, WorldCorner);
}
}

std::optional<EditorPhysicsProperties>
BuildDefaultStaticMeshPhysics(const MeshSceneData &SceneData,
const EditorTransformDetails &RootTransform) {
glm::vec3 CombinedMin(0.0f);
glm::vec3 CombinedMax(0.0f);
bool HasBounds = false;

for (const auto &Instance : SceneData.Instances) {
ExpandBounds(Instance.Mesh.BoundsMin, Instance.Mesh.BoundsMax,
Instance.Transform, CombinedMin, CombinedMax, HasBounds);
}

if (!HasBounds) {
return std::nullopt;
}

glm::vec3 HalfExtents = glm::max(glm::abs(CombinedMin), glm::abs(CombinedMax));
HalfExtents *= AbsVec3(RootTransform.Scale);
HalfExtents = glm::max(HalfExtents, glm::vec3(0.01f));

return EditorPhysicsProperties{
.BodyType = EditorPhysicsBodyType::Static,
.ColliderType = EditorPhysicsColliderType::Box,
.BoxHalfExtents = HalfExtents,
};
}

void ExpandMeshAssetIntoScene(EditorSceneState &State, std::string_view RootObjectId,
const MeshSceneData &SceneData,
std::string_view AssetPath) {
Expand All @@ -186,6 +245,11 @@ void ExpandMeshAssetIntoScene(EditorSceneState &State, std::string_view RootObje
RootDetails.IsGeneratedAssetChild = false;
RootDetails.GeneratedFromAssetRootId = std::nullopt;
RootDetails.AssetRelativePath = std::string(AssetPath);
if (!RootDetails.Physics.has_value()) {
const EditorTransformDetails RootTransform =
RootDetails.Transform.value_or(EditorTransformDetails{});
RootDetails.Physics = BuildDefaultStaticMeshPhysics(SceneData, RootTransform);
}

auto *RootItem = FindSceneItemMutable(State.Items, RootObjectId);
if (RootItem == nullptr) {
Expand Down Expand Up @@ -415,6 +479,26 @@ bool SaveSceneToFile(const std::filesystem::path &Path,
<< ",\"lightIntensity\":" << Details.Light->Intensity
<< ",\"lightDirection\":" << SerializeVec3(Details.Light->Direction);
}
if (Details.Physics.has_value()) {
Out << ",\"physicsBodyType\":"
<< EscStr(Details.Physics->BodyType == EditorPhysicsBodyType::Dynamic
? "dynamic"
: (Details.Physics->BodyType == EditorPhysicsBodyType::Static
? "static"
: "none"))
<< ",\"physicsColliderType\":"
<< EscStr(Details.Physics->ColliderType == EditorPhysicsColliderType::Sphere
? "sphere"
: (Details.Physics->ColliderType == EditorPhysicsColliderType::Box
? "box"
: "none"))
<< ",\"physicsBoxHalfExtents\":"
<< SerializeVec3(Details.Physics->BoxHalfExtents)
<< ",\"physicsSphereRadius\":" << Details.Physics->SphereRadius
<< ",\"physicsMass\":" << Details.Physics->Mass
<< ",\"physicsFriction\":" << Details.Physics->Friction
<< ",\"physicsRestitution\":" << Details.Physics->Restitution;
}
Out << "}";
}
Out << "\n ],\n";
Expand Down Expand Up @@ -646,6 +730,7 @@ LoadSceneFromFile(const std::filesystem::path &Path) {
std::string MaterialAssetPath;
std::string TextureAssetPath;
std::optional<EditorLightProperties> Light;
std::optional<EditorPhysicsProperties> Physics;
};

std::string MeshAsset;
Expand Down Expand Up @@ -745,6 +830,74 @@ LoadSceneFromFile(const std::filesystem::path &Path) {
if (V) { if (!Data.Light) Data.Light = EditorLightProperties{}; Data.Light->Direction = *V; }
return true;
}
if (K == "physicsBodyType") {
auto V = P.ParseString();
if (V) {
if (!Data.Physics) Data.Physics = EditorPhysicsProperties{};
if (*V == "static") {
Data.Physics->BodyType = EditorPhysicsBodyType::Static;
} else if (*V == "dynamic") {
Data.Physics->BodyType = EditorPhysicsBodyType::Dynamic;
} else {
Data.Physics->BodyType = EditorPhysicsBodyType::None;
}
}
return true;
}
if (K == "physicsColliderType") {
auto V = P.ParseString();
if (V) {
if (!Data.Physics) Data.Physics = EditorPhysicsProperties{};
if (*V == "box") {
Data.Physics->ColliderType = EditorPhysicsColliderType::Box;
} else if (*V == "sphere") {
Data.Physics->ColliderType = EditorPhysicsColliderType::Sphere;
} else {
Data.Physics->ColliderType = EditorPhysicsColliderType::None;
}
}
return true;
}
if (K == "physicsBoxHalfExtents") {
auto V = P.ParseVec3();
if (V) {
if (!Data.Physics) Data.Physics = EditorPhysicsProperties{};
Data.Physics->BoxHalfExtents = *V;
}
return true;
}
if (K == "physicsSphereRadius") {
auto V = P.ParseNumber();
if (V) {
if (!Data.Physics) Data.Physics = EditorPhysicsProperties{};
Data.Physics->SphereRadius = static_cast<float>(*V);
}
return true;
}
if (K == "physicsMass") {
auto V = P.ParseNumber();
if (V) {
if (!Data.Physics) Data.Physics = EditorPhysicsProperties{};
Data.Physics->Mass = static_cast<float>(*V);
}
return true;
}
if (K == "physicsFriction") {
auto V = P.ParseNumber();
if (V) {
if (!Data.Physics) Data.Physics = EditorPhysicsProperties{};
Data.Physics->Friction = static_cast<float>(*V);
}
return true;
}
if (K == "physicsRestitution") {
auto V = P.ParseNumber();
if (V) {
if (!Data.Physics) Data.Physics = EditorPhysicsProperties{};
Data.Physics->Restitution = static_cast<float>(*V);
}
return true;
}
return false;
});
if (!ObjId.empty()) Objects[ObjId] = std::move(Data);
Expand Down Expand Up @@ -817,6 +970,7 @@ LoadSceneFromFile(const std::filesystem::path &Path) {
Details.Transform = Data.Transform;
Details.ScriptClass = Data.ScriptClass;
Details.Light = Data.Light;
Details.Physics = Data.Physics;
Details.GeneratedFromAssetRootId = Data.GeneratedFromAssetRootId;
Details.AssetRelativePath = Data.AssetRelativePath;
State.ObjectDetailsById[Id] = std::move(Details);
Expand Down Expand Up @@ -927,6 +1081,16 @@ LoadSceneFromFile(const std::filesystem::path &Path) {
Transform = BuildTransformMatrix(T);
}

if (DetailsIt != State.ObjectDetailsById.end() &&
!DetailsIt->second.Physics.has_value()) {
const auto RootTransform =
DetailsIt->second.Transform.value_or(EditorTransformDetails{});
MeshSceneData SingleMeshScene;
SingleMeshScene.Instances.push_back(Instance);
DetailsIt->second.Physics =
BuildDefaultStaticMeshPhysics(SingleMeshScene, RootTransform);
}

State.MeshInstances.push_back({
.ObjectId = ObjId,
.Mesh = Instance.Mesh,
Expand Down
9 changes: 9 additions & 0 deletions Axiom/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ set(ENGINE_SOURCES
Core/Log.cpp
Core/VulkanLoader.cpp
Remote/AxiomSessionEndpoint.cpp
Physics/PhysicsWorld.cpp
Session/BufferedEditorInputSource.cpp
Session/EditorMessageBus.cpp
Session/EditorSceneRendererAdapter.cpp
Expand Down Expand Up @@ -293,6 +294,7 @@ target_include_directories(AxiomCore PUBLIC
"${CMAKE_SOURCE_DIR}/ThirdParty/vma"
"${CMAKE_SOURCE_DIR}/ThirdParty/volk"
"${CMAKE_SOURCE_DIR}/ThirdParty/spdlog/include"
"$<$<BOOL:${AXIOM_ENABLE_PHYSICS}>:${AXIOM_JOLT_SOURCE_DIR}>"
"${Vulkan_INCLUDE_DIRS}"
)

Expand All @@ -314,6 +316,13 @@ else()
target_compile_definitions(AxiomCore PUBLIC AXIOM_ENABLE_WEBRTC=0)
endif()

if(AXIOM_ENABLE_PHYSICS)
target_link_libraries(AxiomCore PUBLIC Jolt)
target_compile_definitions(AxiomCore PUBLIC AXIOM_ENABLE_PHYSICS=1)
else()
target_compile_definitions(AxiomCore PUBLIC AXIOM_ENABLE_PHYSICS=0)
endif()

if(AXIOM_ENABLE_SCRIPTING)
target_link_libraries(AxiomCore PUBLIC CoralNative)
target_compile_definitions(AxiomCore PUBLIC AXIOM_SCRIPTING_ENABLED=1)
Expand Down
Loading
Loading