diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index bf2b4cd7d..a9e24af60 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -109,8 +109,10 @@ set(CUBOS_ENGINE_SOURCE "src/physics/solver/plugin.cpp" "src/physics/constraints/penetration_constraint.cpp" "src/physics/constraints/distance_constraint.cpp" + "src/physics/constraints/spring_constraint.cpp" "src/physics/solver/penetration_constraint/plugin.cpp" "src/physics/solver/distance_constraint/plugin.cpp" + "src/physics/solver/spring_constraint/plugin.cpp" "src/physics/fixed_substep/plugin.cpp" "src/physics/fixed_substep/substeps.cpp" "src/physics/solver/integration/plugin.cpp" diff --git a/engine/include/cubos/engine/physics/constraints/spring_constraint.hpp b/engine/include/cubos/engine/physics/constraints/spring_constraint.hpp new file mode 100644 index 000000000..467efe40a --- /dev/null +++ b/engine/include/cubos/engine/physics/constraints/spring_constraint.hpp @@ -0,0 +1,27 @@ +/// @file +/// @brief Component @ref cubos::engine::SpringConstraint. +/// @ingroup physics-solver-plugin + +#pragma once + +#include + +#include + +#include +#include + +namespace cubos::engine +{ + /// @brief Relation which holds the information for a spring constraint between two bodies. + /// @ingroup physics-solver-plugin + struct SpringConstraint + { + CUBOS_REFLECT; + float stiffness; ///< Spring stiffness (force per unit extension) + float damping; ///< Damping coefficient + float restLength; ///< Rest length of spring + glm::vec3 localAnchor1; ///< The local contact point relative to the center of mass of the first body. + glm::vec3 localAnchor2; ///< The local contact point relative to the center of mass of the second body. + }; +} // namespace cubos::engine diff --git a/engine/samples/CMakeLists.txt b/engine/samples/CMakeLists.txt index 4603327a3..38e574997 100644 --- a/engine/samples/CMakeLists.txt +++ b/engine/samples/CMakeLists.txt @@ -30,6 +30,7 @@ function(make_sample) target_link_libraries(${target} cubos::engine) cubos_common_target_options(${target}) + if(EMSCRIPTEN) target_link_options(${target} PRIVATE "SHELL:--shell-file ${CMAKE_CURRENT_SOURCE_DIR}/emscripten.html" @@ -77,4 +78,5 @@ make_sample(DIR "ui" ASSETS) make_sample(DIR "physics" ASSETS) make_sample(DIR "complex_physics" ASSETS) make_sample(DIR "distance_constraint" ASSETS) +make_sample(DIR "spring_constraint" ASSETS) make_sample(DIR "games/cubosurfers" ASSETS SOURCES "spawner.cpp" "obstacle.cpp" "player.cpp") diff --git a/engine/samples/distance_constraint/main.cpp b/engine/samples/distance_constraint/main.cpp index c99a378ac..fec0cbfe6 100644 --- a/engine/samples/distance_constraint/main.cpp +++ b/engine/samples/distance_constraint/main.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -48,9 +49,10 @@ struct Options Circle, Pendulum, DoublePendulum, - PushPull + PushPull, + SpringTest }; - Scenarios currentScenario = DoublePendulum; + Scenarios currentScenario = Circle; }; struct State @@ -288,7 +290,7 @@ int main(int argc, char** argv) glm::vec3 direction = glm::normalize(glm::vec3(localToWorld2.mat * glm::vec4(constraint.localAnchor2, 1.0F)) - pointFrom); - gizmos.color({1.0F, 0.0F, 0.0F}); + gizmos.color({0.0F, 1.0F, 1.0F}); gizmos.drawLine("the line between", pointFrom, constraint.isRigid ? pointFrom + glm::normalize(direction) * constraint.fixedDistance : pointFrom + glm::normalize(direction) * constraint.minDistance); @@ -345,6 +347,9 @@ int main(int argc, char** argv) options.currentScenario = Options::Scenarios::PushPull; break; case Options::Scenarios::PushPull: + options.currentScenario = Options::Scenarios::SpringTest; + break; + case Options::Scenarios::SpringTest: options.currentScenario = Options::Scenarios::Circle; break; } diff --git a/engine/samples/spring_constraint/assets/collisions.bind b/engine/samples/spring_constraint/assets/collisions.bind new file mode 100644 index 000000000..9104f94ae --- /dev/null +++ b/engine/samples/spring_constraint/assets/collisions.bind @@ -0,0 +1,44 @@ +{ + "actions": { + "camera-toggle": [ + {"keys": ["LControl"]} + ], + "reset": [ + {"keys": ["Space"]} + ] + }, + "axes": { + "move-lateral": { + "positive": [ + {"keys": ["D"]} + ], + "negative": [ + {"keys": ["A"]} + ] + }, + "move-vertical": { + "positive": [ + {"keys": ["E"]} + ], + "negative": [ + {"keys": ["Q"]} + ] + }, + "move-longitudinal": { + "positive": [ + {"keys": ["W"]} + ], + "negative": [ + {"keys": ["S"]} + ] + }, + "change-speed": { + "positive": [ + {"keys": ["Tab"]} + ], + "negative": [ + {"keys": ["LShift"]} + ] + } + } +} \ No newline at end of file diff --git a/engine/samples/spring_constraint/assets/collisions.bind.meta b/engine/samples/spring_constraint/assets/collisions.bind.meta new file mode 100644 index 000000000..bafb44aaa --- /dev/null +++ b/engine/samples/spring_constraint/assets/collisions.bind.meta @@ -0,0 +1,3 @@ +{ + "id": "53d85d6f-42a8-4eb8-aeae-1754a4cb20df" +} diff --git a/engine/samples/spring_constraint/main.cpp b/engine/samples/spring_constraint/main.cpp new file mode 100644 index 000000000..bfb266ba7 --- /dev/null +++ b/engine/samples/spring_constraint/main.cpp @@ -0,0 +1,319 @@ +#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 + +using cubos::core::geom::Box; +using cubos::core::io::Key; +using cubos::core::io::Modifiers; + +using namespace cubos::engine; + +static const Asset BindingsAsset = AnyAsset("53d85d6f-42a8-4eb8-aeae-1754a4cb20df"); + +struct Options +{ + CUBOS_ANONYMOUS_REFLECT(Options); + + enum Scenarios + { + SimpleSpring, + DoubleSpring, + PushPull + }; + Scenarios currentScenario = SimpleSpring; +}; + +struct State +{ + CUBOS_ANONYMOUS_REFLECT(State); + + Entity a; + Entity b; + Entity c; +}; + +void createScenario(Commands& commands, State& state, Options& options) +{ + + if (options.currentScenario == Options::Scenarios::SimpleSpring) + { + state.a = commands.create() + .add(BoxCollisionShape{}) + .add(CollisionLayers{}) + .add(CollisionMask{}) + .add(LocalToWorld{}) + .add(Position{glm::vec3{0.0F, 0.0F, 0.0F}}) + .add(Rotation{}) + .add(PhysicsBundle{.mass = 10000000000000000.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .entity(); + + state.b = commands.create() + .add(BoxCollisionShape{}) + .add(CollisionLayers{}) + .add(CollisionMask{}) + .add(LocalToWorld{}) + .add(Position{glm::vec3{0.0F, -5.0F, 0.0F}}) + .add(Rotation{}) + .add(PhysicsBundle{.mass = 50.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .entity(); + + commands.relate(state.a, state.b, + SpringConstraint{.stiffness = 80.0F, + .damping = 8.0F, + .restLength = 1.0F, + .localAnchor1 = {0.0F, 0.0F, 0.0F}, + .localAnchor2 = {0.0F, 0.0F, 0.0F}}); + } + if (options.currentScenario == Options::Scenarios::DoubleSpring) + { + state.a = commands.create() + .add(BoxCollisionShape{}) + .add(CollisionLayers{}) + .add(CollisionMask{}) + .add(LocalToWorld{}) + .add(Position{glm::vec3{0.0F, 0.0F, 0.0F}}) + .add(Rotation{}) + .add(PhysicsBundle{.mass = 10000000000000000.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .entity(); + + state.b = commands.create() + .add(BoxCollisionShape{}) + .add(CollisionLayers{}) + .add(CollisionMask{}) + .add(LocalToWorld{}) + .add(Position{glm::vec3{0.0F, 0.0F, 5.0F}}) + .add(Rotation{}) + .add(PhysicsBundle{.mass = 50.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .entity(); + + state.c = commands.create() + .add(BoxCollisionShape{}) + .add(CollisionLayers{}) + .add(CollisionMask{}) + .add(LocalToWorld{}) + .add(Position{glm::vec3{0.0F, 0.0F, 10.0F}}) + .add(Rotation{}) + .add(PhysicsBundle{.mass = 50.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .entity(); + + commands.relate(state.a, state.b, + SpringConstraint{.stiffness = 80.0F, + .damping = 80.0F, + .restLength = 1.0F, + .localAnchor1 = {0.0F, 0.0F, 0.0F}, + .localAnchor2 = {0.0F, 0.0F, 0.0F}}); + + commands.relate(state.b, state.c, + SpringConstraint{.stiffness = 80.0F, + .damping = 8.0F, + .restLength = 1.0F, + .localAnchor1 = {0.0F, 0.0F, 0.0F}, + .localAnchor2 = {0.0F, 0.0F, 0.0F}}); + } + if (options.currentScenario == Options::Scenarios::PushPull) + { + state.a = commands.create() + .add(BoxCollisionShape{}) + .add(CollisionLayers{}) + .add(CollisionMask{}) + .add(LocalToWorld{}) + .add(Position{glm::vec3{0.0F, 0.0F, 0.0F}}) + .add(Rotation{}) + .add(PhysicsBundle{.mass = 10000000000000000.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .entity(); + + state.b = commands.create() + .add(BoxCollisionShape{}) + .add(CollisionLayers{}) + .add(CollisionMask{}) + .add(LocalToWorld{}) + .add(Position{glm::vec3{0.0F, 0.0F, 2.0F}}) + .add(Rotation{}) + .add(PhysicsBundle{.mass = 50.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .entity(); + + state.c = commands.create() + .add(BoxCollisionShape{}) + .add(CollisionLayers{}) + .add(CollisionMask{}) + .add(LocalToWorld{}) + .add(Position{glm::vec3{0.0F, 0.0F, 5.0F}}) + .add(Rotation{}) + .add(PhysicsBundle{.mass = 50.0F, .velocity = {0.0F, 0.0F, -1.0F}}) + .entity(); + + commands.relate(state.a, state.b, + SpringConstraint{.stiffness = 80.0F, + .damping = 0.0F, + .restLength = 2.0F, + .localAnchor1 = {0.0F, 0.0F, 0.0F}, + .localAnchor2 = {0.0F, 0.0F, 0.0F}}); + commands.relate(state.b, state.c, + SpringConstraint{.stiffness = 80.0F, + .damping = 0.0F, + .restLength = 3.0F, + .localAnchor1 = {0.0F, 0.0F, 0.0F}, + .localAnchor2 = {0.0F, 0.0F, 0.0F}}); + } +} + +int main(int argc, char** argv) +{ + auto cubos = Cubos(argc, argv); + + cubos.plugin(settingsPlugin); + cubos.plugin(windowPlugin); + cubos.plugin(transformPlugin); + cubos.plugin(fixedStepPlugin); + cubos.plugin(assetsPlugin); + cubos.plugin(inputPlugin); + cubos.plugin(renderDefaultsPlugin); + cubos.plugin(gizmosPlugin); + cubos.plugin(collisionsPlugin); + cubos.plugin(physicsPlugin); + cubos.plugin(physicsSolverPlugin); + cubos.plugin(freeCameraPlugin); + cubos.tag(gizmosDrawTag).after(toneMappingTag); + + cubos.resource(); + cubos.resource(); + + cubos.startupSystem("configure Assets").before(settingsTag).call([](Settings& settings) { + settings.setString("assets.app.osPath", APP_ASSETS_PATH); + settings.setString("assets.builtin.osPath", BUILTIN_ASSETS_PATH); + }); + + cubos.startupSystem("setup input").tagged(assetsTag).call([](const Assets& assets, Input& input) { + auto bindings = assets.read(BindingsAsset); + input.bind(*bindings); + }); + + cubos.startupSystem("setup camera").call([](Commands commands) { + auto targetEnt = commands.create().add(RenderTargetDefaults{}).add(GizmosTarget{}).entity(); + + commands.create() + .relatedTo(targetEnt, DrawsTo{}) + .add(Camera{.zNear = 0.1F, .zFar = 100.0F}) + .add(PerspectiveCamera{.fovY = 60.0F}) + .add(LocalToWorld{}) + .add(Position{{-4.0F, 1.5F, 0.0F}}) + .add(Rotation{Rotation::lookingAt({3.0F, -1.0F, 0.0F}, glm::vec3{0.0F, 1.0F, 0.0F})}) + .add(FreeCameraController{.phi = -15.0F, .theta = 90.0F}); + }); + + cubos.startupSystem("create starting scenario").call([](State& state, Options& options, Commands commands) { + createScenario(commands, state, options); + }); + + cubos.system("activate free camera").call([](const Input& input, Query query) { + for (auto [controller] : query) + { + if (input.justPressed("camera-toggle")) + { + controller.enabled = !controller.enabled; + } + } + }); + + cubos.system("render") + .after(gizmosDrawTag) + .call([](Gizmos& gizmos, Query query, + Query query2) { + for (auto [localToWorld, colliderAABB] : query) + { + auto size = colliderAABB.localAABB.box().halfSize * 2.0F; + glm::mat4 transform = glm::scale(localToWorld.mat, size); + gizmos.color({0.8F, 0.8F, 1.0F}); + gizmos.drawWireBox("local AABB", transform); + + gizmos.color({0.0F, 1.0F, 0.0F}); + gizmos.drawWireBox("world AABB", colliderAABB.worldAABB.min(), colliderAABB.worldAABB.max()); + } + for (auto [localToWorld1, constraint, localToWorld2] : query2) + { + + glm::vec3 worldAnchor1 = (localToWorld1.mat * glm::vec4(constraint.localAnchor1, 1.0F)); + glm::vec3 worldAnchor2 = (localToWorld2.mat * glm::vec4(constraint.localAnchor2, 1.0F)); + + gizmos.color({0.0F, 1.0F, 1.0F}); + gizmos.drawLine("spring", worldAnchor1, worldAnchor2); + } + }); + + cubos.system("apply gravity") + .tagged(physicsApplyForcesTag) + .call([](State& state, Options& options, Query query) { + auto [bVel, bFor, bMass] = *query.at(state.b); + + if (options.currentScenario == Options::Scenarios::PushPull) + { + return; + } + + glm::vec3 gravitationForce = bMass.mass * glm::vec3(0.0F, -9.8F, 0.0F); + bFor.add(gravitationForce); + + if (options.currentScenario == Options::Scenarios::DoubleSpring) + { + auto [cVel, cFor, cMass] = *query.at(state.c); + + cFor.add(gravitationForce); + } + }); + + cubos.system("switch scenarios").call([](State& state, Options& options, Commands commands, const Input& input) { + if (input.justPressed("reset")) + { + commands.destroy(state.a); + commands.destroy(state.b); + if (!state.c.isNull()) + { + commands.destroy(state.c); + } + switch (options.currentScenario) + { + case Options::Scenarios::SimpleSpring: + options.currentScenario = Options::Scenarios::DoubleSpring; + break; + case Options::Scenarios::DoubleSpring: + options.currentScenario = Options::Scenarios::PushPull; + break; + case Options::Scenarios::PushPull: + options.currentScenario = Options::Scenarios::SimpleSpring; + break; + } + createScenario(commands, state, options); + } + }); + + cubos.run(); + return 0; +} diff --git a/engine/samples/spring_constraint/page.md b/engine/samples/spring_constraint/page.md new file mode 100644 index 000000000..e8e4a7a8b --- /dev/null +++ b/engine/samples/spring_constraint/page.md @@ -0,0 +1,21 @@ +SpringConstraint {#examples-engine-spring_constraint} + +@brief Using the @ref physics-solver-plugin plugin. + +@see Full source code here + +This example demonstrates the behaviour of the @ref cubos::engine::SpringConstraint relation in multiple physics scenarios. + +The @ref cubos::engine::SpringConstraint enforces a spring-like connection between two entities. The spring tries to maintain a specified rest length, applying forces to pull or push the entities toward each other. Damping can be applied to reduce oscillations. Each entity requires the position of the local anchor points (localAnchor1 and localAnchor2), which can be set to {0.0F, 0.0F, 0.0F} for the center of the grid. + +The sample includes three scenarios: + +SimpleSpring: One entity is fixed and the other is free with an initial position. The free entity oscillates around the fixed one due to the spring force. + +Double Spring: Adds a second free entity connected to the first, creating a chained spring system. + +PushPull: One entity collides with a constrained spring entity, demonstrating spring response. + +To switch between these scenarios, press the space-bar. + + diff --git a/engine/src/physics/constraints/spring_constraint.cpp b/engine/src/physics/constraints/spring_constraint.cpp new file mode 100644 index 000000000..eed74dcc7 --- /dev/null +++ b/engine/src/physics/constraints/spring_constraint.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include + +#include + +CUBOS_REFLECT_IMPL(cubos::engine::SpringConstraint) +{ + return cubos::core::ecs::TypeBuilder("cubos::engine::SpringConstraint") + .withField("stiffness", &SpringConstraint::stiffness) + .withField("damping", &SpringConstraint::damping) + .withField("restLength", &SpringConstraint::restLength) + .withField("localAnchor1", &SpringConstraint::localAnchor1) + .withField("localAnchor2", &SpringConstraint::localAnchor2) + .build(); +} diff --git a/engine/src/physics/solver/plugin.cpp b/engine/src/physics/solver/plugin.cpp index 9a737112e..b755d6fe3 100644 --- a/engine/src/physics/solver/plugin.cpp +++ b/engine/src/physics/solver/plugin.cpp @@ -7,7 +7,7 @@ #include "../fixed_substep/plugin.hpp" #include "distance_constraint/plugin.hpp" #include "integration/plugin.hpp" - +#include "spring_constraint/plugin.hpp" CUBOS_DEFINE_TAG(cubos::engine::physicsPrepareSolveTag); CUBOS_DEFINE_TAG(cubos::engine::physicsIntegrateVelocityTag); @@ -56,4 +56,5 @@ void cubos::engine::physicsSolverPlugin(Cubos& cubos) cubos.plugin(physicsIntegrationPlugin); cubos.plugin(penetrationConstraintPlugin); cubos.plugin(distanceConstraintPlugin); + cubos.plugin(springConstraintPlugin); } diff --git a/engine/src/physics/solver/spring_constraint/plugin.cpp b/engine/src/physics/solver/spring_constraint/plugin.cpp new file mode 100644 index 000000000..593bdf253 --- /dev/null +++ b/engine/src/physics/solver/spring_constraint/plugin.cpp @@ -0,0 +1,86 @@ +#include "plugin.hpp" + +#include +#include + +#include +#include +#include +#include + +#include "../../fixed_substep/plugin.hpp" + +using namespace cubos::engine; + +CUBOS_DEFINE_TAG(cubos::engine::springConstraintSolveTag); + +static void solveSpringConstraint(Query + query, + const FixedDeltaTime& dt, const Substeps& substeps) +{ + float subDeltaTime = dt.value / static_cast(substeps.value); + + for (auto [ent1, mass1, inertia1, rotation1, position1, velocity1, angVelocity1, constraint, ent2, mass2, inertia2, + rotation2, position2, velocity2, angVelocity2] : query) + { + glm::vec3 r1 = rotation1.quat * constraint.localAnchor1; + glm::vec3 r2 = rotation2.quat * constraint.localAnchor2; + + glm::vec3 worldAnchor1 = position1.vec + r1; + glm::vec3 worldAnchor2 = position2.vec + r2; + + glm::vec3 springVec = worldAnchor2 - worldAnchor1; + float currentLength = glm::length(springVec); + + if (currentLength < 1e-6F) + { + continue; + } + + glm::vec3 direction = springVec / currentLength; + + float extension = currentLength - constraint.restLength; + + float springForce = constraint.stiffness * extension; + + glm::vec3 vel1 = velocity1.vec + glm::cross(angVelocity1.vec, r1); + glm::vec3 vel2 = velocity2.vec + glm::cross(angVelocity2.vec, r2); + glm::vec3 relVel = vel2 - vel1; + + float dampingForce = constraint.damping * glm::dot(relVel, direction); + + float totalForce = springForce + dampingForce; + + float impulse = totalForce * subDeltaTime; + glm::vec3 impulseVec = impulse * direction; + + velocity1.vec += impulseVec * mass1.inverseMass; + velocity2.vec -= impulseVec * mass2.inverseMass; + + angVelocity1.vec += inertia1.inverseInertia * glm::cross(r1, impulseVec); + angVelocity2.vec -= inertia2.inverseInertia * glm::cross(r2, impulseVec); + } +} + +void cubos::engine::springConstraintPlugin(Cubos& cubos) +{ + cubos.depends(fixedStepPlugin); + cubos.depends(transformPlugin); + cubos.depends(physicsPlugin); + cubos.depends(physicsSolverPlugin); + + cubos.relation(); + + cubos.tag(springConstraintSolveTag); + + cubos.system("solve spring constraints") + .tagged(springConstraintSolveTag) + .tagged(physicsSolveConstraintTag) + .call([](Query + query, + const FixedDeltaTime& dt, const Substeps& substeps) { solveSpringConstraint(query, dt, substeps); }); +} diff --git a/engine/src/physics/solver/spring_constraint/plugin.hpp b/engine/src/physics/solver/spring_constraint/plugin.hpp new file mode 100644 index 000000000..c322d268a --- /dev/null +++ b/engine/src/physics/solver/spring_constraint/plugin.hpp @@ -0,0 +1,22 @@ +/// @file +/// @brief Spring Constraint plugin. +/// @ingroup physics-solver-plugin + +#pragma once + +#include +#include + +namespace cubos::engine +{ + /// @ingroup physics-solver-plugin + /// @brief Adds solver for spring constraint. + + /// @brief Tag for the spring constraint solve system. + extern Tag springConstraintSolveTag; + + /// @brief Plugin entry function. + /// @param cubos @b Cubos main class + /// @ingroup physics-solver-plugin + void springConstraintPlugin(Cubos& cubos); +} // namespace cubos::engine diff --git a/flake.lock b/flake.lock index b8cbf4c79..dfc7079e6 100644 --- a/flake.lock +++ b/flake.lock @@ -20,40 +20,23 @@ }, "nixpkgs": { "locked": { - "lastModified": 1746810718, - "narHash": "sha256-VljtYzyttmvkWUKTVJVW93qAsJsrBbgAzy7DdnJaQfI=", + "lastModified": 1735563628, + "narHash": "sha256-OnSAY7XDSx7CtDoqNh8jwVwh4xNL/2HaJxGjryLWzX8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0c0bf9c057382d5f6f63d54fd61f1abd5e1c2f63", + "rev": "b134951a4c9f3c995fd7be05f3243f8ecd65d798", "type": "github" }, "original": { "id": "nixpkgs", - "ref": "nixos-24.11", + "ref": "nixos-24.05", "type": "indirect" } }, - "nixpkgs-unstable": { - "locked": { - "lastModified": 1746663147, - "narHash": "sha256-Ua0drDHawlzNqJnclTJGf87dBmaO/tn7iZ+TCkTRpRc=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "dda3dcd3fe03e991015e9a74b22d35950f264a54", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", - "nixpkgs-unstable": "nixpkgs-unstable" + "nixpkgs": "nixpkgs" } }, "systems": { diff --git a/flake.nix b/flake.nix index 85a70ae34..e84209768 100644 --- a/flake.nix +++ b/flake.nix @@ -1,77 +1,161 @@ -# Flake used for development with nix { + inputs = { - nixpkgs.url = "nixpkgs/nixos-24.11"; - nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable"; + + nixpkgs.url = "nixpkgs/nixos-24.05"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = inputs: + inputs.flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import inputs.nixpkgs { inherit system; }; - pkgs-unstable = import inputs.nixpkgs-unstable { inherit system; }; + + sysLibs = with pkgs; [ + libGL + xorg.libX11 + xorg.libXrandr + xorg.libXinerama + xorg.libXcursor + xorg.libXi + xorg.libXdmcp + libpulseaudio + wayland + ]; + in + { + name = "cubos"; + + devShell = pkgs.mkShell { + hardeningDisable = [ "all" ]; + + packages = with pkgs; [ + # = build tools = + cmake + ccache + pkg-config - pkgs-unstable.emscripten + + emscripten + ninja + gcc + + + # = formatting = + clang-tools + + # = docs = + doxygen + + (python3.withPackages (ps: [ + ps.jinja2 + ps.pygments + ])) + + # = libs = + glfw + glm + doctest + + # = debug = + gdb + ] + ++ sysLibs; + + LD_LIBRARY_PATH = "$LD_LIBRARY_PATH:${pkgs.lib.makeLibraryPath sysLibs}"; + + shellHook = '' - export EMSCRIPTEN=${pkgs-unstable.emscripten}/share/emscripten + + export EMSCRIPTEN=${pkgs.emscripten}/share/emscripten + export EM_CACHE=$(git rev-parse --show-toplevel)/.em_cache + mkdir -p $EM_CACHE + cp -r $EMSCRIPTEN/cache/* $EM_CACHE + chmod u+rwx -R $EM_CACHE + + + + + + if [ -n "$WAYLAND_DISPLAY" ]; then + + + LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$(nix-build '' -A wayland --no-out-link)/lib" + + + fi + ''; + }; + + packages.default = pkgs.callPackage ./default.nix { }; + + formatter = pkgs.alejandra; + }); + }