// Sources pour https://github.com/NazaraEngine/NazaraEngine/wiki/(FR)-Tutoriel:-%5B01%5D-Hello-World #define USE_JOLT 1 #include #include #include #if USE_JOLT #include #else #include #endif #include #include #include #include constexpr float BoxDims = 16.f; int main() { try { // Mise en place de l'application, de la fenêtre et du monde Nz::Renderer::Config renderConfig; renderConfig.validationLevel = Nz::RenderAPIValidationLevel::None; #if USE_JOLT Nz::Application app(renderConfig); #else Nz::Application app(renderConfig); #endif auto& windowing = app.AddComponent(); Nz::Window& mainWindow = windowing.CreateWindow(Nz::VideoMode(1920, 1080), "Physics playground"); auto& fs = app.AddComponent(); { std::filesystem::path resourceDir = "assets/examples"; if (!std::filesystem::is_directory(resourceDir) && std::filesystem::is_directory("../.." / resourceDir)) resourceDir = "../.." / resourceDir; fs.Mount("assets", resourceDir); } auto& ecs = app.AddComponent(); auto& world = ecs.AddWorld(); #if USE_JOLT auto& physSystem = world.AddSystem(); #else auto& physSystem = world.AddSystem(); #endif physSystem.GetPhysWorld().SetMaxStepCount(1); physSystem.GetPhysWorld().SetStepSize(Nz::Time::TickDuration(30)); physSystem.GetPhysWorld().SetGravity(Nz::Vector3f::Down() * 9.81f); //physSystem.GetPhysWorld().SetGravity(Nz::Vector3f::Zero()); auto& renderSystem = world.AddSystem(); Nz::WindowSwapchain& windowSwapchain = renderSystem.CreateSwapchain(mainWindow); Nz::Vector3f target = Nz::Vector3f::Zero(); entt::handle boxEntity = world.CreateEntity(); { std::shared_ptr boxMesh = Nz::GraphicalMesh::Build(Nz::Primitive::Box(Nz::Vector3f(BoxDims), Nz::Vector3ui::Zero(), Nz::Matrix4f::Scale(Nz::Vector3f(-1.f)), Nz::Rectf(0.f, 0.f, 2.f, 2.f))); Nz::TextureSamplerInfo planeSampler; planeSampler.anisotropyLevel = 16; planeSampler.wrapModeU = Nz::SamplerWrap::Repeat; planeSampler.wrapModeV = Nz::SamplerWrap::Repeat; std::shared_ptr boxMat = Nz::Graphics::Instance()->GetDefaultMaterials().phongMaterial->Instantiate(); boxMat->SetTextureProperty("BaseColorMap", fs.Load("assets/dev_grey.png"), planeSampler); boxMat->DisablePass("ShadowPass"); boxMat->UpdatePassesStates([&](Nz::RenderStates& states) { states.frontFace = Nz::FrontFace::Clockwise; }); std::shared_ptr boxModel = std::make_shared(std::move(boxMesh)); boxModel->SetMaterial(0, std::move(boxMat)); auto& boxGfx = boxEntity.emplace(); boxGfx.AttachRenderable(std::move(boxModel)); boxEntity.emplace(); #if USE_JOLT std::shared_ptr wallCollider = std::make_shared(Nz::Vector3f(BoxDims, BoxDims, 1.f)); #else std::shared_ptr wallCollider = std::make_shared(Nz::Vector3f(BoxDims, BoxDims, 1.f)); #endif #if USE_JOLT std::vector colliders; #else std::vector colliders; #endif for (Nz::Vector3f normal : { Nz::Vector3f::Forward(), Nz::Vector3f::Backward(), Nz::Vector3f::Left(), Nz::Vector3f::Right(), Nz::Vector3f::Up(), Nz::Vector3f::Down() }) { auto& colliderEntry = colliders.emplace_back(); colliderEntry.collider = wallCollider; #if USE_JOLT colliderEntry.rotation = Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), normal); colliderEntry.offset = normal * BoxDims * 0.5f + normal * 0.5f; #else colliderEntry.offsetMatrix = Nz::Matrix4f::Transform(normal * BoxDims * 0.5f + normal * 0.5f, Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), normal)); #endif } #if USE_JOLT std::shared_ptr boxCollider = std::make_shared(std::move(colliders)); #else std::shared_ptr boxCollider = std::make_shared(std::move(colliders)); #endif #if USE_JOLT auto& ballPhysics = boxEntity.emplace(physSystem.CreateRigidBody(boxCollider)); #else auto& ballPhysics = boxEntity.emplace(physSystem.CreateRigidBody(boxCollider)); #endif ballPhysics.SetMass(0.f); std::shared_ptr colliderModel; { std::shared_ptr colliderMat = Nz::Graphics::Instance()->GetDefaultMaterials().basicMaterial->Instantiate(); colliderMat->SetValueProperty("BaseColor", Nz::Color::Green()); colliderMat->UpdatePassesStates([](Nz::RenderStates& states) { states.primitiveMode = Nz::PrimitiveMode::LineList; return true; }); /*std::shared_ptr colliderMesh = Nz::Mesh::Build(boxCollider->GenerateDebugMesh()); std::shared_ptr colliderGraphicalMesh = Nz::GraphicalMesh::BuildFromMesh(*colliderMesh); colliderModel = std::make_shared(colliderGraphicalMesh); for (std::size_t i = 0; i < colliderModel->GetSubMeshCount(); ++i) colliderModel->SetMaterial(i, colliderMat); boxGfx.AttachRenderable(std::move(colliderModel));*/ } } std::shared_ptr sphereMesh = Nz::GraphicalMesh::Build(Nz::Primitive::IcoSphere(1.f)); //std::mt19937 rd(std::random_device{}()); std::mt19937 rd(42); std::uniform_real_distribution colorDis(0.f, 360.f); std::uniform_real_distribution radiusDis(0.1f, 0.5f); constexpr std::size_t SphereCount = 1000; for (std::size_t i = 0; i < SphereCount; ++i) { float radius = radiusDis(rd); std::uniform_real_distribution positionRandom(-BoxDims * 0.5f + radius, BoxDims * 0.5f - radius); #if USE_JOLT std::shared_ptr sphereCollider = std::make_shared(radius); #else std::shared_ptr sphereCollider = std::make_shared(radius); #endif entt::handle ballEntity = world.CreateEntity(); std::shared_ptr ballMaterial = Nz::Graphics::Instance()->GetDefaultMaterials().phongMaterial->Instantiate(); ballMaterial->SetValueProperty("BaseColor", Nz::Color::FromHSV(colorDis(rd), 1.f, 1.f)); std::shared_ptr sphereModel = std::make_shared(sphereMesh); sphereModel->SetMaterial(0, std::move(ballMaterial)); auto& ballGfx = ballEntity.emplace(); ballGfx.AttachRenderable(std::move(sphereModel)); auto& ballNode = ballEntity.emplace(); ballNode.SetPosition(positionRandom(rd), positionRandom(rd), positionRandom(rd)); ballNode.SetScale(radius); #if USE_JOLT auto& ballPhysics = ballEntity.emplace(physSystem.CreateRigidBody(sphereCollider)); #else auto& ballPhysics = ballEntity.emplace(physSystem.CreateRigidBody(sphereCollider)); #endif ballPhysics.SetMass(4.f / 3.f * Nz::Pi * Nz::IntegralPow(radius, 3)); //ballPhysics.DisableSleeping(); } std::uniform_real_distribution lengthDis(0.2f, 1.5f); std::shared_ptr boxMesh = Nz::GraphicalMesh::Build(Nz::Primitive::Box(Nz::Vector3f(1.f))); constexpr std::size_t BoxCount = 100; for (std::size_t i = 0; i < BoxCount; ++i) { float width = lengthDis(rd); float height = lengthDis(rd); float depth = lengthDis(rd); std::uniform_real_distribution xRandom(-BoxDims * 0.5f + width, BoxDims * 0.5f - width); std::uniform_real_distribution yRandom(-BoxDims * 0.5f + height, BoxDims * 0.5f - height); std::uniform_real_distribution zRandom(-BoxDims * 0.5f + depth, BoxDims * 0.5f - depth); entt::handle ballEntity = world.CreateEntity(); std::shared_ptr boxMaterial = Nz::Graphics::Instance()->GetDefaultMaterials().phongMaterial->Instantiate(); boxMaterial->SetValueProperty("BaseColor", Nz::Color::FromHSV(colorDis(rd), 1.f, 1.f)); std::shared_ptr sphereModel = std::make_shared(boxMesh); sphereModel->SetMaterial(0, std::move(boxMaterial)); auto& ballGfx = ballEntity.emplace(); ballGfx.AttachRenderable(std::move(sphereModel)); auto& ballNode = ballEntity.emplace(); ballNode.SetPosition(xRandom(rd), yRandom(rd), zRandom(rd)); ballNode.SetScale(width, height, depth); #if USE_JOLT std::shared_ptr boxCollider = std::make_shared(Nz::Vector3f(width, height, depth)); #else std::shared_ptr boxCollider = std::make_shared(Nz::Vector3f(width, height, depth)); #endif #if USE_JOLT auto& ballPhysics = ballEntity.emplace(physSystem.CreateRigidBody(boxCollider)); #else auto& ballPhysics = ballEntity.emplace(physSystem.CreateRigidBody(boxCollider)); #endif ballPhysics.SetMass(width * height * depth); //ballPhysics.DisableSleeping(); } // Lumière entt::handle lightEntity = world.CreateEntity(); { auto& lightNode = lightEntity.emplace(); //lightNode.SetPosition(Nz::Vector3f(-20.f, 20.f, -20.f)); //lightNode.SetRotation(Nz::EulerAnglesf(-45.f, -135.f, 0.f)); lightNode.SetPosition(Nz::Vector3f::Up() * 15.f); lightNode.SetRotation(Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), Nz::Vector3f::Down())); auto& lightComp = lightEntity.emplace(); auto& spotLight = lightComp.AddLight(); spotLight.EnableShadowCasting(true); spotLight.UpdateInnerAngle(Nz::DegreeAnglef(30.f)); spotLight.UpdateOuterAngle(Nz::DegreeAnglef(40.f)); spotLight.UpdateRadius(30.f); spotLight.UpdateShadowMapSize(2048); spotLight.UpdateAmbientFactor(0.5f); } Nz::EulerAnglesf camAngles(-45.f, -135.f, 0.f); float camDistance = 12.f; // Création de la caméra entt::handle cameraEntity = world.CreateEntity(); { cameraEntity.emplace(); auto& cameraComponent = cameraEntity.emplace(&windowSwapchain, Nz::ProjectionType::Perspective); cameraComponent.UpdateFOV(70.f); cameraComponent.UpdateClearColor(Nz::Color(0.46f, 0.48f, 0.84f, 1.f)); } auto UpdateCamera = [&] { auto& cameraNode = cameraEntity.get(); cameraNode.SetRotation(camAngles); cameraNode.SetPosition(target - cameraNode.GetForward() * camDistance); }; UpdateCamera(); NazaraSlot(Nz::WindowEventHandler, OnMouseMoved, cameraMove); NazaraSlot(Nz::WindowEventHandler, OnMouseMoved, grabbedObjectMove); //std::optional grabConstraint; auto mouseMoveCallback = [&](const Nz::WindowEventHandler*, const Nz::WindowEvent::MouseMoveEvent& event) { constexpr float sensitivity = 0.3f; camAngles.yaw -= event.deltaX * sensitivity; camAngles.yaw.Normalize(); camAngles.pitch = Nz::Clamp(camAngles.pitch - event.deltaY * sensitivity, -89.f, 89.f); UpdateCamera(); }; Nz::WindowEventHandler& eventHandler = mainWindow.GetEventHandler(); eventHandler.OnMouseButtonPressed.Connect([&](const Nz::WindowEventHandler*, const Nz::WindowEvent::MouseButtonEvent& event) { if (event.button == Nz::Mouse::Middle) { cameraMove.Connect(eventHandler.OnMouseMoved, mouseMoveCallback); } else if (event.button == Nz::Mouse::Left) { auto& cameraComponent = cameraEntity.get(); Nz::Vector3f from = cameraComponent.Unproject({ float(event.x), float(event.y), 0.f }); Nz::Vector3f to = cameraComponent.Unproject({ float(event.x), float(event.y), 1.f }); Nz::JoltPhysics3DSystem::RaycastHit lastHitInfo; auto callback = [&](const Nz::JoltPhysics3DSystem::RaycastHit& hitInfo) -> std::optional { if (hitInfo.hitEntity == boxEntity) { Nz::Vector3f dirToCenter = Nz::Vector3f::Zero() - hitInfo.hitPosition; dirToCenter.Normalize(); #ifdef USE_JOLT if (Nz::Vector3f::DotProduct(dirToCenter, (hitInfo.hitPosition - from).Normalize()) < 0.f) return std::nullopt; #else if (Nz::Vector3f::DotProduct(dirToCenter, hitInfo.hitNormal) < 0.f) return std::nullopt; #endif } lastHitInfo = hitInfo; return hitInfo.fraction; }; if (physSystem.RaycastQuery(from, to, callback)) { if (lastHitInfo.hitBody && lastHitInfo.hitEntity != boxEntity) { //grabConstraint.emplace(*lastHitInfo.hitBody, lastHitInfo.hitPosition); //grabConstraint->SetImpulseClamp(30.f); grabbedObjectMove.Connect(eventHandler.OnMouseMoved, [&, body = lastHitInfo.hitBody, distance = Nz::Vector3f::Distance(from, lastHitInfo.hitPosition)](const Nz::WindowEventHandler*, const Nz::WindowEvent::MouseMoveEvent& event) { Nz::Vector3f from = cameraComponent.Unproject({ float(event.x), float(event.y), 0.f }); Nz::Vector3f to = cameraComponent.Unproject({ float(event.x), float(event.y), 1.f }); Nz::Vector3f newPosition = from + (to - from).Normalize() * distance; //grabConstraint->SetSecondAnchor(newPosition); }); } else cameraMove.Connect(eventHandler.OnMouseMoved, mouseMoveCallback); } else cameraMove.Connect(eventHandler.OnMouseMoved, mouseMoveCallback); } }); eventHandler.OnMouseButtonReleased.Connect([&](const Nz::WindowEventHandler*, const Nz::WindowEvent::MouseButtonEvent& event) { if (event.button == Nz::Mouse::Left) { //grabConstraint.reset(); cameraMove.Disconnect(); grabbedObjectMove.Disconnect(); } else if (event.button == Nz::Mouse::Middle) cameraMove.Disconnect(); }); eventHandler.OnMouseWheelMoved.Connect([&](const Nz::WindowEventHandler*, const Nz::WindowEvent::MouseWheelEvent& event) { camDistance = Nz::Clamp(camDistance - event.delta, 5.f, 20.f); UpdateCamera(); }); eventHandler.OnKeyReleased.Connect([&](const Nz::WindowEventHandler*, const Nz::WindowEvent::KeyEvent& event) { if (event.virtualKey == Nz::Keyboard::VKey::G) { auto& cameraNode = cameraEntity.get(); physSystem.GetPhysWorld().SetGravity(cameraNode.GetBackward() * 9.81f); } }); Nz::DegreeAnglef rotation = 0.f; app.AddUpdater([&](Nz::Time elapsedTime) { rotation += elapsedTime.AsSeconds() * 45.f; //physSystem.GetPhysWorld().SetGravity(Nz::Quaternionf(Nz::EulerAnglesf(0.f, rotation, 0.f)) * Nz::Vector3f::Forward() * 10.f); }); Nz::MillisecondClock fpsClock; unsigned int fps = 0; app.AddUpdater([&](Nz::Time /*elapsedTime*/) { fps++; if (fpsClock.RestartIfOver(Nz::Time::Second())) { mainWindow.SetTitle("Physics playground - " + Nz::NumberToString(fps) + " FPS" + " - " + Nz::NumberToString(world.GetRegistry().alive()) + " entities"); physSystem.Dump(); fps = 0; } }); return app.Run(); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } }