diff --git a/ChangeLog.md b/ChangeLog.md index ad2dd0a55..b2f71d16d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -19,6 +19,7 @@ Miscellaneous: - When supported, projects are now parts of a virtual "workspace group" according to their kind - Fixed .dll copy when building Nazara occuring on Linux when targeting Windows (MinGW) - ⚠ Appveyor nightlies are now compiled with VS2017 +- Set libraries' rpath to current folder (.) Nazara Engine: - VertexMapper:GetComponentPtr no longer throw an error if component is disabled or incompatible with template type, instead a null pointer is returned. @@ -54,7 +55,7 @@ Nazara Engine: - Fix RigidBody3D copy constructor not copying all physics states (angular/linear damping/velocity, mass center, position and rotation) - Add RigidBody3D simulation control (via EnableSimulation and IsSimulationEnabled), which allows to disable physics and collisions at will. - Fix some uninitialized values (found by Valgrind) in Network module -- Fix possible infinite recursion when outputting a Thread::Id object +- Fix possible infinite recursion when outputting a Thread::Id object - ⚠️ Replaced implicit conversion from a Nz::String to a std::string by an explicit method ToStdString() - Fix LuaInstance movement constructor/assignment operator which was corrupting Lua memory - Fix potential bug on SocketImpl::Connect (used by TcpClient::Connect) on POSIX platforms @@ -111,7 +112,7 @@ Nazara Engine: - Fixed SocketPoller not be able to recover from some errors (like invalid sockets and such) - Add LuaImplQuery implementation for std::vector - Fixed LuaState::PushGlobal & LuaState::PushField to copy the object before moving it -- ⚠️ Replaced currentBitPos and currentByte fields by [read|write][BitPos][Byte] to handle properly bit reading/writing. +- ⚠️ Replaced currentBitPos and currentByte fields by [read|write][BitPos][Byte] to handle properly bit reading/writing. - InstancedRenderable::SetMaterial methods are now public. - Fixed Model copy constructor not copying materials - ⚠️ Added InstancedRenderable::Clone() method @@ -147,14 +148,47 @@ Nazara Engine: - ⚠️ CullingList now handles full and partial visibility testing - Added math class Angle, capable of handling both degrees and radians angles and converting them to euler angles/quaternions to improve 2D interface. - ⚠️ AbstractSocket::OnStateChange has been replaced by OnStateChanged, which is now called after state has been changed (with oldState and newState as parameters). -- ⚠️ TcpClient::WaitForconnected now returns the new socket state. -- Added TcpClient::PollForConnected +- ⚠️ TcpClient::WaitForconnected now returns the new socket state. +- Added TcpClient::PollForConnected - ⚠️ Use of the new Angle class instead of floating point angle - It is now possible to set elasticity/friction/surface bodies of 2D colliders and change it at runtime on RigidBody2D - ObjectHandle were remade and should be way more optimized now - Added ENetHost and ENetPeer accessor to total packet/data received/sent/lost - ⚠ **Changed the way resources were Loaded, almost every LoadFromX and OpenFromX methods are now static and create the object themselves.** - ⚠ SoundStream is now responsible for loaders instead of Music, and is now threadsafe (you can now load a stream only once and play it multiple times at the same time) +- Added LuaState::RawEqual +- Fixed LuaCoroutine movement assignation operator +- Added Arbiter2D::GetBodies +- Added RigidBody2D::ForEachArbiter +- Added possibility to change the RigidBody2D velocity function called by the physics engine +- Fixed MouseButtonEvent and MouseMoveEvent mouse absolute position being unsigned (now signed) +- Fixed Window::SetCursor changing cursor even if window was in foreground on Windows +- Fixed SystemCursor_Move not showing up on Windows +- Fixed Window movement constructor/assignation operator +- Window::PushEvent is now public (useful for pushing external events ie. when using Qt or similar framework controlling window) +- Fixed TileMap not rendering the right materials if it had no tile using some materials in-between +- Added Vector[2|3|4](u)i64 typedefs +- Fixed missing static Vector4::DotProduct implementation +- ⚠ **By default, Nazara computes the mass center of all 2D physics object when calling SetGeom** +- ⚠ Added Collider2D::ComputeCenterOfMass +- Signal now implement a copy constructor and copy assignation operator for convenience +- Fixed ENet UnreliableFragment packets sent as Unreliable (and such being incomplete upon reception) +- ENet DisconnectLater now reflects libenet behavior (and is waiting for unreliable commands to be sent before disconnecting for good) +- ⚠ Collider3D::ForEachPolygon now takes a void(Vector3f\*, std::size_t) callback (instead of void(float\*, std::size_t)) +- Added Collider2D::ForEachPolygon method +- Added RigidBody::[Get|Set]PositionOffset allowing set an offset between body logic position and body physics position (center of mass position) +- ⚠ Default TextureSampler WrapMode is now Clamp (instead of Repeat) +- Fixed StateMachine ignoring transitions made in Enter/Leave events of states +- Fixed Material::Configure resetting textures +- ⚠ Renamed TextStyleFlags enum to TextStyle, introduced Flags specialization of TextStyle as TextStyleFlags +- ⚠ Font, FontData and SimpleTextDrawer now use a proper TextStyleFlags instead of a UInt32 +- Almost all Math algorithms are now constexpr +- PhysWorld2D: Fixed callbacks not properly replacing each others when registering twice with the same collisionId (pair) +- ⚠ **Font, FontData and SimpleTextDrawer now supports text outlining.** +- Fixed TextSprite not handling multiple textures well +- ⚠ TextSprite will now use multiple render layers by itself (the current one and the one right before, ex: [-1, 0] if base layer is 0) if you use text outlines. +- ⚠ SimpleTextDrawer no longer supports faux bold rendering +- Added PhysWorld2D::[RaycastQuery, RegionQuery] overloads taking a callback Nazara Development Kit: - Added ImageWidget (#139) @@ -198,7 +232,7 @@ Nazara Development Kit: - Fix GraphicsComponent bounding volume not taking local matrix in account - ⚠️ Rewrote all render queue system, which should be more efficient, take scissor box into account - ⚠️ All widgets are now bound to a scissor box when rendering -- Add DebugComponent (a component able to show aabb/obb/collision mesh) +- Add DebugComponent (a component able to show aabb/obb/collision mesh 2D and 3D) - ⚠️ TextAreaWidget now support text selection (WIP) - ⚠️ TextAreaWidget::GetHoveredGlyph now returns a two-dimensional position instead of a single glyph position - Fixed Entity::OnEntityDestruction signal not being properly moved and thus not being called. @@ -226,6 +260,13 @@ Nazara Development Kit: - Added EntityOwner::Release - Add missing `recomputeMoment` parameter to PhysicsComponent2D::SetMass - Added possibility of disabling synchronization between PhysicsComponent2D and NodeComponent +- Fixed GraphicsComponent not invalidating render queue on material change (causing crashes or visual errors) +- Added CollisionComponent2D::SetGeomOffset and CollisionComponent2D::Recenter +- Added LifetimeComponent and LifetimeSystem +- Fixed a subtle bug regarding entities invalidation and kill (ex: if an entity #2 kills entity #1 during Entity::Destroy callbacks, entity #1 will survive destruction). +- Added PhysicsSystem2D::[RaycastQuery, RegionQuery] overloads taking a callback +- Added TextAreaWidget support for outline +- Fixed possible crash when disabling BaseWidget background # 0.4: @@ -313,8 +354,8 @@ Nazara Engine: - Added [Nz::TcpClient::SendMultiple](https://nazara.digitalpulsesoftware.net/doc/class_nz_1_1_tcp_client.html#a495c32beb46ed9192699a3b82d358035) method, allowing to send multiple buffers at once. - Added [Nz::PlacementDestroy](https://nazara.digitalpulsesoftware.net/doc/namespace_nz.html#a27c8667def991fc896c5beff3e62668a). (ea985fa76586762f008e4054938db3234eeaf0cb) - Added [Nz::String::Format](https://nazara.digitalpulsesoftware.net/doc/class_nz_1_1_string.html#a4b699982e7f9ea38f6d44b43ac1e2040) and [Nz::String::FormatVA](https://nazara.digitalpulsesoftware.net/doc/class_nz_1_1_string.html#abe0fcbce11224b157ac756b60e8dee92) static methods. (cc6e4127dc6c61799a64404770992cef0804ad34). -- Added [Nz::ParticleGroup::GetBuffer](https://nazara.digitalpulsesoftware.net/doc/class_nz_1_1_particle_mapper.html#aefe1b251efc8c9b8668842275561be0c) method. (4dc85789b59e50d964c83321dbd4b6485c04bef6) -- Added Nz::ParticleMapper::GetPointer method. (1f4e6c2d1594b7bb9dd6f4ea5480fdd16cf5f208) +- Added [Nz::ParticleGroup::GetBuffer](https://nazara.digitalpulsesoftware.net/doc/class_nz_1_1_particle_mapper.html#aefe1b251efc8c9b8668842275561be0c) method. (4dc85789b59e50d964c83321dbd4b6485c04bef6) +- Added Nz::ParticleMapper::GetPointer method. (1f4e6c2d1594b7bb9dd6f4ea5480fdd16cf5f208) - ⚠️ Structures provied by ParticleStruct header now have a float life. (472d964d587d906764ad1e05bfcc9ab1bf979483) - Fixed scale property of Nz::TextSprite not affecting its bounding volume. (52b29bac775823294c4ad7de70f4dc3f4adfa743) - ⚠️ Nz:MeshParams::flipUVs has been replaced by texCoordOffset and texCoordScale. (a1a7d908adc060fd7a43491c903dfe3b501d98e5) @@ -341,7 +382,7 @@ Nazara Engine: - All noises classes now uses std::mt19937 as a random number generator, to ensure the same results on every machine. (1f5ea9839016964c173d919263827dee69ecb65d) Nazara Development Kit: -- **Added basic widgets**. (c8a12083b3133e946bf60dd060331a4b4631f8d8) +- **Added basic widgets**. (c8a12083b3133e946bf60dd060331a4b4631f8d8) - VelocitySystem will no longer affect entities with PhysicsComponent2D. (a6853234412c744cdcb28344f02f7b0c92704d77) - Fixed EulerAngles constructor in Lua. (d55149a0a70f6230b6f1c3fb50e37dc82a2feb9f) - Fixed Component::OnDetached not being called on entity destruction. (5b777eb4853639d7aeb232ca46d17f0d432f47ca) @@ -352,7 +393,7 @@ Nazara Engine: Nazara Engine: - Nazara binaries are now compiled with Run-Time Type-Information. (a70acdc8f44010627a65282fd3099202116d3e13) -- Nazara demos are now compiled with relative dependencies on Linux. +- Nazara demos are now compiled with relative dependencies on Linux. (d6fbb4c408d48c4a768fad7b43460c76a0df1777) - Added [**Nz::BitCount**](https://nazara.digitalpulsesoftware.net/doc/group__core.html#ga6bfbcff78eb6cfbe3ddaedcfc8c04196) function. (82e31a3ec8449da6618f41690164c2e1d883edb4) - Added [**Nz::Bitset::AppendBits**](https://nazara.digitalpulsesoftware.net/doc/class_nz_1_1_bitset.html#a5ca8f365006c86d6d699d02471904f7e) method. (b018a400499a2356c4455a40d9f6a6c12b3cb36b) diff --git a/SDK/include/NDK/Components.hpp b/SDK/include/NDK/Components.hpp index 97c15fc7c..cc8c37d93 100644 --- a/SDK/include/NDK/Components.hpp +++ b/SDK/include/NDK/Components.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/SDK/include/NDK/Components/CollisionComponent2D.hpp b/SDK/include/NDK/Components/CollisionComponent2D.hpp index aecdc5197..a5da3b8c2 100644 --- a/SDK/include/NDK/Components/CollisionComponent2D.hpp +++ b/SDK/include/NDK/Components/CollisionComponent2D.hpp @@ -20,8 +20,9 @@ namespace Ndk class NDK_API CollisionComponent2D : public Component { - friend class PhysicsSystem2D; friend class ConstraintComponent2D; + friend class PhysicsComponent2D; + friend class PhysicsSystem2D; public: CollisionComponent2D(Nz::Collider2DRef geom = Nz::Collider2DRef()); @@ -30,8 +31,12 @@ namespace Ndk Nz::Rectf GetAABB() const; const Nz::Collider2DRef& GetGeom() const; + const Nz::Vector2f& GetGeomOffset() const; + + void Recenter(const Nz::Vector2f& origin); void SetGeom(Nz::Collider2DRef geom); + void SetGeomOffset(const Nz::Vector2f& geomOffset); CollisionComponent2D& operator=(Nz::Collider2DRef geom); CollisionComponent2D& operator=(CollisionComponent2D&& collision) = default; @@ -40,7 +45,10 @@ namespace Ndk private: void InitializeStaticBody(); + Nz::RigidBody2D* GetRigidBody(); + const Nz::RigidBody2D* GetRigidBody() const; Nz::RigidBody2D* GetStaticBody(); + const Nz::RigidBody2D* GetStaticBody() const; void OnAttached() override; void OnComponentAttached(BaseComponent& component) override; diff --git a/SDK/include/NDK/Components/CollisionComponent2D.inl b/SDK/include/NDK/Components/CollisionComponent2D.inl index b85fe3017..1bc9f0978 100644 --- a/SDK/include/NDK/Components/CollisionComponent2D.inl +++ b/SDK/include/NDK/Components/CollisionComponent2D.inl @@ -28,16 +28,6 @@ namespace Ndk { } - /*! - * \brief Gets the collision box representing the entity - * \return The physics collision box - */ - - inline Nz::Rectf CollisionComponent2D::GetAABB() const - { - return m_staticBody->GetAABB(); - } - /*! * \brief Gets the geometry representing the entity * \return A constant reference to the physics geometry @@ -62,13 +52,13 @@ namespace Ndk return *this; } - /*! - * \brief Gets the static body used by the entity - * \return A pointer to the entity - */ - inline Nz::RigidBody2D* CollisionComponent2D::GetStaticBody() { return m_staticBody.get(); } + + inline const Nz::RigidBody2D* CollisionComponent2D::GetStaticBody() const + { + return m_staticBody.get(); + } } diff --git a/SDK/include/NDK/Components/DebugComponent.hpp b/SDK/include/NDK/Components/DebugComponent.hpp index 206f85724..0777a9812 100644 --- a/SDK/include/NDK/Components/DebugComponent.hpp +++ b/SDK/include/NDK/Components/DebugComponent.hpp @@ -16,7 +16,7 @@ namespace Ndk { enum class DebugDraw { - //TODO: Collider2D + Collider2D, Collider3D, GraphicsAABB, GraphicsOBB, diff --git a/SDK/include/NDK/Components/LifetimeComponent.hpp b/SDK/include/NDK/Components/LifetimeComponent.hpp new file mode 100644 index 000000000..72004a90d --- /dev/null +++ b/SDK/include/NDK/Components/LifetimeComponent.hpp @@ -0,0 +1,40 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#pragma once + +#ifndef NDK_COMPONENTS_LIFETIMECOMPONENT_HPP +#define NDK_COMPONENTS_LIFETIMECOMPONENT_HPP + +#include + +namespace Ndk +{ + class LifetimeComponent; + + using LifetimeComponentHandle = Nz::ObjectHandle; + + class NDK_API LifetimeComponent : public Component + { + friend class LifetimeSystem; + + public: + inline LifetimeComponent(float lifetime); + LifetimeComponent(const LifetimeComponent&) = default; + ~LifetimeComponent() = default; + + inline float GetRemainingTime() const; + + static ComponentIndex componentIndex; + + private: + inline bool UpdateLifetime(float elapsedTime); + + float m_lifetime; + }; +} + +#include + +#endif // NDK_COMPONENTS_LIFETIMECOMPONENT_HPP diff --git a/SDK/include/NDK/Components/LifetimeComponent.inl b/SDK/include/NDK/Components/LifetimeComponent.inl new file mode 100644 index 000000000..145e160ba --- /dev/null +++ b/SDK/include/NDK/Components/LifetimeComponent.inl @@ -0,0 +1,24 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#include + +namespace Ndk +{ + inline LifetimeComponent::LifetimeComponent(float lifetime) : + m_lifetime(lifetime) + { + } + + inline float Ndk::LifetimeComponent::GetRemainingTime() const + { + return m_lifetime; + } + + inline bool LifetimeComponent::UpdateLifetime(float elapsedTime) + { + m_lifetime -= elapsedTime; + return m_lifetime < 0.f; + } +} diff --git a/SDK/include/NDK/Components/PhysicsComponent2D.hpp b/SDK/include/NDK/Components/PhysicsComponent2D.hpp index 696a8f99d..93383d707 100644 --- a/SDK/include/NDK/Components/PhysicsComponent2D.hpp +++ b/SDK/include/NDK/Components/PhysicsComponent2D.hpp @@ -24,6 +24,8 @@ namespace Ndk friend class ConstraintComponent2D; public: + using VelocityFunc = Nz::RigidBody2D::VelocityFunc; + PhysicsComponent2D(); PhysicsComponent2D(const PhysicsComponent2D& physics); ~PhysicsComponent2D() = default; @@ -38,6 +40,8 @@ namespace Ndk inline void EnableNodeSynchronization(bool nodeSynchronization); + inline void ForEachArbiter(const std::function& callback); + inline Nz::Rectf GetAABB() const; inline float GetAngularDamping() const; inline Nz::RadianAnglef GetAngularVelocity() const; @@ -53,9 +57,13 @@ namespace Ndk inline Nz::Vector2f GetSurfaceVelocity(std::size_t shapeIndex = 0) const; inline std::size_t GetShapeCount() const; inline Nz::Vector2f GetVelocity() const; + const VelocityFunc& GetVelocityFunction() const; inline bool IsNodeSynchronizationEnabled() const; inline bool IsSleeping() const; + inline bool IsValid() const; + + inline void ResetVelocityFunction(); inline void SetAngularDamping(float angularDamping); inline void SetAngularVelocity(const Nz::RadianAnglef& angularVelocity); @@ -71,6 +79,9 @@ namespace Ndk inline void SetSurfaceVelocity(const Nz::Vector2f& velocity); inline void SetSurfaceVelocity(std::size_t shapeIndex, const Nz::Vector2f& velocity); inline void SetVelocity(const Nz::Vector2f& velocity); + inline void SetVelocityFunction(VelocityFunc velocityFunc); + + inline void UpdateVelocity(const Nz::Vector2f& gravity, float damping, float deltaTime); static ComponentIndex componentIndex; diff --git a/SDK/include/NDK/Components/PhysicsComponent2D.inl b/SDK/include/NDK/Components/PhysicsComponent2D.inl index dd90b5f9a..f30dd8618 100644 --- a/SDK/include/NDK/Components/PhysicsComponent2D.inl +++ b/SDK/include/NDK/Components/PhysicsComponent2D.inl @@ -138,6 +138,15 @@ namespace Ndk m_entity->Invalidate(); } + /*! + TODO + */ + inline void PhysicsComponent2D::ForEachArbiter(const std::function& callback) + { + NazaraAssert(m_object, "Invalid physics object"); + + return m_object->ForEachArbiter(callback); + } /*! * \brief Gets the AABB of the physics object * \return AABB of the object @@ -329,7 +338,6 @@ namespace Ndk * * \remark Produces a NazaraAssert if the physics object is invalid */ - inline Nz::Vector2f PhysicsComponent2D::GetVelocity() const { NazaraAssert(m_object, "Invalid physics object"); @@ -337,6 +345,19 @@ namespace Ndk return m_object->GetVelocity(); } + /*! + * \brief Gets the custom velocity function of the physics object + * \return Velocity function of the object (may be empty if default function is used) + * + * \remark Produces a NazaraAssert if the physics object is invalid + */ + inline auto PhysicsComponent2D::GetVelocityFunction() const -> const VelocityFunc& + { + NazaraAssert(m_object, "Invalid physics object"); + + return m_object->GetVelocityFunction(); + } + /*! * \brief Checks if position & rotation are synchronized with NodeComponent * \return true If synchronization is enabled @@ -361,6 +382,30 @@ namespace Ndk return m_object->IsSleeping(); } + /*! + * \brief Checks if this component is bound to a valid rigid body + * + * A component may not be bound to a rigid body if the component is not bound to an entity or if this entity is being destroyed + * + * \return true If bound, false otherwise + */ + inline bool PhysicsComponent2D::IsValid() const + { + return bool(m_object); + } + + /*! + * \brief Reset velocity function to default one + * + * \remark Produces a NazaraAssert if the physics object is invalid + */ + inline void PhysicsComponent2D::ResetVelocityFunction() + { + NazaraAssert(m_object, "Invalid physics object"); + + return m_object->ResetVelocityFunction(); + } + /*! * \brief Sets the angular damping or moment of inertia of the physics object * @@ -571,6 +616,39 @@ namespace Ndk m_object->SetVelocity(velocity); } + /*! + * \brief Sets a custom velocity function for the physics object + * + * A velocity function is called (for non-kinematic and non-static objects) at every physics update to compute the new velocity of the object. + * You may call UpdateVelocity (the default velocity function) to let the physics engine compute that itself and then adjust it using GetVelocity/SetVelocity as you need. + * + * \param velocityFunc New custom velocity function + * + * \remark Passing an empty VelocityFunc has the same effect as calling ResetVelocityFunction + * \see ResetVelocityFunction + * \see UpdateVelocity + */ + inline void PhysicsComponent2D::SetVelocityFunction(VelocityFunc velocityFunc) + { + NazaraAssert(m_object, "Invalid physics object"); + + m_object->SetVelocityFunction(std::move(velocityFunc)); + } + + /*! + * \brief Calls the physics engine default velocity function + * + * \param gravity Physics system gravity + * \param damping Physics system damping (adjusted to deltaTime) + * \param deltaTime Elapsed time since last physics update + */ + inline void PhysicsComponent2D::UpdateVelocity(const Nz::Vector2f& gravity, float damping, float deltaTime) + { + NazaraAssert(m_object, "Invalid physics object"); + + m_object->UpdateVelocity(gravity, damping, deltaTime); + } + /*! * \brief Gets the underlying physics object * \return A reference to the physics object diff --git a/SDK/include/NDK/Components/VelocityComponent.hpp b/SDK/include/NDK/Components/VelocityComponent.hpp index 8785f6861..2c619da76 100644 --- a/SDK/include/NDK/Components/VelocityComponent.hpp +++ b/SDK/include/NDK/Components/VelocityComponent.hpp @@ -19,10 +19,11 @@ namespace Ndk class NDK_API VelocityComponent : public Component { public: - VelocityComponent(const Nz::Vector3f& velocity = Nz::Vector3f::Zero()); + VelocityComponent(const Nz::Vector3f& velocity = Nz::Vector3f::Zero(), Nz::CoordSys coordSystem = Nz::CoordSys_Global); ~VelocityComponent() = default; Nz::Vector3f linearVelocity; + Nz::CoordSys coordSys; VelocityComponent& operator=(const Nz::Vector3f& vel); diff --git a/SDK/include/NDK/Components/VelocityComponent.inl b/SDK/include/NDK/Components/VelocityComponent.inl index 3ca449132..2adedf413 100644 --- a/SDK/include/NDK/Components/VelocityComponent.inl +++ b/SDK/include/NDK/Components/VelocityComponent.inl @@ -16,8 +16,9 @@ namespace Ndk * \param velocity Linear velocity */ - inline VelocityComponent::VelocityComponent(const Nz::Vector3f& velocity) : - linearVelocity(velocity) + inline VelocityComponent::VelocityComponent(const Nz::Vector3f& velocity, Nz::CoordSys coordSystem) : + linearVelocity(velocity), + coordSys(coordSystem) { } diff --git a/SDK/include/NDK/StateMachine.inl b/SDK/include/NDK/StateMachine.inl index 1b2a625ab..ba2a74928 100644 --- a/SDK/include/NDK/StateMachine.inl +++ b/SDK/include/NDK/StateMachine.inl @@ -159,8 +159,13 @@ namespace Ndk */ inline bool StateMachine::Update(float elapsedTime) { - for (StateTransition& transition : m_transitions) + // Use a classic for instead of a range-for because some state may push/pop on enter/leave, adding new transitions as we iterate + // (range-for is a problem here because it doesn't handle mutable containers) + + for (std::size_t i = 0; i < m_transitions.size(); ++i) { + StateTransition& transition = m_transitions[i]; + switch (transition.type) { case TransitionType::Pop: diff --git a/SDK/include/NDK/Systems.hpp b/SDK/include/NDK/Systems.hpp index 80571ef45..e9a60f0c3 100644 --- a/SDK/include/NDK/Systems.hpp +++ b/SDK/include/NDK/Systems.hpp @@ -6,6 +6,7 @@ #define NDK_SYSTEMS_GLOBAL_HPP #include +#include #include #include #include diff --git a/SDK/include/NDK/Systems/DebugSystem.hpp b/SDK/include/NDK/Systems/DebugSystem.hpp index 3dd186f36..362b59f32 100644 --- a/SDK/include/NDK/Systems/DebugSystem.hpp +++ b/SDK/include/NDK/Systems/DebugSystem.hpp @@ -26,6 +26,7 @@ namespace Ndk private: Nz::InstancedRenderableRef GenerateBox(Nz::Boxf box); + Nz::InstancedRenderableRef GenerateCollision2DMesh(Entity* entity, Nz::Vector3f* offset); Nz::InstancedRenderableRef GenerateCollision3DMesh(Entity* entity); Nz::MaterialRef GetCollisionMaterial(); diff --git a/SDK/include/NDK/Systems/LifetimeSystem.hpp b/SDK/include/NDK/Systems/LifetimeSystem.hpp new file mode 100644 index 000000000..9957c1b53 --- /dev/null +++ b/SDK/include/NDK/Systems/LifetimeSystem.hpp @@ -0,0 +1,29 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#pragma once + +#ifndef NDK_SYSTEMS_LIFETIMESYSTEM_HPP +#define NDK_SYSTEMS_LIFETIMESYSTEM_HPP + +#include + +namespace Ndk +{ + class NDK_API LifetimeSystem : public System + { + public: + LifetimeSystem(); + ~LifetimeSystem() = default; + + static SystemIndex systemIndex; + + private: + void OnUpdate(float elapsedTime) override; + }; +} + +#include + +#endif // NDK_SYSTEMS_LIFETIMESYSTEM_HPP diff --git a/SDK/include/NDK/Systems/LifetimeSystem.inl b/SDK/include/NDK/Systems/LifetimeSystem.inl new file mode 100644 index 000000000..5302ce8d0 --- /dev/null +++ b/SDK/include/NDK/Systems/LifetimeSystem.inl @@ -0,0 +1,3 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp diff --git a/SDK/include/NDK/Systems/PhysicsSystem2D.hpp b/SDK/include/NDK/Systems/PhysicsSystem2D.hpp index a919abbd5..35579862a 100644 --- a/SDK/include/NDK/Systems/PhysicsSystem2D.hpp +++ b/SDK/include/NDK/Systems/PhysicsSystem2D.hpp @@ -51,9 +51,11 @@ namespace Ndk bool NearestBodyQuery(const Nz::Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, EntityHandle* nearestBody = nullptr); bool NearestBodyQuery(const Nz::Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, NearestQueryResult* result); + void RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, const std::function& callback); bool RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector* hitInfos); bool RaycastQueryFirst(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RaycastHit* hitInfo = nullptr); + void RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, const std::function& callback); void RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector* bodies); void RegisterCallbacks(unsigned int collisionId, Callback callbacks); diff --git a/SDK/include/NDK/Widgets.hpp b/SDK/include/NDK/Widgets.hpp index 26917a4c9..a4b74b2d6 100644 --- a/SDK/include/NDK/Widgets.hpp +++ b/SDK/include/NDK/Widgets.hpp @@ -5,6 +5,7 @@ #ifndef NDK_WIDGETS_GLOBAL_HPP #define NDK_WIDGETS_GLOBAL_HPP +#include #include #include #include diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.hpp b/SDK/include/NDK/Widgets/TextAreaWidget.hpp index 159412568..bb121c5f0 100644 --- a/SDK/include/NDK/Widgets/TextAreaWidget.hpp +++ b/SDK/include/NDK/Widgets/TextAreaWidget.hpp @@ -50,6 +50,8 @@ namespace Ndk inline std::size_t GetGlyphIndex(const Nz::Vector2ui& cursorPosition) const; inline const Nz::String& GetText() const; inline const Nz::Color& GetTextColor() const; + inline const Nz::Color& GetTextOulineColor() const; + inline float GetTextOulineThickness() const; Nz::Vector2ui GetHoveredGlyph(float x, float y) const; @@ -71,6 +73,8 @@ namespace Ndk inline void SetSelection(Nz::Vector2ui fromPosition, Nz::Vector2ui toPosition); inline void SetText(const Nz::String& text); inline void SetTextColor(const Nz::Color& text); + inline void SetTextOutlineColor(const Nz::Color& color); + inline void SetTextOutlineThickness(float thickness); inline void Write(const Nz::String& text); inline void Write(const Nz::String& text, const Nz::Vector2ui& glyphPosition); diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.inl b/SDK/include/NDK/Widgets/TextAreaWidget.inl index 2a7234b79..e5dbced4a 100644 --- a/SDK/include/NDK/Widgets/TextAreaWidget.inl +++ b/SDK/include/NDK/Widgets/TextAreaWidget.inl @@ -107,6 +107,16 @@ namespace Ndk return m_drawer.GetColor(); } + inline const Nz::Color& TextAreaWidget::GetTextOulineColor() const + { + return m_drawer.GetOutlineColor(); + } + + inline float TextAreaWidget::GetTextOulineThickness() const + { + return m_drawer.GetOutlineThickness(); + } + inline bool TextAreaWidget::HasSelection() const { return m_cursorPositionBegin != m_cursorPositionEnd; @@ -246,7 +256,21 @@ namespace Ndk { m_drawer.SetColor(text); - m_textSprite->Update(m_drawer); + UpdateDisplayText(); + } + + inline void TextAreaWidget::SetTextOutlineColor(const Nz::Color& color) + { + m_drawer.SetOutlineColor(color); + + UpdateDisplayText(); + } + + inline void TextAreaWidget::SetTextOutlineThickness(float thickness) + { + m_drawer.SetOutlineThickness(thickness); + + UpdateDisplayText(); } inline void TextAreaWidget::Write(const Nz::String& text) diff --git a/SDK/include/NDK/World.hpp b/SDK/include/NDK/World.hpp index 98e6d87ac..891c3df11 100644 --- a/SDK/include/NDK/World.hpp +++ b/SDK/include/NDK/World.hpp @@ -98,6 +98,12 @@ namespace Ndk inline void InvalidateSystemOrder(); void ReorderSystems(); + struct DoubleBitset + { + Nz::Bitset front; + Nz::Bitset back; + }; + struct EntityBlock { EntityBlock(Entity&& e) : @@ -119,9 +125,9 @@ namespace Ndk std::vector> m_waitingEntities; EntityList m_aliveEntities; ProfilerData m_profilerData; - Nz::Bitset m_dirtyEntities; + DoubleBitset m_dirtyEntities; Nz::Bitset m_freeEntityIds; - Nz::Bitset m_killedEntities; + DoubleBitset m_killedEntities; bool m_orderedSystemsUpdated; bool m_isProfilerEnabled; }; diff --git a/SDK/include/NDK/World.inl b/SDK/include/NDK/World.inl index 860454a0d..1c3e446e1 100644 --- a/SDK/include/NDK/World.inl +++ b/SDK/include/NDK/World.inl @@ -308,7 +308,7 @@ namespace Ndk inline void World::KillEntity(Entity* entity) { if (IsEntityValid(entity)) - m_killedEntities.UnboundedSet(entity->GetId(), true); + m_killedEntities.front.UnboundedSet(entity->GetId(), true); } /*! @@ -343,7 +343,7 @@ namespace Ndk */ inline bool World::IsEntityDying(EntityId id) const { - return m_killedEntities.UnboundedTest(id); + return m_killedEntities.front.UnboundedTest(id); } /*! @@ -467,13 +467,13 @@ namespace Ndk inline void World::Invalidate() { - m_dirtyEntities.Resize(m_entityBlocks.size(), false); - m_dirtyEntities.Set(true); // Activation of all bits + m_dirtyEntities.front.Resize(m_entityBlocks.size(), false); + m_dirtyEntities.front.Set(true); // Activation of all bits } inline void World::Invalidate(EntityId id) { - m_dirtyEntities.UnboundedSet(id, true); + m_dirtyEntities.front.UnboundedSet(id, true); } inline void World::InvalidateSystemOrder() diff --git a/SDK/src/NDK/BaseWidget.cpp b/SDK/src/NDK/BaseWidget.cpp index 89ca6bb9c..499a7aa2b 100644 --- a/SDK/src/NDK/BaseWidget.cpp +++ b/SDK/src/NDK/BaseWidget.cpp @@ -89,6 +89,7 @@ namespace Ndk } else { + DestroyEntity(m_backgroundEntity); m_backgroundEntity.Reset(); m_backgroundSprite.Reset(); } @@ -185,7 +186,7 @@ namespace Ndk void BaseWidget::Layout() { - if (m_backgroundEntity) + if (m_backgroundSprite) m_backgroundSprite->SetSize(m_size.x, m_size.y); UpdatePositionAndSize(); diff --git a/SDK/src/NDK/Components/CollisionComponent2D.cpp b/SDK/src/NDK/Components/CollisionComponent2D.cpp index 5cdfd1589..fddbe8842 100644 --- a/SDK/src/NDK/Components/CollisionComponent2D.cpp +++ b/SDK/src/NDK/Components/CollisionComponent2D.cpp @@ -17,28 +17,57 @@ namespace Ndk * \brief NDK class that represents a two-dimensional collision geometry */ + /*! + * \brief Gets the collision box representing the entity + * \return The physics collision box + */ + Nz::Rectf CollisionComponent2D::GetAABB() const + { + return GetRigidBody()->GetAABB(); + } + + /*! + * \brief Gets the position offset between the actual rigid body center of mass position and the origin of the geometry + * \return Position offset + */ + const Nz::Vector2f& CollisionComponent2D::GetGeomOffset() const + { + return GetRigidBody()->GetPositionOffset(); + } + + /*! + * \brief Convenience function to align center of geometry to a specific point + * + * \param geomOffset Position offset + * + * \remark This does not change the center of mass + */ + void CollisionComponent2D::Recenter(const Nz::Vector2f& origin) + { + const Nz::RigidBody2D* rigidBody = GetRigidBody(); + SetGeomOffset(origin - rigidBody->GetAABB().GetCenter() + rigidBody->GetPositionOffset()); + } + /*! * \brief Sets geometry for the entity * * \param geom Geometry used for collisions - * - * \remark Produces a NazaraAssert if the entity has no physics component and has no static body */ void CollisionComponent2D::SetGeom(Nz::Collider2DRef geom) { m_geom = std::move(geom); - if (m_entity->HasComponent()) - { - // We update the geometry of the PhysiscsObject linked to the PhysicsComponent2D - PhysicsComponent2D& physComponent = m_entity->GetComponent(); - physComponent.GetRigidBody()->SetGeom(m_geom); - } - else - { - NazaraAssert(m_staticBody, "An entity without physics component should have a static body"); - m_staticBody->SetGeom(m_geom); - } + GetRigidBody()->SetGeom(m_geom); + } + + /*! + * \brief Sets the position offset between the actual rigid body center of mass position and the origin of the geometry + * + * \param geomOffset Position offset + */ + void CollisionComponent2D::SetGeomOffset(const Nz::Vector2f& geomOffset) + { + GetRigidBody()->SetPositionOffset(geomOffset); } /*! @@ -47,7 +76,6 @@ namespace Ndk * \remark Produces a NazaraAssert if entity is invalid * \remark Produces a NazaraAssert if entity is not linked to a world, or the world has no physics system */ - void CollisionComponent2D::InitializeStaticBody() { NazaraAssert(m_entity, "Invalid entity"); @@ -67,7 +95,34 @@ namespace Ndk matrix.MakeIdentity(); m_staticBody->SetPosition(Nz::Vector2f(matrix.GetTranslation())); + } + Nz::RigidBody2D* CollisionComponent2D::GetRigidBody() + { + if (m_entity->HasComponent()) + { + PhysicsComponent2D& physComponent = m_entity->GetComponent(); + return physComponent.GetRigidBody(); + } + else + { + NazaraAssert(m_staticBody, "An entity without physics component should have a static body"); + return m_staticBody.get(); + } + } + + const Nz::RigidBody2D* CollisionComponent2D::GetRigidBody() const + { + if (m_entity->HasComponent()) + { + PhysicsComponent2D& physComponent = m_entity->GetComponent(); + return physComponent.GetRigidBody(); + } + else + { + NazaraAssert(m_staticBody, "An entity without physics component should have a static body"); + return m_staticBody.get(); + } } /*! diff --git a/SDK/src/NDK/Components/GraphicsComponent.cpp b/SDK/src/NDK/Components/GraphicsComponent.cpp index 5182e2d2d..43bfa3b54 100644 --- a/SDK/src/NDK/Components/GraphicsComponent.cpp +++ b/SDK/src/NDK/Components/GraphicsComponent.cpp @@ -124,9 +124,11 @@ namespace Ndk const Nz::MaterialRef& oldMat = renderable->GetMaterial(skinIndex, matIndex); UnregisterMaterial(oldMat); + + ForceCullingInvalidation(); } - void Ndk::GraphicsComponent::InvalidateReflectionMap() + void GraphicsComponent::InvalidateReflectionMap() { m_entity->Invalidate(); @@ -230,6 +232,8 @@ namespace Ndk std::size_t materialCount = renderable->GetMaterialCount(); for (std::size_t i = 0; i < materialCount; ++i) UnregisterMaterial(renderable->GetMaterial(i)); + + ForceCullingInvalidation(); } void GraphicsComponent::OnInstancedRenderableSkinChange(const Nz::InstancedRenderable* renderable, std::size_t newSkinIndex) @@ -240,6 +244,8 @@ namespace Ndk for (std::size_t i = 0; i < materialCount; ++i) UnregisterMaterial(renderable->GetMaterial(i)); + + ForceCullingInvalidation(); } void GraphicsComponent::OnMaterialReflectionChange(const Nz::Material* material, Nz::ReflectionMode reflectionMode) @@ -297,6 +303,9 @@ namespace Ndk RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem(); m_aabb.Set(-1.f, -1.f, -1.f); + + bool isAabbSet = false; + for (const Renderable& r : m_renderables) { r.boundingVolume = r.renderable->GetBoundingVolume(); @@ -305,10 +314,13 @@ namespace Ndk { r.boundingVolume.Update(r.data.transformMatrix); - if (m_aabb.IsValid()) + if (isAabbSet) m_aabb.ExtendTo(r.boundingVolume.aabb); else + { m_aabb.Set(r.boundingVolume.aabb); + isAabbSet = true; + } } } diff --git a/SDK/src/NDK/Components/LifetimeComponent.cpp b/SDK/src/NDK/Components/LifetimeComponent.cpp new file mode 100644 index 000000000..5ffa6da82 --- /dev/null +++ b/SDK/src/NDK/Components/LifetimeComponent.cpp @@ -0,0 +1,10 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#include + +namespace Ndk +{ + ComponentIndex LifetimeComponent::componentIndex; +} diff --git a/SDK/src/NDK/Components/PhysicsComponent2D.cpp b/SDK/src/NDK/Components/PhysicsComponent2D.cpp index 62cd922a4..f9feaff6e 100644 --- a/SDK/src/NDK/Components/PhysicsComponent2D.cpp +++ b/SDK/src/NDK/Components/PhysicsComponent2D.cpp @@ -31,9 +31,17 @@ namespace Ndk Nz::PhysWorld2D& world = entityWorld->GetSystem().GetPhysWorld(); + Nz::Vector2f positionOffset; + Nz::Collider2DRef geom; if (m_entity->HasComponent()) - geom = m_entity->GetComponent().GetGeom(); + { + const CollisionComponent2D& entityCollision = m_entity->GetComponent(); + geom = entityCollision.GetGeom(); + positionOffset = entityCollision.GetStaticBody()->GetPositionOffset(); //< Calling GetGeomOffset would retrieve current component which is not yet initialized + } + else + positionOffset = Nz::Vector2f::Zero(); Nz::Matrix4f matrix; if (m_entity->HasComponent()) @@ -42,6 +50,7 @@ namespace Ndk matrix.MakeIdentity(); m_object = std::make_unique(&world, 1.f, geom); + m_object->SetPositionOffset(positionOffset); m_object->SetPosition(Nz::Vector2f(matrix.GetTranslation())); m_object->SetUserdata(reinterpret_cast(static_cast(m_entity->GetId()))); } diff --git a/SDK/src/NDK/Lua/LuaBinding_Utility.cpp b/SDK/src/NDK/Lua/LuaBinding_Utility.cpp index 43ea4a8c7..262620b12 100644 --- a/SDK/src/NDK/Lua/LuaBinding_Utility.cpp +++ b/SDK/src/NDK/Lua/LuaBinding_Utility.cpp @@ -123,12 +123,13 @@ namespace Ndk lua.Push(instance->GetCachedGlyphCount()); return 1; - case 2: + case 3: { unsigned int characterSize = lua.Check(&argIndex); - Nz::UInt32 style = lua.Check(&argIndex); + Nz::TextStyleFlags style = lua.Check(&argIndex); + float outlineThickness = lua.Check(&argIndex); - lua.Push(instance->GetCachedGlyphCount(characterSize, style)); + lua.Push(instance->GetCachedGlyphCount(characterSize, style, outlineThickness)); return 1; } } @@ -146,7 +147,7 @@ namespace Ndk font.BindMethod("IsValid", &Nz::Font::IsValid); - font.BindMethod("Precache", (bool(Nz::Font::*)(unsigned int, Nz::UInt32, const Nz::String&) const) &Nz::Font::Precache); + font.BindMethod("Precache", (bool(Nz::Font::*)(unsigned int, Nz::TextStyleFlags, float, const Nz::String&) const) &Nz::Font::Precache); font.BindMethod("SetGlyphBorder", &Nz::Font::SetGlyphBorder); font.BindMethod("SetMinimumStepSize", &Nz::Font::SetMinimumStepSize); diff --git a/SDK/src/NDK/Sdk.cpp b/SDK/src/NDK/Sdk.cpp index 90d07c738..341e6f0d5 100644 --- a/SDK/src/NDK/Sdk.cpp +++ b/SDK/src/NDK/Sdk.cpp @@ -17,11 +17,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -88,6 +90,7 @@ namespace Ndk // Shared components InitializeComponent("NdkColl2"); InitializeComponent("NdkColl3"); + InitializeComponent("NdkLiftm"); InitializeComponent("NdkNode"); InitializeComponent("NdkPhys2"); InitializeComponent("NdkPhys3"); @@ -110,6 +113,7 @@ namespace Ndk BaseSystem::Initialize(); // Shared systems + InitializeSystem(); InitializeSystem(); InitializeSystem(); InitializeSystem(); diff --git a/SDK/src/NDK/Systems/DebugSystem.cpp b/SDK/src/NDK/Systems/DebugSystem.cpp index 063a25d77..d8c61b9bc 100644 --- a/SDK/src/NDK/Systems/DebugSystem.cpp +++ b/SDK/src/NDK/Systems/DebugSystem.cpp @@ -8,10 +8,12 @@ #include #include #include +#include #include #include #include #include +#include namespace Ndk { @@ -227,17 +229,24 @@ namespace Ndk { switch (option) { + case DebugDraw::Collider2D: + { + Nz::Vector3f offset; + Nz::InstancedRenderableRef renderable = GenerateCollision2DMesh(entity, &offset); + if (renderable) + entityGfx.Attach(renderable, Nz::Matrix4f::Translate(offset), DebugDrawOrder); + + entityDebug.UpdateDebugRenderable(option, std::move(renderable)); + break; + } + case DebugDraw::Collider3D: { const Nz::Boxf& obb = entityGfx.GetAABB(); Nz::InstancedRenderableRef renderable = GenerateCollision3DMesh(entity); if (renderable) - { - renderable->SetPersistent(false); - entityGfx.Attach(renderable, Nz::Matrix4f::Translate(obb.GetCenter() - entityNode.GetPosition()), DebugDrawOrder); - } entityDebug.UpdateDebugRenderable(option, std::move(renderable)); break; @@ -305,6 +314,76 @@ namespace Ndk return model; } + + Nz::InstancedRenderableRef DebugSystem::GenerateCollision2DMesh(Entity* entity, Nz::Vector3f* offset) + { + if (entity->HasComponent()) + { + CollisionComponent2D& entityCollision = entity->GetComponent(); + const Nz::Collider2DRef& geom = entityCollision.GetGeom(); + + std::vector vertices; + std::vector indices; + + geom->ForEachPolygon([&](const Nz::Vector2f* polygonVertices, std::size_t vertexCount) + { + std::size_t firstIndex = vertices.size(); + + // Don't reserve and let the vector handle its own capacity + for (std::size_t i = 0; i < vertexCount; ++i) + vertices.emplace_back(*polygonVertices++); + + for (std::size_t i = 0; i < vertexCount - 1; ++i) + { + indices.push_back(firstIndex + i); + indices.push_back(firstIndex + i + 1); + } + + indices.push_back(firstIndex + vertexCount - 1); + indices.push_back(firstIndex); + }); + + Nz::IndexBufferRef indexBuffer = Nz::IndexBuffer::New(vertices.size() > 0xFFFF, Nz::UInt32(indices.size()), Nz::DataStorage_Hardware, 0); + Nz::IndexMapper indexMapper(indexBuffer, Nz::BufferAccess_WriteOnly); + + Nz::IndexIterator indexPtr = indexMapper.begin(); + for (std::size_t index : indices) + *indexPtr++ = static_cast(index); + + indexMapper.Unmap(); + + Nz::VertexBufferRef vertexBuffer = Nz::VertexBuffer::New(Nz::VertexDeclaration::Get(Nz::VertexLayout_XYZ), Nz::UInt32(vertices.size()), Nz::DataStorage_Hardware, 0); + vertexBuffer->Fill(vertices.data(), 0, Nz::UInt32(vertices.size())); + + Nz::MeshRef mesh = Nz::Mesh::New(); + mesh->CreateStatic(); + + Nz::StaticMeshRef subMesh = Nz::StaticMesh::New(vertexBuffer, indexBuffer); + subMesh->SetPrimitiveMode(Nz::PrimitiveMode_LineList); + subMesh->SetMaterialIndex(0); + subMesh->GenerateAABB(); + + mesh->SetMaterialCount(1); + mesh->AddSubMesh(subMesh); + + Nz::ModelRef model = Nz::Model::New(); + model->SetMesh(mesh); + model->SetMaterial(0, GetCollisionMaterial()); + + // Find center of mass + if (entity->HasComponent()) + { + const PhysicsComponent2D& entityPhys = entity->GetComponent(); + *offset = entityPhys.GetMassCenter(Nz::CoordSys_Local) + entityCollision.GetGeomOffset(); + } + else + *offset = entityCollision.GetGeomOffset(); + + return model; + } + else + return nullptr; + } Nz::InstancedRenderableRef DebugSystem::GenerateCollision3DMesh(Entity* entity) { @@ -315,16 +394,12 @@ namespace Ndk std::vector vertices; std::vector indices; - - geom->ForEachPolygon([&](const float* polygonVertices, std::size_t vertexCount) + + geom->ForEachPolygon([&](const Nz::Vector3f* polygonVertices, std::size_t vertexCount) { std::size_t firstIndex = vertices.size(); - - for (std::size_t i = 0; i < vertexCount; ++i) - { - const float* vertexData = &polygonVertices[i * 3]; - vertices.emplace_back(vertexData[0], vertexData[1], vertexData[2]); - } + vertices.resize(firstIndex + vertexCount); + std::copy(polygonVertices, polygonVertices + vertexCount, &vertices[firstIndex]); for (std::size_t i = 0; i < vertexCount - 1; ++i) { @@ -378,7 +453,7 @@ namespace Ndk m_globalAabbMaterial->EnableDepthBuffer(true); m_globalAabbMaterial->SetDiffuseColor(Nz::Color::Orange); m_globalAabbMaterial->SetFaceFilling(Nz::FaceFilling_Line); - m_globalAabbMaterial->SetLineWidth(2.f); + //m_globalAabbMaterial->SetLineWidth(2.f); } return m_globalAabbMaterial; @@ -393,7 +468,7 @@ namespace Ndk m_localAabbMaterial->EnableDepthBuffer(true); m_localAabbMaterial->SetDiffuseColor(Nz::Color::Red); m_localAabbMaterial->SetFaceFilling(Nz::FaceFilling_Line); - m_localAabbMaterial->SetLineWidth(2.f); + //m_localAabbMaterial->SetLineWidth(2.f); } return m_localAabbMaterial; @@ -408,7 +483,7 @@ namespace Ndk m_collisionMaterial->EnableDepthBuffer(true); m_collisionMaterial->SetDiffuseColor(Nz::Color::Blue); m_collisionMaterial->SetFaceFilling(Nz::FaceFilling_Line); - m_collisionMaterial->SetLineWidth(2.f); + //m_collisionMaterial->SetLineWidth(2.f); } return m_collisionMaterial; @@ -423,7 +498,7 @@ namespace Ndk m_obbMaterial->EnableDepthBuffer(true); m_obbMaterial->SetDiffuseColor(Nz::Color::Green); m_obbMaterial->SetFaceFilling(Nz::FaceFilling_Line); - m_obbMaterial->SetLineWidth(2.f); + //m_obbMaterial->SetLineWidth(2.f); } return m_obbMaterial; diff --git a/SDK/src/NDK/Systems/LifetimeSystem.cpp b/SDK/src/NDK/Systems/LifetimeSystem.cpp new file mode 100644 index 000000000..d575ebdc4 --- /dev/null +++ b/SDK/src/NDK/Systems/LifetimeSystem.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#include +#include + +namespace Ndk +{ + LifetimeSystem::LifetimeSystem() + { + Requires(); + } + + void LifetimeSystem::OnUpdate(float elapsedTime) + { + for (const Ndk::EntityHandle& entity : GetEntities()) + { + auto& lifetime = entity->GetComponent(); + + if (lifetime.UpdateLifetime(elapsedTime)) + entity->Kill(); + } + } + + SystemIndex LifetimeSystem::systemIndex; +} diff --git a/SDK/src/NDK/Systems/PhysicsSystem2D.cpp b/SDK/src/NDK/Systems/PhysicsSystem2D.cpp index 6579bb1fb..891b5b8a3 100644 --- a/SDK/src/NDK/Systems/PhysicsSystem2D.cpp +++ b/SDK/src/NDK/Systems/PhysicsSystem2D.cpp @@ -86,14 +86,30 @@ namespace Ndk bool PhysicsSystem2D::NearestBodyQuery(const Nz::Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, NearestQueryResult* result) { Nz::PhysWorld2D::NearestQueryResult queryResult; - bool res = GetPhysWorld().NearestBodyQuery(from, maxDistance, collisionGroup, categoryMask, collisionMask, &queryResult); + if (GetPhysWorld().NearestBodyQuery(from, maxDistance, collisionGroup, categoryMask, collisionMask, &queryResult)) + { + result->nearestBody = GetEntityFromBody(*queryResult.nearestBody); + result->closestPoint = std::move(queryResult.closestPoint); + result->fraction = std::move(queryResult.fraction); + result->distance = queryResult.distance; - result->nearestBody = GetEntityFromBody(*queryResult.nearestBody); - result->closestPoint = std::move(queryResult.closestPoint); - result->fraction = std::move(queryResult.fraction); - result->distance = queryResult.distance; + return true; + } + else + return false; + } - return res; + void PhysicsSystem2D::RaycastQuery(const Nz::Vector2f & from, const Nz::Vector2f & to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, const std::function& callback) + { + return GetPhysWorld().RaycastQuery(from, to, radius, collisionGroup, categoryMask, collisionMask, [this, &callback](const Nz::PhysWorld2D::RaycastHit& hitInfo) + { + callback({ + GetEntityFromBody(*hitInfo.nearestBody), + hitInfo.hitPos, + hitInfo.hitNormal, + hitInfo.fraction + }); + }); } bool PhysicsSystem2D::RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector* hitInfos) @@ -108,7 +124,7 @@ namespace Ndk std::move(hitResult.hitPos), std::move(hitResult.hitNormal), hitResult.fraction - }); + }); } return res; @@ -117,14 +133,25 @@ namespace Ndk bool PhysicsSystem2D::RaycastQueryFirst(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RaycastHit* hitInfo) { Nz::PhysWorld2D::RaycastHit queryResult; - bool res = GetPhysWorld().RaycastQueryFirst(from, to, radius, collisionGroup, categoryMask, collisionMask, &queryResult); + if (GetPhysWorld().RaycastQueryFirst(from, to, radius, collisionGroup, categoryMask, collisionMask, &queryResult)) + { + hitInfo->body = GetEntityFromBody(*queryResult.nearestBody); + hitInfo->hitPos = std::move(queryResult.hitPos); + hitInfo->hitNormal = std::move(queryResult.hitNormal); + hitInfo->fraction = queryResult.fraction; - hitInfo->body = GetEntityFromBody(*queryResult.nearestBody); - hitInfo->hitPos = std::move(queryResult.hitPos); - hitInfo->hitNormal = std::move(queryResult.hitNormal); - hitInfo->fraction = queryResult.fraction; + return true; + } + else + return false; + } - return res; + void PhysicsSystem2D::RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, const std::function& callback) + { + return GetPhysWorld().RegionQuery(boundingBox, collisionGroup, categoryMask, collisionMask, [this, &callback](Nz::RigidBody2D* body) + { + callback(GetEntityFromBody(*body)); + }); } void PhysicsSystem2D::RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector* bodies) @@ -213,7 +240,7 @@ namespace Ndk Nz::Vector2f newPosition = Nz::Vector2f(node.GetPosition(Nz::CoordSys_Global)); // To move static objects and ensure their collisions, we have to specify them a velocity - // (/!\: the physical motor does not apply the speed on static objects) + // (/!\: the physical engine does not apply the speed on static objects) if (newPosition != oldPosition) { body->SetPosition(newPosition); @@ -222,8 +249,7 @@ namespace Ndk else body->SetVelocity(Nz::Vector2f::Zero()); -/* - if (newRotation != oldRotation) + /*if (newRotation != oldRotation) { Nz::Quaternionf transition = newRotation * oldRotation.GetConjugate(); Nz::EulerAnglesf angles = transition.ToEulerAngles(); @@ -235,8 +261,7 @@ namespace Ndk physObj->SetAngularVelocity(angularVelocity); } else - physObj->SetAngularVelocity(Nz::Vector3f::Zero()); -*/ + physObj->SetAngularVelocity(Nz::Vector3f::Zero());*/ } } @@ -283,7 +308,7 @@ namespace Ndk void PhysicsSystem2D::RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, Callback callbacks) { - Nz::PhysWorld2D::Callback worldCallbacks{}; + Nz::PhysWorld2D::Callback worldCallbacks; if (callbacks.endCallback) { diff --git a/SDK/src/NDK/Systems/VelocitySystem.cpp b/SDK/src/NDK/Systems/VelocitySystem.cpp index 0b1845bfb..b633d7b68 100644 --- a/SDK/src/NDK/Systems/VelocitySystem.cpp +++ b/SDK/src/NDK/Systems/VelocitySystem.cpp @@ -43,7 +43,7 @@ namespace Ndk NodeComponent& node = entity->GetComponent(); const VelocityComponent& velocity = entity->GetComponent(); - node.Move(velocity.linearVelocity * elapsedTime, Nz::CoordSys_Global); + node.Move(velocity.linearVelocity * elapsedTime, velocity.coordSys); } } diff --git a/SDK/src/NDK/Widgets/ImageWidget.cpp b/SDK/src/NDK/Widgets/ImageWidget.cpp index 8a766419b..0a1cfdc81 100644 --- a/SDK/src/NDK/Widgets/ImageWidget.cpp +++ b/SDK/src/NDK/Widgets/ImageWidget.cpp @@ -12,7 +12,7 @@ namespace Ndk BaseWidget(parent) { m_entity = CreateEntity(); - m_entity->AddComponent(); + m_entity->AddComponent().SetParent(this); auto& gfx = m_entity->AddComponent(); m_sprite = Nz::Sprite::New(); diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index bfbe0140c..d8a1ec0e3 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,7 @@ namespace Ndk void World::AddDefaultSystems() { + AddSystem(); AddSystem(); AddSystem(); AddSystem(); @@ -133,9 +135,9 @@ namespace Ndk m_waitingEntities.clear(); m_aliveEntities.Clear(); - m_dirtyEntities.Clear(); + m_dirtyEntities.front.Clear(); m_freeEntityIds.Clear(); - m_killedEntities.Clear(); + m_killedEntities.front.Clear(); } /*! @@ -208,7 +210,8 @@ namespace Ndk } // Handle killed entities before last call - for (std::size_t i = m_killedEntities.FindFirst(); i != m_killedEntities.npos; i = m_killedEntities.FindNext(i)) + std::swap(m_killedEntities.front, m_killedEntities.back); + for (std::size_t i = m_killedEntities.back.FindFirst(); i != m_killedEntities.back.npos; i = m_killedEntities.back.FindNext(i)) { NazaraAssert(i < m_entityBlocks.size(), "Entity index out of range"); @@ -218,12 +221,13 @@ namespace Ndk entity->Destroy(); // Send back the identifier of the entity to the free queue - m_freeEntityIds.UnboundedSet(entity->GetId()); + m_freeEntityIds.UnboundedSet(i); } - m_killedEntities.Reset(); + m_killedEntities.back.Clear(); // Handle of entities which need an update from the systems - for (std::size_t i = m_dirtyEntities.FindFirst(); i != m_dirtyEntities.npos; i = m_dirtyEntities.FindNext(i)) + std::swap(m_dirtyEntities.front, m_dirtyEntities.back); + for (std::size_t i = m_dirtyEntities.back.FindFirst(); i != m_dirtyEntities.back.npos; i = m_dirtyEntities.back.FindNext(i)) { NazaraAssert(i < m_entityBlocks.size(), "Entity index out of range"); @@ -234,7 +238,7 @@ namespace Ndk continue; Nz::Bitset<>& removedComponents = entity->GetRemovedComponentBits(); - for (std::size_t j = removedComponents.FindFirst(); j != m_dirtyEntities.npos; j = removedComponents.FindNext(j)) + for (std::size_t j = removedComponents.FindFirst(); j != m_dirtyEntities.back.npos; j = removedComponents.FindNext(j)) entity->DestroyComponent(static_cast(j)); removedComponents.Reset(); @@ -260,7 +264,7 @@ namespace Ndk } } } - m_dirtyEntities.Reset(); + m_dirtyEntities.back.Clear(); } /*! diff --git a/build/scripts/common.lua b/build/scripts/common.lua index 20472cf0d..4270fb6dd 100644 --- a/build/scripts/common.lua +++ b/build/scripts/common.lua @@ -58,6 +58,7 @@ function NazaraBuild:Execute() workspace("NazaraEngine") platforms(platformData) + startproject "DemoFirstScene" location(_ACTION) do @@ -841,7 +842,7 @@ function NazaraBuild:PrepareGeneric() symbols("On") filter("configurations:not *Debug*") - flags("NoFramePointer") + omitframepointer("On") -- Setup some optimizations for release filter("configurations:Release*") @@ -855,6 +856,10 @@ function NazaraBuild:PrepareGeneric() filter("configurations:*Dynamic") kind("SharedLib") + -- Enable MSVC conformance (not required but better) + filter("action:vs*") + buildoptions({"/permissive-", "/Zc:__cplusplus", "/Zc:referenceBinding", "/Zc:throwingNew"}) + -- Enable SSE math and vectorization optimizations filter({"configurations:Release*", clangGccActions}) buildoptions("-mfpmath=sse") @@ -872,6 +877,7 @@ function NazaraBuild:PrepareMainWorkspace() -- Add lib/conf/arch to library search path self:FilterLibDirectory("../lib/", libdirs) + self:FilterLibDirectory("../lib/", runpathdirs) filter("action:vs*") buildoptions({"/MP", "/bigobj"}) -- Multiprocessus build and big .obj diff --git a/examples/Tut01/main.cpp b/examples/Tut01/main.cpp index 9d53b7b5e..4570cec08 100644 --- a/examples/Tut01/main.cpp +++ b/examples/Tut01/main.cpp @@ -29,9 +29,13 @@ int main(int argc, char* argv[]) viewer.SetTarget(&mainWindow); viewer.SetProjectionType(Nz::ProjectionType_Orthogonal); + Nz::SimpleTextDrawer textDrawer; + textDrawer.SetCharacterSize(72); + textDrawer.SetOutlineThickness(4.f); + textDrawer.SetText("Hello world !"); Nz::TextSpriteRef textSprite = Nz::TextSprite::New(); - textSprite->Update(Nz::SimpleTextDrawer::Draw("Hello world !", 72)); + textSprite->Update(textDrawer); Ndk::EntityHandle text = world.CreateEntity(); Ndk::NodeComponent& nodeComponent = text->AddComponent(); diff --git a/include/Nazara/Core/Signal.hpp b/include/Nazara/Core/Signal.hpp index 1569c5d0e..973181e47 100644 --- a/include/Nazara/Core/Signal.hpp +++ b/include/Nazara/Core/Signal.hpp @@ -32,7 +32,7 @@ namespace Nz class ConnectionGuard; Signal(); - Signal(const Signal&) = delete; + Signal(const Signal&); Signal(Signal&& signal) noexcept; ~Signal() = default; @@ -47,7 +47,7 @@ namespace Nz void operator()(Args... args) const; - Signal& operator=(const Signal&) = delete; + Signal& operator=(const Signal&); Signal& operator=(Signal&& signal) noexcept; private: diff --git a/include/Nazara/Core/Signal.inl b/include/Nazara/Core/Signal.inl index c4e89bd40..47d490a09 100644 --- a/include/Nazara/Core/Signal.inl +++ b/include/Nazara/Core/Signal.inl @@ -18,13 +18,23 @@ namespace Nz /*! * \brief Constructs a Signal object by default */ - template Signal::Signal() : m_slotIterator(0) { } + /*! + * \brief Constructs a Signal object by default + * + * \remark It doesn't make sense to copy a signal, this is only available for convenience to allow compiler-generated copy constructors + */ + template + Signal::Signal(const Signal&) : + Signal() + { + } + /*! * \brief Constructs a Signal object by move semantic * @@ -174,13 +184,24 @@ namespace Nz m_slots[m_slotIterator]->callback(args...); } + /*! + * \brief Doesn't do anything + * \return A reference to this + * + * \remark This is only for convenience to allow compiled-generated assignation operator + */ + template + Signal& Signal::operator=(const Signal&) + { + return *this; + } + /*! * \brief Moves the signal into this * \return A reference to this * * \param signal Signal to move in this */ - template Signal& Signal::operator=(Signal&& signal) noexcept { diff --git a/include/Nazara/Graphics/CullingList.inl b/include/Nazara/Graphics/CullingList.inl index 5e924d013..341508351 100644 --- a/include/Nazara/Graphics/CullingList.inl +++ b/include/Nazara/Graphics/CullingList.inl @@ -408,12 +408,7 @@ namespace Nz template template - #ifdef NAZARA_COMPILER_MSVC - // MSVC bug - typename CullingList::Entry& CullingList::Entry::operator=(Entry&& entry) - #else typename CullingList::template Entry& CullingList::Entry::operator=(Entry&& entry) - #endif { m_index = entry.m_index; m_parent = entry.m_parent; diff --git a/include/Nazara/Graphics/DeferredGeometryPass.hpp b/include/Nazara/Graphics/DeferredGeometryPass.hpp index 3c7890bfe..043378642 100644 --- a/include/Nazara/Graphics/DeferredGeometryPass.hpp +++ b/include/Nazara/Graphics/DeferredGeometryPass.hpp @@ -51,8 +51,16 @@ namespace Nz int textureOverlay; }; + struct SpriteBatch + { + std::size_t spriteCount; + const Material* material; + const Texture* overlayTexture; + Recti scissorRect; + }; + mutable std::unordered_map m_shaderUniforms; - mutable std::vector> m_spriteChains; + mutable std::vector m_spriteBatches; Buffer m_vertexBuffer; RenderStates m_clearStates; ShaderRef m_clearShader; diff --git a/include/Nazara/Graphics/DepthRenderTechnique.hpp b/include/Nazara/Graphics/DepthRenderTechnique.hpp index f8f7bc4e3..5ae883352 100644 --- a/include/Nazara/Graphics/DepthRenderTechnique.hpp +++ b/include/Nazara/Graphics/DepthRenderTechnique.hpp @@ -61,8 +61,16 @@ namespace Nz int textureOverlay; }; + struct SpriteBatch + { + std::size_t spriteCount; + const Material* material; + const Texture* overlayTexture; + Recti scissorRect; + }; + mutable std::unordered_map m_shaderUniforms; - mutable std::vector> m_spriteChains; + mutable std::vector m_spriteBatches; Buffer m_vertexBuffer; RenderStates m_clearStates; ShaderRef m_clearShader; diff --git a/include/Nazara/Graphics/Material.inl b/include/Nazara/Graphics/Material.inl index 32a59b1bb..66c34e19c 100644 --- a/include/Nazara/Graphics/Material.inl +++ b/include/Nazara/Graphics/Material.inl @@ -119,6 +119,14 @@ namespace Nz { m_pipelineInfo = pipelineInfo; + // Temp and dirty fix for pipeline overriding has*Map + m_pipelineInfo.hasAlphaMap = m_alphaMap.IsValid(); + m_pipelineInfo.hasDiffuseMap = m_diffuseMap.IsValid(); + m_pipelineInfo.hasEmissiveMap = m_emissiveMap.IsValid(); + m_pipelineInfo.hasHeightMap = m_heightMap.IsValid(); + m_pipelineInfo.hasNormalMap = m_normalMap.IsValid(); + m_pipelineInfo.hasSpecularMap = m_specularMap.IsValid(); + InvalidatePipeline(); } diff --git a/include/Nazara/Graphics/TextSprite.hpp b/include/Nazara/Graphics/TextSprite.hpp index d86d999ad..3811fea95 100644 --- a/include/Nazara/Graphics/TextSprite.hpp +++ b/include/Nazara/Graphics/TextSprite.hpp @@ -59,6 +59,31 @@ namespace Nz void OnAtlasLayerChange(const AbstractAtlas* atlas, AbstractImage* oldLayer, AbstractImage* newLayer); void UpdateData(InstanceData* instanceData) const override; + struct RenderKey + { + Texture* texture; + int renderOrder; + + bool operator==(const RenderKey& rhs) const + { + return texture == rhs.texture && renderOrder == rhs.renderOrder; + } + + bool operator!=(const RenderKey& rhs) const + { + return !operator==(rhs); + } + }; + + struct HashRenderKey + { + std::size_t operator()(const RenderKey& key) const + { + // Since renderOrder will be very small, this will be enough + return std::hash()(key.texture) + key.renderOrder; + } + }; + struct RenderIndices { unsigned int first; @@ -74,7 +99,7 @@ namespace Nz }; std::unordered_map m_atlases; - mutable std::unordered_map m_renderInfos; + mutable std::unordered_map m_renderInfos; mutable std::vector m_localVertices; Color m_color; Recti m_localBounds; diff --git a/include/Nazara/Graphics/TileMap.inl b/include/Nazara/Graphics/TileMap.inl index 23065d429..bb24ea3d4 100644 --- a/include/Nazara/Graphics/TileMap.inl +++ b/include/Nazara/Graphics/TileMap.inl @@ -149,8 +149,8 @@ namespace Nz m_layers[materialIndex].tiles.insert(tileIndex); else if (materialIndex != tile.layerIndex) { - m_layers[materialIndex].tiles.erase(tileIndex); - m_layers[tile.layerIndex].tiles.insert(tileIndex); + m_layers[tile.layerIndex].tiles.erase(tileIndex); + m_layers[materialIndex].tiles.insert(tileIndex); invalidatedLayers |= 1U << tile.layerIndex; } @@ -285,8 +285,8 @@ namespace Nz m_layers[materialIndex].tiles.insert(tileIndex); else if (materialIndex != tile.layerIndex) { - m_layers[materialIndex].tiles.erase(tileIndex); - m_layers[tile.layerIndex].tiles.insert(tileIndex); + m_layers[tile.layerIndex].tiles.erase(tileIndex); + m_layers[materialIndex].tiles.insert(tileIndex); invalidatedLayers |= 1U << tile.layerIndex; } diff --git a/include/Nazara/Lua/LuaCoroutine.inl b/include/Nazara/Lua/LuaCoroutine.inl index 63c7d0eda..43a3f87cb 100644 --- a/include/Nazara/Lua/LuaCoroutine.inl +++ b/include/Nazara/Lua/LuaCoroutine.inl @@ -15,8 +15,7 @@ namespace Nz { LuaState::operator=(std::move(instance)); - m_ref = instance.m_ref; - instance.m_ref = -1; + std::swap(m_ref, instance.m_ref); return *this; } diff --git a/include/Nazara/Lua/LuaState.hpp b/include/Nazara/Lua/LuaState.hpp index 47c6d07cf..4890eae30 100644 --- a/include/Nazara/Lua/LuaState.hpp +++ b/include/Nazara/Lua/LuaState.hpp @@ -157,6 +157,8 @@ namespace Nz void* PushUserdata(std::size_t size) const; void PushValue(int index) const; + bool RawEqual(int index1, int index2) const; + void Remove(int index) const; void Replace(int index) const; diff --git a/include/Nazara/Math/Algorithm.hpp b/include/Nazara/Math/Algorithm.hpp index dd9cb4d5a..7bd77437d 100644 --- a/include/Nazara/Math/Algorithm.hpp +++ b/include/Nazara/Math/Algorithm.hpp @@ -35,15 +35,15 @@ namespace Nz { - template /*constexpr*/ T Approach(T value, T objective, T increment); + template constexpr T Approach(T value, T objective, T increment); template constexpr T Clamp(T value, T min, T max); - template /*constexpr*/ std::size_t CountBits(T value); + template constexpr std::size_t CountBits(T value); template constexpr T FromDegrees(T degrees); template constexpr T FromRadians(T radians); template constexpr T DegreeToRadian(T degrees); - template /*constexpr*/ T GetNearestPowerOfTwo(T number); - /*constexpr*/ unsigned int GetNumberLength(signed char number); - /*constexpr*/ unsigned int GetNumberLength(unsigned char number); + template constexpr T GetNearestPowerOfTwo(T number); + constexpr unsigned int GetNumberLength(signed char number); + constexpr unsigned int GetNumberLength(unsigned char number); unsigned int GetNumberLength(int number); /*constexpr*/ unsigned int GetNumberLength(unsigned int number); unsigned int GetNumberLength(long long number); @@ -53,12 +53,12 @@ namespace Nz unsigned int GetNumberLength(long double number, UInt8 precision = NAZARA_CORE_DECIMAL_DIGITS); template /*constexpr*/ unsigned int IntegralLog2(T number); template /*constexpr*/ unsigned int IntegralLog2Pot(T pot); - template /*constexpr*/ T IntegralPow(T base, unsigned int exponent); + template constexpr T IntegralPow(T base, unsigned int exponent); template constexpr T Lerp(const T& from, const T& to, const T2& interpolation); template constexpr T MultiplyAdd(T x, T y, T z); - template /*constexpr*/ T NormalizeAngle(T angle); - template /*constexpr*/ bool NumberEquals(T a, T b); - template /*constexpr*/ bool NumberEquals(T a, T b, T maxDifference); + template constexpr T NormalizeAngle(T angle); + template constexpr bool NumberEquals(T a, T b); + template constexpr bool NumberEquals(T a, T b, T maxDifference); String NumberToString(long long number, UInt8 radix = 10); template constexpr T RadianToDegree(T radians); long long StringToNumber(String str, UInt8 radix = 10, bool* ok = nullptr); diff --git a/include/Nazara/Math/Algorithm.inl b/include/Nazara/Math/Algorithm.inl index b393d38e6..5a7bfb8fc 100644 --- a/include/Nazara/Math/Algorithm.inl +++ b/include/Nazara/Math/Algorithm.inl @@ -98,13 +98,13 @@ namespace Nz return 0; } - template /*constexpr*/ std::enable_if_t::value, bool> NumberEquals(T a, T b, T maxDifference) + template constexpr std::enable_if_t::value, bool> NumberEquals(T a, T b, T maxDifference) { T diff = std::abs(a - b); return diff <= maxDifference; } - template /*constexpr*/ std::enable_if_t::value || (!std::is_integral::value && !std::is_floating_point::value), bool> NumberEquals(T a, T b, T maxDifference) + template constexpr std::enable_if_t::value || (!std::is_integral::value && !std::is_floating_point::value), bool> NumberEquals(T a, T b, T maxDifference) { if (b > a) std::swap(a, b); @@ -113,7 +113,7 @@ namespace Nz return diff <= maxDifference; } - template /*constexpr*/ std::enable_if_t::value && std::is_integral::value, bool> NumberEquals(T a, T b, T maxDifference) + template constexpr std::enable_if_t::value && std::is_integral::value, bool> NumberEquals(T a, T b, T maxDifference) { if (b > a) std::swap(a, b); @@ -132,10 +132,8 @@ namespace Nz * \param objective Target value * \param increment One step value */ - template - //TODO: Mark as constexpr when supported by all major compilers - /*constexpr*/ inline T Approach(T value, T objective, T increment) + constexpr inline T Approach(T value, T objective, T increment) { if (value < objective) return std::min(value + increment, objective); @@ -154,7 +152,6 @@ namespace Nz * \param min Minimum of the interval * \param max Maximum of the interval */ - template constexpr T Clamp(T value, T min, T max) { @@ -168,10 +165,8 @@ namespace Nz * * \param value The value to count bits */ - template - //TODO: Mark as constexpr when supported by all major compilers - /*constexpr*/ inline std::size_t CountBits(T value) + constexpr inline std::size_t CountBits(T value) { // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan std::size_t count = 0; @@ -191,7 +186,6 @@ namespace Nz * * \param degrees Angle in degree (this is expected between 0..360) */ - template constexpr T DegreeToRadian(T degrees) { @@ -205,7 +199,6 @@ namespace Nz * * \param degrees Convert degree to NAZARA_MATH_ANGLE_RADIAN unit */ - template constexpr T FromDegrees(T degrees) { @@ -223,7 +216,6 @@ namespace Nz * * \param radians Convert radian to NAZARA_MATH_ANGLE_RADIAN unit */ - template constexpr T FromRadians(T radians) { @@ -241,10 +233,8 @@ namespace Nz * * \param number Number to get nearest power */ - template - //TODO: Mark as constexpr when supported by all major compilers - /*constexpr*/ inline T GetNearestPowerOfTwo(T number) + constexpr inline T GetNearestPowerOfTwo(T number) { T x = 1; while (x < number) @@ -260,9 +250,7 @@ namespace Nz * * \param number Number to get number of digits */ - - //TODO: Mark as constexpr when supported by all major compilers - /*constexpr*/ inline unsigned int GetNumberLength(signed char number) + constexpr inline unsigned int GetNumberLength(signed char number) { // Char is expected to be 1 byte static_assert(sizeof(number) == 1, "Signed char must be one byte-sized"); @@ -288,9 +276,7 @@ namespace Nz * * \param number Number to get number of digits */ - - //TODO: Mark as constexpr when supported by all major compilers - /*constexpr*/ inline unsigned int GetNumberLength(unsigned char number) + constexpr inline unsigned int GetNumberLength(unsigned char number) { // Char is expected to be 1 byte static_assert(sizeof(number) == 1, "Unsigned char must be one byte-sized"); @@ -310,7 +296,6 @@ namespace Nz * * \param number Number to get number of digits */ - inline unsigned int GetNumberLength(int number) { if (number == 0) @@ -326,7 +311,6 @@ namespace Nz * * \param number Number to get number of digits */ - //TODO: Mark as constexpr when supported by all major compilers /*constexpr*/ inline unsigned int GetNumberLength(unsigned int number) { @@ -343,7 +327,6 @@ namespace Nz * * \param number Number to get number of digits */ - inline unsigned int GetNumberLength(long long number) { if (number == 0) @@ -359,7 +342,6 @@ namespace Nz * * \param number Number to get number of digits */ - //TODO: Mark as constexpr when supported by all major compilers /*constexpr*/ inline unsigned int GetNumberLength(unsigned long long number) { @@ -377,7 +359,6 @@ namespace Nz * \param number Number to get number of digits * \param precision Number of digit after the dot */ - inline unsigned int GetNumberLength(float number, UInt8 precision) { // The imprecision of floats need a cast (log10(9.99999) = 0.99999) @@ -392,7 +373,6 @@ namespace Nz * \param number Number to get number of digits * \param precision Number of digit after the dot */ - inline unsigned int GetNumberLength(double number, UInt8 precision) { // The imprecision of floats need a cast (log10(9.99999) = 0.99999) @@ -407,7 +387,6 @@ namespace Nz * \param number Number to get number of digits * \param precision Number of digit after the dot */ - inline unsigned int GetNumberLength(long double number, UInt8 precision) { // The imprecision of floats need a cast (log10(9.99999) = 0.99999) @@ -423,7 +402,6 @@ namespace Nz * * \remark If number is 0, 0 is returned */ - template //TODO: Mark as constexpr when supported by all major compilers /*constexpr*/ inline unsigned int IntegralLog2(T number) @@ -442,7 +420,6 @@ namespace Nz * \remark Only works for power of two * \remark If number is 0, 0 is returned */ - template //TODO: Mark as constexpr when supported by all major compilers /*constexpr*/ inline unsigned int IntegralLog2Pot(T pot) @@ -458,10 +435,8 @@ namespace Nz * \param base Base of the exponentation * \param exponent Power for the base */ - - //TODO: Mark as constexpr when supported by all major compilers template - /*constexpr*/ T IntegralPow(T base, unsigned int exponent) + constexpr T IntegralPow(T base, unsigned int exponent) { T r = 1; for (unsigned int i = 0; i < exponent; ++i) @@ -484,7 +459,6 @@ namespace Nz * * \see Lerp */ - template constexpr T Lerp(const T& from, const T& to, const T2& interpolation) { @@ -540,10 +514,8 @@ namespace Nz * * \param angle Angle to normalize */ - template - //TODO: Mark as constexpr when supported by all major compilers - /*constexpr*/ inline T NormalizeAngle(T angle) + constexpr inline T NormalizeAngle(T angle) { #if NAZARA_MATH_ANGLE_RADIAN const T limit = T(M_PI); @@ -567,10 +539,8 @@ namespace Nz * \param a First value * \param b Second value */ - template - //TODO: Mark as constexpr when supported by all major compilers - /*constexpr*/ inline bool NumberEquals(T a, T b) + constexpr inline bool NumberEquals(T a, T b) { return NumberEquals(a, b, std::numeric_limits::epsilon()); } @@ -584,10 +554,8 @@ namespace Nz * \param b Second value * \param maxDifference Epsilon of comparison (expected to be positive) */ - template - //TODO: Mark as constexpr when supported by all major compilers - /*constexpr*/ inline bool NumberEquals(T a, T b, T maxDifference) + constexpr inline bool NumberEquals(T a, T b, T maxDifference) { return Detail::NumberEquals(a, b, maxDifference); } @@ -603,7 +571,6 @@ namespace Nz * \remark radix is meant to be between 2 and 36, other values are potentially undefined behavior * \remark With NAZARA_MATH_SAFE, a NazaraError is produced and String() is returned */ - inline String NumberToString(long long number, UInt8 radix) { #if NAZARA_MATH_SAFE @@ -651,7 +618,6 @@ namespace Nz * * \param radians Angle in radian (this is expected between 0..2*pi) */ - template constexpr T RadianToDegree(T radians) { @@ -670,7 +636,6 @@ namespace Nz * \remark radix is meant to be between 2 and 36, other values are potentially undefined behavior * \remark With NAZARA_MATH_SAFE, a NazaraError is produced and 0 is returned */ - inline long long StringToNumber(String str, UInt8 radix, bool* ok) { #if NAZARA_MATH_SAFE @@ -727,7 +692,6 @@ namespace Nz * * \param angle Convert degree from NAZARA_MATH_ANGLE_RADIAN unit to degrees */ - template constexpr T ToDegrees(T angle) { @@ -745,7 +709,6 @@ namespace Nz * * \param angle Convert degree from NAZARA_MATH_ANGLE_RADIAN unit to radians */ - template constexpr T ToRadians(T angle) { diff --git a/include/Nazara/Math/Vector2.hpp b/include/Nazara/Math/Vector2.hpp index 28985305e..d8a53eb47 100644 --- a/include/Nazara/Math/Vector2.hpp +++ b/include/Nazara/Math/Vector2.hpp @@ -109,7 +109,9 @@ namespace Nz using Vector2i = Vector2; using Vector2ui = Vector2; using Vector2i32 = Vector2; + using Vector2i64 = Vector2; using Vector2ui32 = Vector2; + using Vector2ui64 = Vector2; template bool Serialize(SerializationContext& context, const Vector2& vector, TypeTag>); template bool Unserialize(SerializationContext& context, Vector2* vector, TypeTag>); diff --git a/include/Nazara/Math/Vector3.hpp b/include/Nazara/Math/Vector3.hpp index 4bd026acf..b46f2e40b 100644 --- a/include/Nazara/Math/Vector3.hpp +++ b/include/Nazara/Math/Vector3.hpp @@ -129,7 +129,9 @@ namespace Nz using Vector3i = Vector3; using Vector3ui = Vector3; using Vector3i32 = Vector3; + using Vector3i64 = Vector3; using Vector3ui32 = Vector3; + using Vector3ui64 = Vector3; template bool Serialize(SerializationContext& context, const Vector3& vector, TypeTag>); template bool Unserialize(SerializationContext& context, Vector3* vector, TypeTag>); diff --git a/include/Nazara/Math/Vector4.hpp b/include/Nazara/Math/Vector4.hpp index f1a799fb3..7a34a6d11 100644 --- a/include/Nazara/Math/Vector4.hpp +++ b/include/Nazara/Math/Vector4.hpp @@ -106,7 +106,9 @@ namespace Nz using Vector4i = Vector4; using Vector4ui = Vector4; using Vector4i32 = Vector4; + using Vector4i64 = Vector4; using Vector4ui32 = Vector4; + using Vector4ui64 = Vector4; template bool Serialize(SerializationContext& context, const Vector4& vector, TypeTag>); template bool Unserialize(SerializationContext& context, Vector4* vector, TypeTag>); diff --git a/include/Nazara/Math/Vector4.inl b/include/Nazara/Math/Vector4.inl index 69d0b513f..8c602d214 100644 --- a/include/Nazara/Math/Vector4.inl +++ b/include/Nazara/Math/Vector4.inl @@ -899,6 +899,21 @@ namespace Nz return !operator<(vec); } + /*! + * \brief Calculates the dot (scalar) product with two vectors + * \return The value of the dot product + * + * \param vec1 The first vector to calculate the dot product with + * \param vec2 The second vector to calculate the dot product with + * + * \see AbsDotProduct, DotProduct + */ + template + T Vector4::DotProduct(const Vector4& vec1, const Vector4& vec2) + { + return vec1.DotProduct(vec2); + } + /*! * \brief Interpolates the vector to other one with a factor of interpolation * \return A new vector which is the interpolation of two vectors @@ -911,7 +926,6 @@ namespace Nz * * \see Lerp */ - template Vector4 Vector4::Lerp(const Vector4& from, const Vector4& to, T interpolation) { diff --git a/include/Nazara/Network/ENetPeer.inl b/include/Nazara/Network/ENetPeer.inl index 04ec4aae2..7820620ca 100644 --- a/include/Nazara/Network/ENetPeer.inl +++ b/include/Nazara/Network/ENetPeer.inl @@ -74,7 +74,7 @@ namespace Nz inline bool ENetPeer::HasPendingCommands() { - return m_outgoingReliableCommands.empty() && m_outgoingUnreliableCommands.empty() && m_sentReliableCommands.empty(); + return m_outgoingReliableCommands.empty() && m_outgoingUnreliableCommands.empty() && m_sentReliableCommands.empty() && m_sentUnreliableCommands.empty(); } inline bool ENetPeer::IsConnected() const diff --git a/include/Nazara/Physics2D/Arbiter2D.hpp b/include/Nazara/Physics2D/Arbiter2D.hpp index 64c38dd8d..b8683e2eb 100644 --- a/include/Nazara/Physics2D/Arbiter2D.hpp +++ b/include/Nazara/Physics2D/Arbiter2D.hpp @@ -16,6 +16,8 @@ struct cpArbiter; namespace Nz { + class RigidBody2D; + class NAZARA_PHYSICS2D_API Arbiter2D { public: @@ -27,22 +29,24 @@ namespace Nz float ComputeTotalKinematicEnergy() const; Nz::Vector2f ComputeTotalImpulse() const; + std::pair GetBodies() const; + std::size_t GetContactCount() const; float GetContactDepth(std::size_t i) const; - Nz::Vector2f GetContactPointA(std::size_t i) const; - Nz::Vector2f GetContactPointB(std::size_t i) const; + Vector2f GetContactPointA(std::size_t i) const; + Vector2f GetContactPointB(std::size_t i) const; float GetElasticity() const; float GetFriction() const; - Nz::Vector2f GetNormal() const; - Nz::Vector2f GetSurfaceVelocity() const; + Vector2f GetNormal() const; + Vector2f GetSurfaceVelocity() const; bool IsFirstContact() const; bool IsRemoval() const; void SetElasticity(float elasticity); void SetFriction(float friction); - void SetSurfaceVelocity(const Nz::Vector2f& surfaceVelocity); + void SetSurfaceVelocity(const Vector2f& surfaceVelocity); Arbiter2D& operator=(const Arbiter2D&) = delete; Arbiter2D& operator=(Arbiter2D&&) = default; diff --git a/include/Nazara/Physics2D/Collider2D.hpp b/include/Nazara/Physics2D/Collider2D.hpp index 8b69bd780..e817457b8 100644 --- a/include/Nazara/Physics2D/Collider2D.hpp +++ b/include/Nazara/Physics2D/Collider2D.hpp @@ -41,8 +41,11 @@ namespace Nz Collider2D(Collider2D&&) = delete; virtual ~Collider2D(); + virtual Vector2f ComputeCenterOfMass() const = 0; virtual float ComputeMomentOfInertia(float mass) const = 0; + virtual void ForEachPolygon(const std::function& callback) const; + inline UInt32 GetCategoryMask() const; inline UInt32 GetCollisionGroup() const; inline unsigned int GetCollisionId() const; @@ -99,6 +102,7 @@ namespace Nz BoxCollider2D(const Vector2f& size, float radius = 0.f); BoxCollider2D(const Rectf& rect, float radius = 0.f); + Nz::Vector2f ComputeCenterOfMass() const override; float ComputeMomentOfInertia(float mass) const override; inline float GetRadius() const; @@ -125,6 +129,7 @@ namespace Nz public: CircleCollider2D(float radius, const Vector2f& offset = Vector2f::Zero()); + Nz::Vector2f ComputeCenterOfMass() const override; float ComputeMomentOfInertia(float mass) const override; inline const Vector2f& GetOffset() const; @@ -150,6 +155,7 @@ namespace Nz public: CompoundCollider2D(std::vector geoms); + Nz::Vector2f ComputeCenterOfMass() const override; float ComputeMomentOfInertia(float mass) const override; inline bool DoesOverrideCollisionProperties() const; @@ -179,6 +185,7 @@ namespace Nz public: ConvexCollider2D(SparsePtr vertices, std::size_t vertexCount, float radius = 0.f); + Nz::Vector2f ComputeCenterOfMass() const override; float ComputeMomentOfInertia(float mass) const override; ColliderType2D GetType() const override; @@ -203,6 +210,7 @@ namespace Nz public: NullCollider2D() = default; + Nz::Vector2f ComputeCenterOfMass() const override; float ComputeMomentOfInertia(float mass) const override; ColliderType2D GetType() const override; @@ -223,6 +231,7 @@ namespace Nz public: inline SegmentCollider2D(const Vector2f& first, const Vector2f& second, float thickness = 1.f); + Nz::Vector2f ComputeCenterOfMass() const override; float ComputeMomentOfInertia(float mass) const override; inline const Vector2f& GetFirstPoint() const; diff --git a/include/Nazara/Physics2D/PhysWorld2D.hpp b/include/Nazara/Physics2D/PhysWorld2D.hpp index 42e9288a1..8d3afe07c 100644 --- a/include/Nazara/Physics2D/PhysWorld2D.hpp +++ b/include/Nazara/Physics2D/PhysWorld2D.hpp @@ -64,13 +64,15 @@ namespace Nz bool NearestBodyQuery(const Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RigidBody2D** nearestBody = nullptr); bool NearestBodyQuery(const Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, NearestQueryResult* result); + void RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, const std::function& callback); bool RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector* hitInfos); bool RaycastQueryFirst(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RaycastHit* hitInfo = nullptr); + void RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, const std::function& callback); void RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector* bodies); - void RegisterCallbacks(unsigned int collisionId, const Callback& callbacks); - void RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, const Callback& callbacks); + void RegisterCallbacks(unsigned int collisionId, Callback callbacks); + void RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, Callback callbacks); void SetDamping(float dampingValue); void SetGravity(const Vector2f& gravity); @@ -91,7 +93,7 @@ namespace Nz ContactPreSolveCallback preSolveCallback = nullptr; ContactPostSolveCallback postSolveCallback = nullptr; ContactStartCallback startCallback = nullptr; - void* userdata; + void* userdata = nullptr; }; struct DebugDrawOptions @@ -130,7 +132,7 @@ namespace Nz NazaraSignal(OnPhysWorld2DPostStep, const PhysWorld2D* /*physWorld*/, float /*invStepCount*/); private: - void InitCallbacks(cpCollisionHandler* handler, const Callback& callbacks); + void InitCallbacks(cpCollisionHandler* handler, Callback callbacks); using PostStep = std::function; diff --git a/include/Nazara/Physics2D/RigidBody2D.hpp b/include/Nazara/Physics2D/RigidBody2D.hpp index 39dbb0b83..bc40ef9c0 100644 --- a/include/Nazara/Physics2D/RigidBody2D.hpp +++ b/include/Nazara/Physics2D/RigidBody2D.hpp @@ -14,21 +14,25 @@ #include #include #include +#include #include struct cpBody; namespace Nz { + class Arbiter2D; class PhysWorld2D; class NAZARA_PHYSICS2D_API RigidBody2D { public: + using VelocityFunc = std::function; + RigidBody2D(PhysWorld2D* world, float mass); RigidBody2D(PhysWorld2D* world, float mass, Collider2DRef geom); RigidBody2D(const RigidBody2D& object); - RigidBody2D(RigidBody2D&& object); + RigidBody2D(RigidBody2D&& object) noexcept; ~RigidBody2D(); void AddForce(const Vector2f& force, CoordSys coordSys = CoordSys_Global); @@ -41,6 +45,8 @@ namespace Nz void EnableSimulation(bool simulation); + void ForEachArbiter(std::function callback); + Rectf GetAABB() const; inline float GetAngularDamping() const; RadianAnglef GetAngularVelocity() const; @@ -54,12 +60,14 @@ namespace Nz Vector2f GetMassCenter(CoordSys coordSys = CoordSys_Local) const; float GetMomentOfInertia() const; Vector2f GetPosition() const; + inline const Vector2f& GetPositionOffset() const; RadianAnglef GetRotation() const; inline std::size_t GetShapeCount() const; std::size_t GetShapeIndex(cpShape* shape) const; Vector2f GetSurfaceVelocity(std::size_t shapeIndex = 0) const; void* GetUserdata() const; Vector2f GetVelocity() const; + const VelocityFunc& GetVelocityFunction() const; PhysWorld2D* GetWorld() const; bool IsKinematic() const; @@ -67,23 +75,29 @@ namespace Nz bool IsSleeping() const; bool IsStatic() const; + void ResetVelocityFunction(); + inline void SetAngularDamping(float angularDamping); void SetAngularVelocity(const RadianAnglef& angularVelocity); void SetElasticity(float elasticity); void SetElasticity(std::size_t shapeIndex, float elasticity); void SetFriction(float friction); void SetFriction(std::size_t shapeIndex, float friction); - void SetGeom(Collider2DRef geom, bool recomputeMoment = true); + void SetGeom(Collider2DRef geom, bool recomputeMoment = true, bool recomputeMassCenter = true); void SetMass(float mass, bool recomputeMoment = true); void SetMassCenter(const Vector2f& center, CoordSys coordSys = CoordSys_Local); void SetMomentOfInertia(float moment); void SetPosition(const Vector2f& position); + void SetPositionOffset(const Vector2f& offset); void SetRotation(const RadianAnglef& rotation); void SetSurfaceVelocity(const Vector2f& surfaceVelocity); void SetSurfaceVelocity(std::size_t shapeIndex, const Vector2f& surfaceVelocity); void SetStatic(bool setStaticBody = true); void SetUserdata(void* ud); void SetVelocity(const Vector2f& velocity); + void SetVelocityFunction(VelocityFunc velocityFunc); + + void UpdateVelocity(const Nz::Vector2f& gravity, float damping, float deltaTime); RigidBody2D& operator=(const RigidBody2D& object); RigidBody2D& operator=(RigidBody2D&& object); @@ -102,6 +116,8 @@ namespace Nz static void CopyBodyData(cpBody* from, cpBody* to); static void CopyShapeData(cpShape* from, cpShape* to); + Vector2f m_positionOffset; + VelocityFunc m_velocityFunc; std::vector m_shapes; Collider2DRef m_geom; cpBody* m_handle; diff --git a/include/Nazara/Physics2D/RigidBody2D.inl b/include/Nazara/Physics2D/RigidBody2D.inl index a654c03e7..272c8fe30 100644 --- a/include/Nazara/Physics2D/RigidBody2D.inl +++ b/include/Nazara/Physics2D/RigidBody2D.inl @@ -17,6 +17,11 @@ namespace Nz return GetMassCenter(coordSys); } + inline const Vector2f& RigidBody2D::GetPositionOffset() const + { + return m_positionOffset; + } + inline std::size_t RigidBody2D::GetShapeCount() const { return m_shapes.size(); diff --git a/include/Nazara/Physics3D/Collider3D.hpp b/include/Nazara/Physics3D/Collider3D.hpp index a7a59a2d1..9dbfe126c 100644 --- a/include/Nazara/Physics3D/Collider3D.hpp +++ b/include/Nazara/Physics3D/Collider3D.hpp @@ -54,7 +54,7 @@ namespace Nz virtual void ComputeInertialMatrix(Vector3f* inertia, Vector3f* center) const; virtual float ComputeVolume() const; - virtual void ForEachPolygon(const std::function& callback) const; + virtual void ForEachPolygon(const std::function& callback) const; NewtonCollision* GetHandle(PhysWorld3D* world) const; virtual ColliderType3D GetType() const = 0; diff --git a/include/Nazara/Platform/Event.hpp b/include/Nazara/Platform/Event.hpp index ece401890..1fe219e8e 100644 --- a/include/Nazara/Platform/Event.hpp +++ b/include/Nazara/Platform/Event.hpp @@ -36,8 +36,8 @@ namespace Nz struct MouseButtonEvent { Mouse::Button button; - unsigned int x; - unsigned int y; + int x; + int y; }; // Used by: @@ -46,8 +46,8 @@ namespace Nz { int deltaX; int deltaY; - unsigned int x; - unsigned int y; + int x; + int y; }; // Used by: diff --git a/include/Nazara/Platform/Window.hpp b/include/Nazara/Platform/Window.hpp index 7eb5f449a..47d416930 100644 --- a/include/Nazara/Platform/Window.hpp +++ b/include/Nazara/Platform/Window.hpp @@ -40,7 +40,7 @@ namespace Nz inline Window(VideoMode mode, const String& title, WindowStyleFlags style = WindowStyle_Default); inline explicit Window(WindowHandle handle); Window(const Window&) = delete; - Window(Window&&) = default; + Window(Window&& window); virtual ~Window(); inline void Close(); @@ -78,6 +78,8 @@ namespace Nz NAZARA_DEPRECATED("Event pooling/waiting is deprecated, please use the EventHandler system") bool PollEvent(WindowEvent* event); + void PushEvent(const WindowEvent& event); + void ProcessEvents(bool block = false); void SetCursor(CursorRef cursor); @@ -101,7 +103,7 @@ namespace Nz bool WaitEvent(WindowEvent* event); Window& operator=(const Window&) = delete; - Window& operator=(Window&&) = default; + Window& operator=(Window&& window); protected: virtual bool OnWindowCreated(); @@ -111,13 +113,17 @@ namespace Nz MovablePtr m_impl; private: + void ConnectSlots(); + void DisconnectSlots(); + void IgnoreNextMouseEvent(int mouseX, int mouseY) const; - inline void HandleEvent(const WindowEvent& event); - inline void PushEvent(const WindowEvent& event); + void HandleEvent(const WindowEvent& event); static bool Initialize(); static void Uninitialize(); + NazaraSlot(CursorController, OnCursorUpdated, m_cursorUpdateSlot); + std::queue m_events; std::vector m_pendingEvents; ConditionVariable m_eventCondition; diff --git a/include/Nazara/Platform/Window.inl b/include/Nazara/Platform/Window.inl index 9ce95a78a..1be1a773f 100644 --- a/include/Nazara/Platform/Window.inl +++ b/include/Nazara/Platform/Window.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Platform module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -90,20 +91,6 @@ namespace Nz SetCursor(Cursor::Get(systemCursor)); } - inline void Window::HandleEvent(const WindowEvent& event) - { - if (m_eventPolling) - m_events.push(event); - - m_eventHandler.Dispatch(event); - - if (event.type == WindowEventType_Resized) - OnWindowResized(); - - if (event.type == WindowEventType_Quit && m_closeOnQuit) - Close(); - } - inline void Window::PushEvent(const WindowEvent& event) { if (!m_asyncWindow) diff --git a/include/Nazara/Prerequisites.hpp b/include/Nazara/Prerequisites.hpp index ddb2796fc..2414aba95 100644 --- a/include/Nazara/Prerequisites.hpp +++ b/include/Nazara/Prerequisites.hpp @@ -77,7 +77,7 @@ // Nazara version macro #define NAZARA_VERSION_MAJOR 0 -#define NAZARA_VERSION_MINOR 3 +#define NAZARA_VERSION_MINOR 4 #define NAZARA_VERSION_PATCH 0 #include @@ -134,9 +134,9 @@ #define NAZARA_CORE_API #endif -// Détection 64 bits +// Detect 64 bits #if !defined(NAZARA_PLATFORM_x64) && (defined(_WIN64) || defined(__amd64__) || defined(__x86_64__) || defined(__ia64__) || defined(__ia64) || \ - defined(_M_IA64) || defined(__itanium__) || defined(__MINGW64__) || defined(_M_AMD64) || defined (_M_X64)) + defined(_M_IA64) || defined(__itanium__) || defined(__MINGW64__) || defined(_M_AMD64) || defined (_M_X64)) #define NAZARA_PLATFORM_x64 #endif diff --git a/include/Nazara/Renderer/OpenGL.hpp b/include/Nazara/Renderer/OpenGL.hpp index c87426f12..77fe10ad3 100644 --- a/include/Nazara/Renderer/OpenGL.hpp +++ b/include/Nazara/Renderer/OpenGL.hpp @@ -17,16 +17,18 @@ #include // Inclusion des headers OpenGL -#include -#include + +#include +#include + #if defined(NAZARA_PLATFORM_WINDOWS) - #include + #include #elif defined(NAZARA_PLATFORM_GLX) namespace GLX { #include // Defined in a namespace to avoid conflict } - #include + #include #endif namespace Nz diff --git a/include/Nazara/Utility/AbstractAtlas.hpp b/include/Nazara/Utility/AbstractAtlas.hpp index bb6c77c2f..fc84cfa0e 100644 --- a/include/Nazara/Utility/AbstractAtlas.hpp +++ b/include/Nazara/Utility/AbstractAtlas.hpp @@ -22,6 +22,8 @@ namespace Nz { public: AbstractAtlas() = default; + AbstractAtlas(const AbstractAtlas&) = delete; + AbstractAtlas(AbstractAtlas&&) noexcept = default; virtual ~AbstractAtlas(); virtual void Clear() = 0; @@ -31,6 +33,9 @@ namespace Nz virtual UInt32 GetStorage() const = 0; virtual bool Insert(const Image& image, Rectui* rect, bool* flipped, unsigned int* layerIndex) = 0; + AbstractAtlas& operator=(const AbstractAtlas&) = delete; + AbstractAtlas& operator=(AbstractAtlas&&) noexcept = default; + // Signals: NazaraSignal(OnAtlasCleared, const AbstractAtlas* /*atlas*/); NazaraSignal(OnAtlasLayerChange, const AbstractAtlas* /*atlas*/, AbstractImage* /*oldLayer*/, AbstractImage* /*newLayer*/); diff --git a/include/Nazara/Utility/AbstractTextDrawer.hpp b/include/Nazara/Utility/AbstractTextDrawer.hpp index c52051a5c..0b45d422d 100644 --- a/include/Nazara/Utility/AbstractTextDrawer.hpp +++ b/include/Nazara/Utility/AbstractTextDrawer.hpp @@ -44,6 +44,7 @@ namespace Nz Vector2f corners[4]; AbstractImage* atlas; bool flipped; + int renderOrder; }; struct Line diff --git a/include/Nazara/Utility/Enums.hpp b/include/Nazara/Utility/Enums.hpp index d6fee2127..f4d343acc 100644 --- a/include/Nazara/Utility/Enums.hpp +++ b/include/Nazara/Utility/Enums.hpp @@ -329,18 +329,26 @@ namespace Nz TextAlign_Max = TextAlign_Right }; - enum TextStyleFlags + enum TextStyle { - TextStyle_Regular = 0x0, + TextStyle_Bold, + TextStyle_Italic, + TextStyle_StrikeThrough, + TextStyle_Underlined, - TextStyle_Bold = 0x1, - TextStyle_Italic = 0x2, - TextStyle_StrikeThrough = 0x4, - TextStyle_Underlined = 0x8, - - TextStyle_Max = TextStyle_Underlined*2-1 + TextStyle_Max = TextStyle_Underlined }; + template<> + struct EnumAsFlags + { + static constexpr TextStyle max = TextStyle_Max; + }; + + using TextStyleFlags = Flags; + + constexpr TextStyleFlags TextStyle_Regular = 0; + enum VertexComponent { VertexComponent_Unused = -1, diff --git a/include/Nazara/Utility/Font.hpp b/include/Nazara/Utility/Font.hpp index be67c92e7..70cbf6150 100644 --- a/include/Nazara/Utility/Font.hpp +++ b/include/Nazara/Utility/Font.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -58,14 +59,14 @@ namespace Nz bool Create(FontData* data); void Destroy(); - bool ExtractGlyph(unsigned int characterSize, char32_t character, UInt32 style, FontGlyph* glyph) const; + bool ExtractGlyph(unsigned int characterSize, char32_t character, TextStyleFlags style, float outlineThickness, FontGlyph* glyph) const; const std::shared_ptr& GetAtlas() const; - std::size_t GetCachedGlyphCount(unsigned int characterSize, UInt32 style) const; + std::size_t GetCachedGlyphCount(unsigned int characterSize, TextStyleFlags style, float outlineThickness) const; std::size_t GetCachedGlyphCount() const; String GetFamilyName() const; int GetKerning(unsigned int characterSize, char32_t first, char32_t second) const; - const Glyph& GetGlyph(unsigned int characterSize, UInt32 style, char32_t character) const; + const Glyph& GetGlyph(unsigned int characterSize, TextStyleFlags style, float outlineThickness, char32_t character) const; unsigned int GetGlyphBorder() const; unsigned int GetMinimumStepSize() const; const SizeInfo& GetSizeInfo(unsigned int characterSize) const; @@ -73,8 +74,8 @@ namespace Nz bool IsValid() const; - bool Precache(unsigned int characterSize, UInt32 style, char32_t character) const; - bool Precache(unsigned int characterSize, UInt32 style, const String& characterSet) const; + bool Precache(unsigned int characterSize, TextStyleFlags style, float outlineThickness, char32_t character) const; + bool Precache(unsigned int characterSize, TextStyleFlags style, float outlineThickness, const String& characterSet) const; void SetAtlas(const std::shared_ptr& atlas); void SetGlyphBorder(unsigned int borderSize); @@ -106,6 +107,7 @@ namespace Nz bool requireFauxItalic; bool flipped; bool valid; + float fauxOutlineThickness; int advance; unsigned int layerIndex; }; @@ -130,11 +132,11 @@ namespace Nz private: using GlyphMap = std::unordered_map; - UInt64 ComputeKey(unsigned int characterSize, UInt32 style) const; + UInt64 ComputeKey(unsigned int characterSize, TextStyleFlags style, float outlineThickness) const; void OnAtlasCleared(const AbstractAtlas* atlas); void OnAtlasLayerChange(const AbstractAtlas* atlas, AbstractImage* oldLayer, AbstractImage* newLayer); void OnAtlasRelease(const AbstractAtlas* atlas); - const Glyph& PrecacheGlyph(GlyphMap& glyphMap, unsigned int characterSize, UInt32 style, char32_t character) const; + const Glyph& PrecacheGlyph(GlyphMap& glyphMap, unsigned int characterSize, TextStyleFlags style, float outlineThickness, char32_t character) const; static bool Initialize(); static void Uninitialize(); diff --git a/include/Nazara/Utility/FontData.hpp b/include/Nazara/Utility/FontData.hpp index efd2e6d94..88838a3b7 100644 --- a/include/Nazara/Utility/FontData.hpp +++ b/include/Nazara/Utility/FontData.hpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace Nz { @@ -21,7 +22,7 @@ namespace Nz FontData() = default; virtual ~FontData(); - virtual bool ExtractGlyph(unsigned int characterSize, char32_t character, UInt32 style, FontGlyph* dst) = 0; + virtual bool ExtractGlyph(unsigned int characterSize, char32_t character, TextStyleFlags style, float outlineThickness, FontGlyph* dst) = 0; virtual String GetFamilyName() const = 0; virtual String GetStyleName() const = 0; @@ -35,7 +36,8 @@ namespace Nz virtual float QueryUnderlinePosition(unsigned int characterSize) const = 0; virtual float QueryUnderlineThickness(unsigned int characterSize) const = 0; - virtual bool SupportsStyle(UInt32 style) const = 0; + virtual bool SupportsOutline(float outlineThickness) const = 0; + virtual bool SupportsStyle(TextStyleFlags style) const = 0; }; } diff --git a/include/Nazara/Utility/GuillotineImageAtlas.hpp b/include/Nazara/Utility/GuillotineImageAtlas.hpp index 5a1633113..506922c46 100644 --- a/include/Nazara/Utility/GuillotineImageAtlas.hpp +++ b/include/Nazara/Utility/GuillotineImageAtlas.hpp @@ -21,7 +21,9 @@ namespace Nz { public: GuillotineImageAtlas(); - virtual ~GuillotineImageAtlas(); + GuillotineImageAtlas(const GuillotineImageAtlas&) = delete; + GuillotineImageAtlas(GuillotineImageAtlas&&) noexcept = default; + ~GuillotineImageAtlas() = default; void Clear() override; @@ -38,6 +40,9 @@ namespace Nz void SetRectChoiceHeuristic(GuillotineBinPack::FreeRectChoiceHeuristic heuristic); void SetRectSplitHeuristic(GuillotineBinPack::GuillotineSplitHeuristic heuristic); + GuillotineImageAtlas& operator=(const GuillotineImageAtlas&) = delete; + GuillotineImageAtlas& operator=(GuillotineImageAtlas&&) noexcept = default; + protected: struct Layer; diff --git a/include/Nazara/Utility/SimpleTextDrawer.hpp b/include/Nazara/Utility/SimpleTextDrawer.hpp index d3c3bd9cc..5b4225805 100644 --- a/include/Nazara/Utility/SimpleTextDrawer.hpp +++ b/include/Nazara/Utility/SimpleTextDrawer.hpp @@ -38,20 +38,26 @@ namespace Nz std::size_t GetGlyphCount() const override; const Line& GetLine(std::size_t index) const override; std::size_t GetLineCount() const override; - UInt32 GetStyle() const; + const Color& GetOutlineColor() const; + float GetOutlineThickness() const; + TextStyleFlags GetStyle() const; const String& GetText() const; void SetCharacterSize(unsigned int characterSize); void SetColor(const Color& color); void SetFont(Font* font); - void SetStyle(UInt32 style); + void SetOutlineColor(const Color& color); + void SetOutlineThickness(float thickness); + void SetStyle(TextStyleFlags style); void SetText(const String& str); SimpleTextDrawer& operator=(const SimpleTextDrawer& drawer); SimpleTextDrawer& operator=(SimpleTextDrawer&& drawer); - static SimpleTextDrawer Draw(const String& str, unsigned int characterSize, UInt32 style = TextStyle_Regular, const Color& color = Color::White); - static SimpleTextDrawer Draw(Font* font, const String& str, unsigned int characterSize, UInt32 style = TextStyle_Regular, const Color& color = Color::White); + static SimpleTextDrawer Draw(const String& str, unsigned int characterSize, TextStyleFlags style = TextStyle_Regular, const Color& color = Color::White); + static SimpleTextDrawer Draw(const String& str, unsigned int characterSize, TextStyleFlags style, const Color& color, float outlineThickness, const Color& outlineColor); + static SimpleTextDrawer Draw(Font* font, const String& str, unsigned int characterSize, TextStyleFlags style = TextStyle_Regular, const Color& color = Color::White); + static SimpleTextDrawer Draw(Font* font, const String& str, unsigned int characterSize, TextStyleFlags style, const Color& color, float outlineThickness, const Color& outlineColor); private: void ClearGlyphs() const; @@ -72,15 +78,17 @@ namespace Nz mutable std::vector m_glyphs; mutable std::vector m_lines; Color m_color; + Color m_outlineColor; FontRef m_font; mutable Rectf m_workingBounds; mutable Recti m_bounds; String m_text; + TextStyleFlags m_style; mutable UInt32 m_previousCharacter; - UInt32 m_style; mutable Vector2ui m_drawPos; mutable bool m_colorUpdated; mutable bool m_glyphUpdated; + float m_outlineThickness; unsigned int m_characterSize; }; } diff --git a/plugins/Assimp/Plugin.cpp b/plugins/Assimp/Plugin.cpp index b91337f9a..5295d2f5a 100644 --- a/plugins/Assimp/Plugin.cpp +++ b/plugins/Assimp/Plugin.cpp @@ -25,11 +25,14 @@ SOFTWARE. #include #include #include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -77,7 +80,7 @@ bool IsSupported(const String& extension) return (aiIsExtensionSupported(dotExt.GetConstBuffer()) == AI_TRUE); } -Ternary Check(Stream& /*stream*/, const MeshParams& parameters) +Ternary CheckAnimation(Stream& /*stream*/, const AnimationParams& parameters) { bool skip; if (parameters.custom.GetBooleanParameter("SkipAssimpLoader", &skip) && skip) @@ -86,7 +89,102 @@ Ternary Check(Stream& /*stream*/, const MeshParams& parameters) return Ternary_Unknown; } -MeshRef Load(Stream& stream, const MeshParams& parameters) +AnimationRef LoadAnimation(Stream& stream, const AnimationParams& parameters) +{ + Nz::String streamPath = stream.GetPath(); + + FileIOUserdata userdata; + userdata.originalFilePath = (!streamPath.IsEmpty()) ? streamPath.GetConstBuffer() : StreamPath; + userdata.originalStream = &stream; + + aiFileIO fileIO; + fileIO.CloseProc = StreamCloser; + fileIO.OpenProc = StreamOpener; + fileIO.UserData = reinterpret_cast(&userdata); + + unsigned int postProcess = aiProcess_CalcTangentSpace | aiProcess_Debone + | aiProcess_FindInvalidData | aiProcess_FixInfacingNormals + | aiProcess_FlipWindingOrder | aiProcess_GenSmoothNormals + | aiProcess_GenUVCoords | aiProcess_JoinIdenticalVertices + | aiProcess_LimitBoneWeights | aiProcess_MakeLeftHanded + | aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes + | aiProcess_RemoveComponent | aiProcess_RemoveRedundantMaterials + | aiProcess_SortByPType | aiProcess_SplitLargeMeshes + | aiProcess_TransformUVCoords | aiProcess_Triangulate; + + aiPropertyStore* properties = aiCreatePropertyStore(); + aiSetImportPropertyInteger(properties, AI_CONFIG_PP_LBW_MAX_WEIGHTS, 4); + aiSetImportPropertyInteger(properties, AI_CONFIG_PP_RVC_FLAGS, ~aiComponent_ANIMATIONS); + + const aiScene* scene = aiImportFileExWithProperties(userdata.originalFilePath, 0, &fileIO, properties); + aiReleasePropertyStore(properties); + + if (!scene) + { + NazaraError("Assimp failed to import file: " + Nz::String(aiGetErrorString())); + return nullptr; + } + + if (!scene->HasAnimations()) + { + NazaraError("File has no animation"); + return nullptr; + } + + aiAnimation* animation = scene->mAnimations[0]; + + unsigned int maxFrameCount = 0; + for (unsigned int i = 0; i < animation->mNumChannels; ++i) + { + aiNodeAnim* nodeAnim = animation->mChannels[i]; + + maxFrameCount = std::max({ maxFrameCount, nodeAnim->mNumPositionKeys, nodeAnim->mNumRotationKeys, nodeAnim->mNumScalingKeys }); + } + + AnimationRef anim = Animation::New(); + + anim->CreateSkeletal(maxFrameCount, animation->mNumChannels); + + Sequence sequence; + sequence.firstFrame = 0; + sequence.frameCount = maxFrameCount; + sequence.frameRate = animation->mTicksPerSecond; + + anim->AddSequence(sequence); + + SequenceJoint* sequenceJoints = anim->GetSequenceJoints(); + + Quaternionf rotationQuat = Quaternionf::Identity(); + + for (unsigned int i = 0; i < animation->mNumChannels; ++i) + { + aiNodeAnim* nodeAnim = animation->mChannels[i]; + for (unsigned int j = 0; j < nodeAnim->mNumPositionKeys; ++j) + { + SequenceJoint& sequenceJoint = sequenceJoints[i*animation->mNumChannels + j]; + + aiQuaternion rotation = nodeAnim->mRotationKeys[j].mValue; + aiVector3D position = nodeAnim->mPositionKeys[j].mValue; + + sequenceJoint.position = Vector3f(position.x, position.y, position.z); + sequenceJoint.rotation = Quaternionf(rotation.w, rotation.x, rotation.y, rotation.z); + sequenceJoint.scale.Set(1.f); + } + } + + return anim; +} + +Ternary CheckMesh(Stream& /*stream*/, const MeshParams& parameters) +{ + bool skip; + if (parameters.custom.GetBooleanParameter("SkipAssimpLoader", &skip) && skip) + return Ternary_False; + + return Ternary_Unknown; +} + +MeshRef LoadMesh(Stream& stream, const MeshParams& parameters) { Nz::String streamPath = stream.GetPath(); @@ -183,7 +281,162 @@ MeshRef Load(Stream& stream, const MeshParams& parameters) ProcessJoints(scene->mRootNode, skeleton, joints); - return nullptr; + // aiMaterial index in scene => Material index and data in Mesh + std::unordered_map> materials; + + for (unsigned int i = 0; i < scene->mNumMeshes; ++i) + { + aiMesh* iMesh = scene->mMeshes[i]; + if (iMesh->HasBones()) + { + // For now, process only skeletal meshes + } + + unsigned int indexCount = iMesh->mNumFaces * 3; + unsigned int vertexCount = iMesh->mNumVertices; + + // Index buffer + bool largeIndices = (vertexCount > std::numeric_limits::max()); + + IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, indexCount, parameters.storage, parameters.indexBufferFlags); + + IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite); + IndexIterator index = indexMapper.begin(); + + for (unsigned int j = 0; j < iMesh->mNumFaces; ++j) + { + aiFace& face = iMesh->mFaces[j]; + if (face.mNumIndices != 3) + NazaraWarning("Assimp plugin: This face is not a triangle!"); + + *index++ = face.mIndices[0]; + *index++ = face.mIndices[1]; + *index++ = face.mIndices[2]; + } + indexMapper.Unmap(); + + // Make sure the normal/tangent matrix won't rescale our vectors + Nz::Matrix4f normalTangentMatrix = parameters.matrix; + if (normalTangentMatrix.HasScale()) + normalTangentMatrix.ApplyScale(1.f / normalTangentMatrix.GetScale()); + + VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent_Skinning), vertexCount, parameters.storage, parameters.vertexBufferFlags | BufferUsage_Dynamic); + BufferMapper vertexMapper(vertexBuffer, BufferAccess_ReadWrite); + SkeletalMeshVertex* vertices = static_cast(vertexMapper.GetPointer()); + + for (std::size_t i = 0; i < vertexCount; ++i) + { + aiVector3D normal = iMesh->mNormals[i]; + aiVector3D position = iMesh->mVertices[i]; + aiVector3D tangent = iMesh->mTangents[i]; + aiVector3D uv = iMesh->mTextureCoords[0][i]; + + vertices[i].weightCount = 0; + vertices[i].normal = normalTangentMatrix.Transform({ normal.x, normal.y, normal.z }, 0.f); + vertices[i].position = parameters.matrix * Vector3f(position.x, position.y, position.z); + vertices[i].tangent = normalTangentMatrix.Transform({ tangent.x, tangent.y, tangent.z }, 0.f); + vertices[i].uv = parameters.texCoordOffset + Vector2f(uv.x, uv.y) * parameters.texCoordScale; + } + + for (unsigned int i = 0; i < iMesh->mNumBones; ++i) + { + aiBone* bone = iMesh->mBones[i]; + for (unsigned int j = 0; j < bone->mNumWeights; ++j) + { + aiVertexWeight& vertexWeight = bone->mWeights[j]; + SkeletalMeshVertex& vertex = vertices[vertexWeight.mVertexId]; + + std::size_t weightIndex = vertex.weightCount++; + vertex.jointIndexes[weightIndex] = i; + vertex.weights[weightIndex] = vertexWeight.mWeight; + } + } + + // Submesh + SkeletalMeshRef subMesh = SkeletalMesh::New(vertexBuffer, indexBuffer); + subMesh->SetMaterialIndex(iMesh->mMaterialIndex); + + auto matIt = materials.find(iMesh->mMaterialIndex); + if (matIt == materials.end()) + { + ParameterList matData; + aiMaterial* aiMat = scene->mMaterials[iMesh->mMaterialIndex]; + + auto ConvertColor = [&](const char* aiKey, unsigned int aiType, unsigned int aiIndex, const char* colorKey) + { + aiColor4D color; + if (aiGetMaterialColor(aiMat, aiKey, aiType, aiIndex, &color) == aiReturn_SUCCESS) + { + matData.SetParameter(colorKey, Color(static_cast(color.r * 255), static_cast(color.g * 255), static_cast(color.b * 255), static_cast(color.a * 255))); + } + }; + + auto ConvertTexture = [&](aiTextureType aiType, const char* textureKey, const char* wrapKey = nullptr) + { + aiString path; + aiTextureMapMode mapMode[3]; + if (aiGetMaterialTexture(aiMat, aiType, 0, &path, nullptr, nullptr, nullptr, nullptr, &mapMode[0], nullptr) == aiReturn_SUCCESS) + { + matData.SetParameter(textureKey, stream.GetDirectory() + String(path.data, path.length)); + + if (wrapKey) + { + SamplerWrap wrap = SamplerWrap_Default; + switch (mapMode[0]) + { + case aiTextureMapMode_Clamp: + case aiTextureMapMode_Decal: + wrap = SamplerWrap_Clamp; + break; + + case aiTextureMapMode_Mirror: + wrap = SamplerWrap_MirroredRepeat; + break; + + case aiTextureMapMode_Wrap: + wrap = SamplerWrap_Repeat; + break; + + default: + NazaraWarning("Assimp texture map mode 0x" + String::Number(mapMode[0], 16) + " not handled"); + break; + } + + matData.SetParameter(wrapKey, static_cast(wrap)); + } + } + }; + + ConvertColor(AI_MATKEY_COLOR_AMBIENT, MaterialData::AmbientColor); + ConvertColor(AI_MATKEY_COLOR_DIFFUSE, MaterialData::DiffuseColor); + ConvertColor(AI_MATKEY_COLOR_SPECULAR, MaterialData::SpecularColor); + + ConvertTexture(aiTextureType_DIFFUSE, MaterialData::DiffuseTexturePath, MaterialData::DiffuseWrap); + ConvertTexture(aiTextureType_EMISSIVE, MaterialData::EmissiveTexturePath); + ConvertTexture(aiTextureType_HEIGHT, MaterialData::HeightTexturePath); + ConvertTexture(aiTextureType_NORMALS, MaterialData::NormalTexturePath); + ConvertTexture(aiTextureType_OPACITY, MaterialData::AlphaTexturePath); + ConvertTexture(aiTextureType_SPECULAR, MaterialData::SpecularTexturePath, MaterialData::SpecularWrap); + + aiString name; + if (aiGetMaterialString(aiMat, AI_MATKEY_NAME, &name) == aiReturn_SUCCESS) + matData.SetParameter(MaterialData::Name, String(name.data, name.length)); + + int iValue; + if (aiGetMaterialInteger(aiMat, AI_MATKEY_TWOSIDED, &iValue) == aiReturn_SUCCESS) + matData.SetParameter(MaterialData::FaceCulling, !iValue); + + matIt = materials.insert(std::make_pair(iMesh->mMaterialIndex, std::make_pair(UInt32(materials.size()), std::move(matData)))).first; + } + + subMesh->SetMaterialIndex(matIt->first); + + mesh->AddSubMesh(subMesh); + } + + mesh->SetMaterialCount(std::max(UInt32(materials.size()), 1)); + for (const auto& pair : materials) + mesh->SetMaterialData(pair.second.first, pair.second.second); } else { @@ -385,12 +638,14 @@ extern "C" { NAZARA_EXPORT int PluginLoad() { - Nz::MeshLoader::RegisterLoader(IsSupported, Check, Load); + Nz::AnimationLoader::RegisterLoader(IsSupported, CheckAnimation, LoadAnimation); + Nz::MeshLoader::RegisterLoader(IsSupported, CheckMesh, LoadMesh); return 1; } NAZARA_EXPORT void PluginUnload() { - Nz::MeshLoader::UnregisterLoader(IsSupported, Check, Load); + Nz::AnimationLoader::RegisterLoader(IsSupported, CheckAnimation, LoadAnimation); + Nz::MeshLoader::UnregisterLoader(IsSupported, CheckMesh, LoadMesh); } } diff --git a/src/Nazara/Core/Unicode.cpp b/src/Nazara/Core/Unicode.cpp index 477a77ff4..5b20ebce6 100644 --- a/src/Nazara/Core/Unicode.cpp +++ b/src/Nazara/Core/Unicode.cpp @@ -37,13 +37,13 @@ namespace Nz { const UnicodeCharacter* GetCharacter(Nz::UInt32 codepoint) { - auto it = std::lower_bound(std::begin(unicodeCharacters), std::end(unicodeCharacters), codepoint, [](const UnicodeCharacter& character, Nz::UInt32 codepoint) { return character.codepoint < codepoint; }); + auto it = std::lower_bound(std::begin(unicodeCharacters), std::end(unicodeCharacters), codepoint, [](const UnicodeCharacter& character, Nz::UInt32 otherCodepoint) { return character.codepoint < otherCodepoint; }); if (it != std::end(unicodeCharacters) && it->codepoint == codepoint) return &*it; else { // Character is not part of the common character array, search in set - auto itSet = std::lower_bound(std::begin(unicodeSets), std::end(unicodeSets), codepoint, [](const UnicodeSet& character, Nz::UInt32 codepoint) { return character.firstCodepoint < codepoint; }); + auto itSet = std::lower_bound(std::begin(unicodeSets), std::end(unicodeSets), codepoint, [](const UnicodeSet& character, Nz::UInt32 otherCodepoint) { return character.firstCodepoint < otherCodepoint; }); if (itSet != std::begin(unicodeSets)) { --itSet; @@ -58,7 +58,7 @@ namespace Nz template const UnicodeCharacterSimpleMapping* GetCharacterMapping(Nz::UInt32 codepoint, const UnicodeCharacterSimpleMapping(&mapping)[N]) { - auto it = std::lower_bound(std::begin(mapping), std::end(mapping), codepoint, [](const UnicodeCharacterSimpleMapping& character, Nz::UInt32 codepoint) { return character.codepoint < codepoint; }); + auto it = std::lower_bound(std::begin(mapping), std::end(mapping), codepoint, [](const UnicodeCharacterSimpleMapping& character, Nz::UInt32 otherCodepoint) { return character.codepoint < otherCodepoint; }); if (it != std::end(mapping) && it->codepoint == codepoint) return &*it; else diff --git a/src/Nazara/Graphics/DeferredGeometryPass.cpp b/src/Nazara/Graphics/DeferredGeometryPass.cpp index dbd500ea4..0129c4980 100644 --- a/src/Nazara/Graphics/DeferredGeometryPass.cpp +++ b/src/Nazara/Graphics/DeferredGeometryPass.cpp @@ -29,8 +29,8 @@ namespace Nz Vector2f uv; }; - UInt32 s_maxQuads = std::numeric_limits::max() / 6; - UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_maxQuadPerDraw = s_vertexBufferSize / sizeof(VertexLayout_XYZ_Color_UV); } /*! @@ -468,62 +468,9 @@ namespace Nz const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); - Renderer::SetIndexBuffer(&s_quadIndexBuffer); - Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); - Renderer::SetVertexBuffer(&m_spriteBuffer); + const std::size_t maxSpriteCount = std::min(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4); const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); - const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); - - m_spriteChains.clear(); - - auto Commit = [&]() - { - std::size_t spriteChainCount = m_spriteChains.size(); - if (spriteChainCount > 0) - { - std::size_t spriteChain = 0; // Which chain of sprites are we treating - std::size_t spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain - - do - { - // We open the buffer in writing mode - BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); - VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); - - std::size_t spriteCount = 0; - - do - { - const VertexStruct_XYZ_Color_UV* currentChain = m_spriteChains[spriteChain].first; - std::size_t currentChainSpriteCount = m_spriteChains[spriteChain].second; - std::size_t count = std::min(maxSpriteCount - spriteCount, currentChainSpriteCount - spriteChainOffset); - - std::memcpy(vertices, currentChain + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); - vertices += count * 4; - - spriteCount += count; - spriteChainOffset += count; - - // Have we treated the entire chain ? - if (spriteChainOffset == currentChainSpriteCount) - { - spriteChain++; - spriteChainOffset = 0; - } - } - while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); - - vertexMapper.Unmap(); - - Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6); - } - while (spriteChain < spriteChainCount); - } - - m_spriteChains.clear(); - }; - const Material* lastMaterial = nullptr; const MaterialPipeline* lastPipeline = nullptr; const Shader* lastShader = nullptr; @@ -533,18 +480,19 @@ namespace Nz const MaterialPipeline::Instance* pipelineInstance = nullptr; - for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) + Renderer::SetIndexBuffer(&s_quadIndexBuffer); + Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); + Renderer::SetVertexBuffer(&m_spriteBuffer); + + auto Draw = [&]() { - const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; - - if (basicSprites.material != lastMaterial || basicSprites.overlay != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + unsigned int firstIndex = 0; + for (const auto& batch : m_spriteBatches) { - Commit(); - - const MaterialPipeline* pipeline = basicSprites.material->GetPipeline(); - if (lastPipeline != pipeline) + const MaterialPipeline* pipeline = batch.material->GetPipeline(); + if (pipeline != lastPipeline) { - pipelineInstance = &basicSprites.material->GetPipeline()->Apply(ShaderFlags_Deferred | ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); const Shader* shader = pipelineInstance->uberInstance->GetShader(); if (shader != lastShader) @@ -566,33 +514,105 @@ namespace Nz lastPipeline = pipeline; } - if (lastMaterial != basicSprites.material) + if (batch.material != lastMaterial) { - basicSprites.material->Apply(*pipelineInstance); + batch.material->Apply(*pipelineInstance); - Renderer::SetTextureSampler(overlayTextureUnit, basicSprites.material->GetDiffuseSampler()); + Renderer::SetTextureSampler(overlayTextureUnit, batch.material->GetDiffuseSampler()); - lastMaterial = basicSprites.material; + lastMaterial = batch.material; } + if (batch.overlayTexture != lastOverlay) + { + Renderer::SetTexture(overlayTextureUnit, batch.overlayTexture); + lastOverlay = batch.overlayTexture; + } + + if (batch.material->IsScissorTestEnabled() && batch.scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(batch.scissorRect); + lastScissorRect = batch.scissorRect; + } + + unsigned int indexCount = batch.spriteCount * 6; + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, firstIndex, indexCount); + firstIndex += indexCount; + } + }; + + m_spriteBatches.clear(); + { + BufferMapper vertexMapper; + VertexStruct_XYZ_Color_UV* vertices = nullptr; + + std::size_t remainingSprite = maxSpriteCount; + + const Material* lastMaterial = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) + { const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get(); - if (overlayTexture != lastOverlay) - { - Renderer::SetTexture(overlayTextureUnit, overlayTexture); - lastOverlay = overlayTexture; - } + const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; - if (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + const VertexStruct_XYZ_Color_UV* spriteVertices = basicSprites.vertices; + std::size_t spriteCount = basicSprites.spriteCount; + + for (;;) { - Renderer::SetScissorRect(scissorRect); - lastScissorRect = scissorRect; + if (m_spriteBatches.empty() || basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + m_spriteBatches.emplace_back(); + SpriteBatch& newBatch = m_spriteBatches.back(); + newBatch.material = basicSprites.material; + newBatch.overlayTexture = overlayTexture; + newBatch.scissorRect = scissorRect; + newBatch.spriteCount = 0; + + lastMaterial = basicSprites.material; + lastOverlay = overlayTexture; + lastScissorRect = scissorRect; + } + + SpriteBatch& currentBatch = m_spriteBatches.back(); + + if (!vertices) + { + vertexMapper.Map(m_spriteBuffer, BufferAccess_DiscardAndWrite); + vertices = static_cast(vertexMapper.GetPointer()); + } + + std::size_t processedSpriteCount = std::min(remainingSprite, spriteCount); + std::size_t processedVertices = processedSpriteCount * 4; + + std::memcpy(vertices, spriteVertices, processedVertices * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += processedVertices; + spriteVertices += processedVertices; + + currentBatch.spriteCount += processedSpriteCount; + spriteCount -= processedSpriteCount; + + remainingSprite -= processedSpriteCount; + if (remainingSprite == 0) + { + vertexMapper.Unmap(); + vertices = nullptr; + + Draw(); + + remainingSprite = maxSpriteCount; + m_spriteBatches.clear(); + } + + if (spriteCount == 0) + break; } } - - m_spriteChains.emplace_back(basicSprites.vertices, basicSprites.spriteCount); } - Commit(); + Draw(); } const DeferredGeometryPass::ShaderUniforms* DeferredGeometryPass::GetShaderUniforms(const Shader* shader) const @@ -631,12 +651,12 @@ namespace Nz { ErrorFlags flags(ErrorFlag_ThrowException, true); - s_quadIndexBuffer.Reset(false, s_maxQuads * 6, DataStorage_Hardware, 0); + s_quadIndexBuffer.Reset(true, s_maxQuadPerDraw * 6, DataStorage_Hardware, 0); BufferMapper mapper(s_quadIndexBuffer, BufferAccess_WriteOnly); - UInt16* indices = static_cast(mapper.GetPointer()); + UInt32* indices = static_cast(mapper.GetPointer()); - for (unsigned int i = 0; i < s_maxQuads; ++i) + for (UInt32 i = 0; i < s_maxQuadPerDraw; ++i) { *indices++ = i * 4 + 0; *indices++ = i * 4 + 2; diff --git a/src/Nazara/Graphics/DepthRenderTechnique.cpp b/src/Nazara/Graphics/DepthRenderTechnique.cpp index 5435d7497..531b9988a 100644 --- a/src/Nazara/Graphics/DepthRenderTechnique.cpp +++ b/src/Nazara/Graphics/DepthRenderTechnique.cpp @@ -31,8 +31,8 @@ namespace Nz Vector2f uv; }; - unsigned int s_maxQuads = std::numeric_limits::max() / 6; - unsigned int s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_maxQuadPerDraw = s_vertexBufferSize / sizeof(VertexLayout_XYZ_Color_UV); } /*! @@ -46,7 +46,7 @@ namespace Nz */ DepthRenderTechnique::DepthRenderTechnique() : - m_vertexBuffer(BufferType_Vertex) + m_vertexBuffer(BufferType_Vertex) { ErrorFlags flags(ErrorFlag_ThrowException, true); @@ -148,12 +148,12 @@ namespace Nz { ErrorFlags flags(ErrorFlag_ThrowException, true); - s_quadIndexBuffer.Reset(false, s_maxQuads * 6, DataStorage_Hardware, 0); + s_quadIndexBuffer.Reset(true, s_maxQuadPerDraw * 6, DataStorage_Hardware, 0); BufferMapper mapper(s_quadIndexBuffer, BufferAccess_WriteOnly); - UInt16* indices = static_cast(mapper.GetPointer()); + UInt32* indices = static_cast(mapper.GetPointer()); - for (unsigned int i = 0; i < s_maxQuads; ++i) + for (UInt32 i = 0; i < s_maxQuadPerDraw; ++i) { *indices++ = i * 4 + 0; *indices++ = i * 4 + 2; @@ -486,62 +486,9 @@ namespace Nz const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); - Renderer::SetIndexBuffer(&s_quadIndexBuffer); - Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); - Renderer::SetVertexBuffer(&m_spriteBuffer); + const std::size_t maxSpriteCount = std::min(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4); const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); - const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); - - m_spriteChains.clear(); - - auto Commit = [&]() - { - std::size_t spriteChainCount = m_spriteChains.size(); - if (spriteChainCount > 0) - { - std::size_t spriteChain = 0; // Which chain of sprites are we treating - std::size_t spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain - - do - { - // We open the buffer in writing mode - BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); - VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); - - std::size_t spriteCount = 0; - - do - { - const VertexStruct_XYZ_Color_UV* currentChain = m_spriteChains[spriteChain].first; - std::size_t currentChainSpriteCount = m_spriteChains[spriteChain].second; - std::size_t count = std::min(maxSpriteCount - spriteCount, currentChainSpriteCount - spriteChainOffset); - - std::memcpy(vertices, currentChain + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); - vertices += count * 4; - - spriteCount += count; - spriteChainOffset += count; - - // Have we treated the entire chain ? - if (spriteChainOffset == currentChainSpriteCount) - { - spriteChain++; - spriteChainOffset = 0; - } - } - while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); - - vertexMapper.Unmap(); - - Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6); - } - while (spriteChain < spriteChainCount); - } - - m_spriteChains.clear(); - }; - const Material* lastMaterial = nullptr; const MaterialPipeline* lastPipeline = nullptr; const Shader* lastShader = nullptr; @@ -551,18 +498,19 @@ namespace Nz const MaterialPipeline::Instance* pipelineInstance = nullptr; - for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) + Renderer::SetIndexBuffer(&s_quadIndexBuffer); + Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); + Renderer::SetVertexBuffer(&m_spriteBuffer); + + auto Draw = [&]() { - const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; - - if (basicSprites.material != lastMaterial || basicSprites.overlay != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + unsigned int firstIndex = 0; + for (const auto& batch : m_spriteBatches) { - Commit(); - - const MaterialPipeline* pipeline = basicSprites.material->GetPipeline(); - if (lastPipeline != pipeline) + const MaterialPipeline* pipeline = batch.material->GetPipeline(); + if (pipeline != lastPipeline) { - pipelineInstance = &basicSprites.material->GetPipeline()->Apply(ShaderFlags_Deferred | ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); const Shader* shader = pipelineInstance->uberInstance->GetShader(); if (shader != lastShader) @@ -582,33 +530,105 @@ namespace Nz lastPipeline = pipeline; } - if (lastMaterial != basicSprites.material) + if (batch.material != lastMaterial) { - basicSprites.material->Apply(*pipelineInstance); + batch.material->Apply(*pipelineInstance); - Renderer::SetTextureSampler(overlayTextureUnit, basicSprites.material->GetDiffuseSampler()); + Renderer::SetTextureSampler(overlayTextureUnit, batch.material->GetDiffuseSampler()); - lastMaterial = basicSprites.material; + lastMaterial = batch.material; } + if (batch.overlayTexture != lastOverlay) + { + Renderer::SetTexture(overlayTextureUnit, batch.overlayTexture); + lastOverlay = batch.overlayTexture; + } + + if (batch.material->IsScissorTestEnabled() && batch.scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(batch.scissorRect); + lastScissorRect = batch.scissorRect; + } + + unsigned int indexCount = batch.spriteCount * 6; + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, firstIndex, indexCount); + firstIndex += indexCount; + } + }; + + m_spriteBatches.clear(); + { + BufferMapper vertexMapper; + VertexStruct_XYZ_Color_UV* vertices = nullptr; + + std::size_t remainingSprite = maxSpriteCount; + + const Material* lastMaterial = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) + { const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get(); - if (overlayTexture != lastOverlay) - { - Renderer::SetTexture(overlayTextureUnit, overlayTexture); - lastOverlay = overlayTexture; - } + const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; - if (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + const VertexStruct_XYZ_Color_UV* spriteVertices = basicSprites.vertices; + std::size_t spriteCount = basicSprites.spriteCount; + + for (;;) { - Renderer::SetScissorRect(scissorRect); - lastScissorRect = scissorRect; + if (m_spriteBatches.empty() || basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + m_spriteBatches.emplace_back(); + SpriteBatch& newBatch = m_spriteBatches.back(); + newBatch.material = basicSprites.material; + newBatch.overlayTexture = overlayTexture; + newBatch.scissorRect = scissorRect; + newBatch.spriteCount = 0; + + lastMaterial = basicSprites.material; + lastOverlay = overlayTexture; + lastScissorRect = scissorRect; + } + + SpriteBatch& currentBatch = m_spriteBatches.back(); + + if (!vertices) + { + vertexMapper.Map(m_spriteBuffer, BufferAccess_DiscardAndWrite); + vertices = static_cast(vertexMapper.GetPointer()); + } + + std::size_t processedSpriteCount = std::min(remainingSprite, spriteCount); + std::size_t processedVertices = processedSpriteCount * 4; + + std::memcpy(vertices, spriteVertices, processedVertices * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += processedVertices; + spriteVertices += processedVertices; + + currentBatch.spriteCount += processedSpriteCount; + spriteCount -= processedSpriteCount; + + remainingSprite -= processedSpriteCount; + if (remainingSprite == 0) + { + vertexMapper.Unmap(); + vertices = nullptr; + + Draw(); + + remainingSprite = maxSpriteCount; + m_spriteBatches.clear(); + } + + if (spriteCount == 0) + break; } } - - m_spriteChains.emplace_back(basicSprites.vertices, basicSprites.spriteCount); } - Commit(); + Draw(); } /*! diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index 934d54d6d..77098ecf5 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -32,8 +32,8 @@ namespace Nz Vector2f uv; }; - UInt32 s_maxQuads = std::numeric_limits::max() / 6; - UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_maxQuadPerDraw = s_vertexBufferSize / sizeof(VertexLayout_XYZ_Color_UV); } /*! @@ -175,12 +175,12 @@ namespace Nz { ErrorFlags flags(ErrorFlag_ThrowException, true); - s_quadIndexBuffer.Reset(false, s_maxQuads * 6, DataStorage_Hardware, 0); + s_quadIndexBuffer.Reset(true, s_maxQuadPerDraw * 6, DataStorage_Hardware, 0); BufferMapper mapper(s_quadIndexBuffer, BufferAccess_WriteOnly); - UInt16* indices = static_cast(mapper.GetPointer()); + UInt32* indices = static_cast(mapper.GetPointer()); - for (unsigned int i = 0; i < s_maxQuads; ++i) + for (UInt32 i = 0; i < s_maxQuadPerDraw; ++i) { *indices++ = i * 4 + 0; *indices++ = i * 4 + 2; @@ -618,52 +618,9 @@ namespace Nz const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + const std::size_t maxSpriteCount = std::min(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4); + const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); - const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); - - m_spriteBatches.clear(); - { - BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); - VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); - - std::size_t remainingSprite = maxSpriteCount; - - const Material* lastMaterial = nullptr; - const Texture* lastOverlay = nullptr; - Recti lastScissorRect = Recti(-1, -1); - - for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) - { - const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get(); - const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; - if (basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) - { - m_spriteBatches.emplace_back(); - SpriteBatch& newBatch = m_spriteBatches.back(); - newBatch.material = basicSprites.material; - newBatch.overlayTexture = overlayTexture; - newBatch.scissorRect = scissorRect; - newBatch.spriteCount = 0; - - lastMaterial = basicSprites.material; - lastOverlay = overlayTexture; - lastScissorRect = scissorRect; - } - - SpriteBatch& currentBatch = m_spriteBatches.back(); - - std::size_t spriteCount = std::min(remainingSprite, basicSprites.spriteCount); - std::memcpy(vertices, basicSprites.vertices, spriteCount * 4 * sizeof(VertexStruct_XYZ_Color_UV)); - vertices += spriteCount * 4; - - currentBatch.spriteCount += spriteCount; - - remainingSprite -= spriteCount; - if (remainingSprite == 0) - break; - } - } - const Material* lastMaterial = nullptr; const MaterialPipeline* lastPipeline = nullptr; const Shader* lastShader = nullptr; @@ -677,59 +634,135 @@ namespace Nz Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); Renderer::SetVertexBuffer(&m_spriteBuffer); - unsigned int firstIndex = 0; - for (const auto& batch : m_spriteBatches) + auto Draw = [&]() { - const MaterialPipeline* pipeline = batch.material->GetPipeline(); - if (pipeline != lastPipeline) + unsigned int firstIndex = 0; + for (const auto& batch : m_spriteBatches) { - pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); - - const Shader* shader = pipelineInstance->uberInstance->GetShader(); - if (shader != lastShader) + const MaterialPipeline* pipeline = batch.material->GetPipeline(); + if (pipeline != lastPipeline) { - // Index of uniforms in the shader - shaderUniforms = GetShaderUniforms(shader); + pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); - // Ambient color of the scene - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position of the camera - shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); - // Overlay texture unit - shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - lastShader = shader; + // Overlay texture unit + shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); + + lastShader = shader; + } + + lastPipeline = pipeline; } - lastPipeline = pipeline; - } + if (batch.material != lastMaterial) + { + batch.material->Apply(*pipelineInstance); - if (batch.material != lastMaterial) + Renderer::SetTextureSampler(overlayTextureUnit, batch.material->GetDiffuseSampler()); + + lastMaterial = batch.material; + } + + if (batch.overlayTexture != lastOverlay) + { + Renderer::SetTexture(overlayTextureUnit, batch.overlayTexture); + lastOverlay = batch.overlayTexture; + } + + if (batch.material->IsScissorTestEnabled() && batch.scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(batch.scissorRect); + lastScissorRect = batch.scissorRect; + } + + unsigned int indexCount = batch.spriteCount * 6; + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, firstIndex, indexCount); + firstIndex += indexCount; + } + }; + + m_spriteBatches.clear(); + { + BufferMapper vertexMapper; + VertexStruct_XYZ_Color_UV* vertices = nullptr; + + std::size_t remainingSprite = maxSpriteCount; + + const Material* lastMaterial = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) { - batch.material->Apply(*pipelineInstance); + const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get(); + const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; - Renderer::SetTextureSampler(overlayTextureUnit, batch.material->GetDiffuseSampler()); + const VertexStruct_XYZ_Color_UV* spriteVertices = basicSprites.vertices; + std::size_t spriteCount = basicSprites.spriteCount; + + for (;;) + { + if (m_spriteBatches.empty() || basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + m_spriteBatches.emplace_back(); + SpriteBatch& newBatch = m_spriteBatches.back(); + newBatch.material = basicSprites.material; + newBatch.overlayTexture = overlayTexture; + newBatch.scissorRect = scissorRect; + newBatch.spriteCount = 0; - lastMaterial = batch.material; + lastMaterial = basicSprites.material; + lastOverlay = overlayTexture; + lastScissorRect = scissorRect; + } + + SpriteBatch& currentBatch = m_spriteBatches.back(); + + if (!vertices) + { + vertexMapper.Map(m_spriteBuffer, BufferAccess_DiscardAndWrite); + vertices = static_cast(vertexMapper.GetPointer()); + } + + std::size_t processedSpriteCount = std::min(remainingSprite, spriteCount); + std::size_t processedVertices = processedSpriteCount * 4; + + std::memcpy(vertices, spriteVertices, processedVertices * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += processedVertices; + spriteVertices += processedVertices; + + currentBatch.spriteCount += processedSpriteCount; + spriteCount -= processedSpriteCount; + + remainingSprite -= processedSpriteCount; + if (remainingSprite == 0) + { + vertexMapper.Unmap(); + vertices = nullptr; + + Draw(); + + remainingSprite = maxSpriteCount; + m_spriteBatches.clear(); + } + + if (spriteCount == 0) + break; + } } - - if (batch.overlayTexture != lastOverlay) - { - Renderer::SetTexture(overlayTextureUnit, batch.overlayTexture); - lastOverlay = batch.overlayTexture; - } - - if (batch.material->IsScissorTestEnabled() && batch.scissorRect != lastScissorRect) - { - Renderer::SetScissorRect(batch.scissorRect); - lastScissorRect = batch.scissorRect; - } - - unsigned int indexCount = batch.spriteCount * 6; - Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, firstIndex, indexCount); - firstIndex += indexCount; } + + Draw(); } const ForwardRenderTechnique::ShaderUniforms* ForwardRenderTechnique::GetShaderUniforms(const Shader* shader) const diff --git a/src/Nazara/Graphics/TextSprite.cpp b/src/Nazara/Graphics/TextSprite.cpp index 8cc4961cf..a694bb563 100644 --- a/src/Nazara/Graphics/TextSprite.cpp +++ b/src/Nazara/Graphics/TextSprite.cpp @@ -30,13 +30,13 @@ namespace Nz { for (auto& pair : m_renderInfos) { - Texture* overlay = pair.first; + const RenderKey& key = pair.first; RenderIndices& indices = pair.second; if (indices.count > 0) { const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast(instanceData.data.data()); - renderQueue->AddSprites(instanceData.renderOrder, GetMaterial(), &vertices[indices.first * 4], indices.count, scissorRect, overlay); + renderQueue->AddSprites(instanceData.renderOrder + key.renderOrder, GetMaterial(), &vertices[indices.first * 4], indices.count, scissorRect, key.texture); } } } @@ -101,15 +101,16 @@ namespace Nz } std::size_t glyphCount = drawer.GetGlyphCount(); - m_localVertices.resize(glyphCount * 4); // Reset glyph count for every texture to zero for (auto& pair : m_renderInfos) pair.second.count = 0; // Count glyph count for each texture - Texture* lastTexture = nullptr; + RenderKey lastRenderKey { nullptr, 0 }; unsigned int* count = nullptr; + + std::size_t visibleGlyphCount = 0; for (std::size_t i = 0; i < glyphCount; ++i) { const AbstractTextDrawer::Glyph& glyph = drawer.GetGlyph(i); @@ -117,19 +118,23 @@ namespace Nz continue; Texture* texture = static_cast(glyph.atlas); - if (lastTexture != texture) + RenderKey renderKey{ texture, glyph.renderOrder }; + if (lastRenderKey != renderKey) { - auto it = m_renderInfos.find(texture); + auto it = m_renderInfos.find(renderKey); if (it == m_renderInfos.end()) - it = m_renderInfos.insert(std::make_pair(texture, RenderIndices{0U, 0U})).first; + it = m_renderInfos.insert(std::make_pair(renderKey, RenderIndices{0U, 0U})).first; count = &it->second.count; - lastTexture = texture; + lastRenderKey = renderKey; } (*count)++; + visibleGlyphCount++; } + m_localVertices.resize(visibleGlyphCount * 4); + // Attributes indices and reinitialize glyph count to zero to use it as a counter in the next loop // This is because the 1st glyph can use texture A, the 2nd glyph can use texture B and the 3th glyph C can use texture A again // so we need a counter to know where to write informations @@ -140,7 +145,7 @@ namespace Nz { RenderIndices& indices = infoIt->second; if (indices.count == 0) - m_renderInfos.erase(infoIt++); //< No glyph uses this texture, remove from indices + infoIt = m_renderInfos.erase(infoIt); //< No glyph uses this texture, remove from indices else { indices.first = index; @@ -151,7 +156,7 @@ namespace Nz } } - lastTexture = nullptr; + lastRenderKey = { nullptr, 0 }; RenderIndices* indices = nullptr; for (unsigned int i = 0; i < glyphCount; ++i) { @@ -160,10 +165,11 @@ namespace Nz continue; Texture* texture = static_cast(glyph.atlas); - if (lastTexture != texture) + RenderKey renderKey{ texture, glyph.renderOrder }; + if (lastRenderKey != renderKey) { - indices = &m_renderInfos[texture]; //< We changed texture, adjust the pointer - lastTexture = texture; + indices = &m_renderInfos[renderKey]; //< We changed texture, adjust the pointer + lastRenderKey = renderKey; } // First, compute the uv coordinates from our atlas rect @@ -185,9 +191,10 @@ namespace Nz for (unsigned int j = 0; j < 4; ++j) { // Remember that indices->count is a counter here, not a count value - m_localVertices[indices->count * 4 + j].color = glyph.color; - m_localVertices[indices->count * 4 + j].position.Set(glyph.corners[j]); - m_localVertices[indices->count * 4 + j].uv.Set(uvRect.GetCorner((glyph.flipped) ? flippedCorners[j] : normalCorners[j])); + std::size_t offset = (indices->first + indices->count) * 4 + j; + m_localVertices[offset].color = glyph.color; + m_localVertices[offset].position.Set(glyph.corners[j]); + m_localVertices[offset].uv.Set(uvRect.GetCorner((glyph.flipped) ? flippedCorners[j] : normalCorners[j])); } // Increment the counter, go to next glyph @@ -236,13 +243,12 @@ namespace Nz } /*! - * \brief Handle the invalidation of an atlas layer + * \brief Handle the size change of an atlas layer * * \param atlas Atlas being invalidated * \param oldLayer Pointer to the previous layer * \param newLayer Pointer to the new layer */ - void TextSprite::OnAtlasLayerChange(const AbstractAtlas* atlas, AbstractImage* oldLayer, AbstractImage* newLayer) { NazaraUnused(atlas); @@ -255,33 +261,38 @@ namespace Nz } #endif + if (!oldLayer) + return; + + assert(newLayer); + // The texture of an atlas have just been recreated (size change) // we have to adjust the coordinates of the texture and the rendering texture Texture* oldTexture = static_cast(oldLayer); Texture* newTexture = static_cast(newLayer); - // It is possible that we don't use the texture (the atlas warning us for each of its layers) - auto it = m_renderInfos.find(oldTexture); - if (it != m_renderInfos.end()) + Vector2ui oldSize(oldTexture->GetSize()); + Vector2ui newSize(newTexture->GetSize()); + Vector2f scale = Vector2f(oldSize) / Vector2f(newSize); // ratio of the old one to the new one + + // It is possible we actually use that texture multiple times, check them all + for (auto it = m_renderInfos.begin(); it != m_renderInfos.end(); ++it) { - // We indeed use this texture, we have to update its coordinates - RenderIndices indices = std::move(it->second); + const RenderKey& renderKey = it->first; + const RenderIndices& indices = it->second; - Vector2ui oldSize(oldTexture->GetSize()); - Vector2ui newSize(newTexture->GetSize()); - Vector2f scale = Vector2f(oldSize) / Vector2f(newSize); // ratio of the old one to the new one - - // Now we will iterate through each coordinates of the concerned texture to multiply them by the ratio - SparsePtr texCoordPtr(&m_localVertices[indices.first].uv, sizeof(VertexStruct_XYZ_Color_UV)); + // Adjust texture coordinates by size ratio + SparsePtr texCoordPtr(&m_localVertices[indices.first].uv, sizeof(VertexStruct_XY_Color_UV)); for (unsigned int i = 0; i < indices.count; ++i) { for (unsigned int j = 0; j < 4; ++j) - m_localVertices[i*4 + j].uv *= scale; + m_localVertices[i * 4 + j].uv *= scale; } - // We get rid off the old texture and we set the new one at the place (same for indices) + // Erase and re-insert with the new texture handle m_renderInfos.erase(it); - m_renderInfos.insert(std::make_pair(newTexture, std::move(indices))); + m_renderInfos.insert(std::make_pair(RenderKey{ newTexture, renderKey.renderOrder }, indices)); + it = m_renderInfos.begin(); //< std::unordered_map::insert may invalidate all iterators, start from the beginning... } } diff --git a/src/Nazara/Graphics/TileMap.cpp b/src/Nazara/Graphics/TileMap.cpp index eb1831bcf..4789f4a8b 100644 --- a/src/Nazara/Graphics/TileMap.cpp +++ b/src/Nazara/Graphics/TileMap.cpp @@ -27,11 +27,14 @@ namespace Nz { const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast(instanceData.data.data()); - std::size_t matCount = 0; std::size_t spriteCount = 0; - for (const Layer& layer : m_layers) + for (std::size_t layerIndex = 0; layerIndex < m_layers.size(); ++layerIndex) { - renderQueue->AddSprites(instanceData.renderOrder, GetMaterial(matCount++), &vertices[4 * spriteCount], layer.tiles.size(), scissorRect); + const auto& layer = m_layers[layerIndex]; + if (layer.tiles.empty()) + continue; + + renderQueue->AddSprites(instanceData.renderOrder, GetMaterial(layerIndex), &vertices[4 * spriteCount], layer.tiles.size(), scissorRect); spriteCount += layer.tiles.size(); } diff --git a/src/Nazara/Lua/LuaState.cpp b/src/Nazara/Lua/LuaState.cpp index dfbec1117..6065087d6 100644 --- a/src/Nazara/Lua/LuaState.cpp +++ b/src/Nazara/Lua/LuaState.cpp @@ -712,6 +712,11 @@ namespace Nz lua_pushvalue(m_state, index); } + bool LuaState::RawEqual(int index1, int index2) const + { + return lua_rawequal(m_state, index1, index2); + } + void LuaState::Remove(int index) const { lua_remove(m_state, index); diff --git a/src/Nazara/Network/ENetPeer.cpp b/src/Nazara/Network/ENetPeer.cpp index fbae65e14..823eaaf18 100644 --- a/src/Nazara/Network/ENetPeer.cpp +++ b/src/Nazara/Network/ENetPeer.cpp @@ -212,7 +212,7 @@ namespace Nz if ((packetRef->flags & (ENetPacketFlag_Reliable | ENetPacketFlag_UnreliableFragment)) == ENetPacketFlag_UnreliableFragment && channel.outgoingUnreliableSequenceNumber < 0xFFFF) { - commandNumber = ENetProtocolCommand_SendUnreliable; + commandNumber = ENetProtocolCommand_SendUnreliableFragment; startSequenceNumber = HostToNet(channel.outgoingUnreliableSequenceNumber + 1); } else @@ -770,7 +770,7 @@ namespace Nz break; if ((incomingCommand.command.header.command & ENetProtocolCommand_Mask) != ENetProtocolCommand_SendUnreliableFragment || - totalLength != incomingCommand.packet->data.GetDataSize() || fragmentCount != incomingCommand.fragments.GetSize()) + totalLength != incomingCommand.packet->data.GetDataSize() || fragmentCount != incomingCommand.fragments.GetSize()) return false; startCommand = &incomingCommand; @@ -778,9 +778,10 @@ namespace Nz } } - if (startCommand) + if (!startCommand) { - if (!QueueIncomingCommand(*command, nullptr, totalLength, ENetPacketFlag_UnreliableFragment, fragmentCount)) + startCommand = QueueIncomingCommand(*command, nullptr, totalLength, ENetPacketFlag_UnreliableFragment, fragmentCount); + if (!startCommand) return false; } @@ -1040,7 +1041,13 @@ namespace Nz void ENetPeer::RemoveSentUnreliableCommands() { + if (m_sentUnreliableCommands.empty()) + return; + m_sentUnreliableCommands.clear(); + + if (m_state == ENetPeerState::DisconnectLater && !HasPendingCommands()) + Disconnect(m_eventData); } void ENetPeer::ResetQueues() diff --git a/src/Nazara/Network/Posix/IpAddressImpl.cpp b/src/Nazara/Network/Posix/IpAddressImpl.cpp index d00d58068..4abaaee94 100644 --- a/src/Nazara/Network/Posix/IpAddressImpl.cpp +++ b/src/Nazara/Network/Posix/IpAddressImpl.cpp @@ -63,7 +63,7 @@ namespace Nz IpAddress::IPv6 ipv6; for (unsigned int i = 0; i < 8; ++i) - ipv6[i] = rawIpV6[i * 2] << 8 | rawIpV6[i * 2 + 1]; + ipv6[i] = Nz::UInt16(rawIpV6[i * 2]) << 8 | rawIpV6[i * 2 + 1]; return ipv6; } diff --git a/src/Nazara/Network/Win32/IpAddressImpl.cpp b/src/Nazara/Network/Win32/IpAddressImpl.cpp index 853dae840..38da48b4b 100644 --- a/src/Nazara/Network/Win32/IpAddressImpl.cpp +++ b/src/Nazara/Network/Win32/IpAddressImpl.cpp @@ -157,7 +157,7 @@ namespace Nz IpAddress::IPv6 ipv6; for (unsigned int i = 0; i < 8; ++i) - ipv6[i] = rawIpV6[i * 2] << 8 | rawIpV6[i * 2 + 1]; + ipv6[i] = Nz::UInt16(rawIpV6[i * 2]) << 8 | rawIpV6[i * 2 + 1]; return IpAddress(ipv6, ntohs(addressv6->sin6_port)); } diff --git a/src/Nazara/Physics2D/Arbiter2D.cpp b/src/Nazara/Physics2D/Arbiter2D.cpp index 8a1243d26..56668bbe5 100644 --- a/src/Nazara/Physics2D/Arbiter2D.cpp +++ b/src/Nazara/Physics2D/Arbiter2D.cpp @@ -19,6 +19,19 @@ namespace Nz return Nz::Vector2f(Nz::Vector2(impulse.x, impulse.y)); } + std::pair Arbiter2D::GetBodies() const + { + std::pair bodies; + cpBody* firstBody; + cpBody* secondBody; + cpArbiterGetBodies(m_arbiter, &firstBody, &secondBody); + + bodies.first = static_cast(cpBodyGetUserData(firstBody)); + bodies.second = static_cast(cpBodyGetUserData(secondBody)); + + return bodies; + } + std::size_t Arbiter2D::GetContactCount() const { return cpArbiterGetCount(m_arbiter); diff --git a/src/Nazara/Physics2D/Collider2D.cpp b/src/Nazara/Physics2D/Collider2D.cpp index 0be746838..3a67797bd 100644 --- a/src/Nazara/Physics2D/Collider2D.cpp +++ b/src/Nazara/Physics2D/Collider2D.cpp @@ -3,14 +3,81 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include +#include #include -#include +#include +#include +#include namespace Nz { Collider2D::~Collider2D() = default; + void Collider2D::ForEachPolygon(const std::function& callback) const + { + // Currently, the only way to get only the polygons of a shape is to create a temporary cpSpace containing only this shape + // A better way to do this would be to reimplement this function in every subclass type in the very same way chipmunk does + + PhysWorld2D physWorld; + RigidBody2D rigidBody(&physWorld, 0.f); + + std::vector shapeVector; + rigidBody.SetGeom(const_cast(this), false, false); //< Won't be used for writing, but still ugly + + PhysWorld2D::DebugDrawOptions drawCallbacks; + drawCallbacks.circleCallback = [&](const Vector2f& origin, const RadianAnglef& /*rotation*/, float radius, Nz::Color /*outlineColor*/, Nz::Color /*fillColor*/, void* /*userData*/) + { + constexpr std::size_t circleVerticesCount = 20; + + std::array vertices; + + RadianAnglef angleBetweenVertices = 2.f * float(M_PI) / vertices.size(); + for (std::size_t i = 0; i < vertices.size(); ++i) + { + RadianAnglef angle = float(i) * angleBetweenVertices; + std::pair sincos = angle.GetSinCos(); + + vertices[i] = origin + Vector2f(radius * sincos.first, radius * sincos.second); + } + + callback(vertices.data(), vertices.size()); + }; + + drawCallbacks.polygonCallback = [&](const Vector2f* vertices, std::size_t vertexCount, float radius, Nz::Color /*outlineColor*/, Nz::Color /*fillColor*/, void* /*userData*/) + { + //TODO: Handle radius + callback(vertices, vertexCount); + }; + + drawCallbacks.segmentCallback = [&](const Vector2f& first, const Vector2f& second, Nz::Color /*color*/, void* /*userData*/) + { + std::array vertices = { first, second }; + + callback(vertices.data(), vertices.size()); + }; + + drawCallbacks.thickSegmentCallback = [&](const Vector2f& first, const Vector2f& second, float thickness, Nz::Color /*outlineColor*/, Nz::Color /*fillColor*/, void* /*userData*/) + { + static std::pair sincos = Nz::DegreeAnglef(90.f).GetSinCos(); + + Vector2f normal = Vector2f::Normalize(second - first); + Vector2f thicknessNormal(sincos.second * normal.x - sincos.first * normal.y, + sincos.first * normal.x + sincos.second * normal.y); + + std::array vertices; + vertices[0] = first + thickness * thicknessNormal; + vertices[1] = first - thickness * thicknessNormal; + vertices[2] = second - thickness * thicknessNormal; + vertices[3] = second + thickness * thicknessNormal; + + callback(vertices.data(), vertices.size()); + }; + + physWorld.DebugDraw(drawCallbacks, true, false, false); + } + std::size_t Collider2D::GenerateShapes(RigidBody2D* body, std::vector* shapes) const { std::size_t shapeCount = CreateShapes(body, shapes); @@ -20,7 +87,7 @@ namespace Nz { cpShape* shape = (*shapes)[i]; - cpShapeSetCollisionType(shape, cpFloat(m_collisionId)); + cpShapeSetCollisionType(shape, m_collisionId); cpShapeSetElasticity(shape, cpFloat(m_elasticity)); cpShapeSetFilter(shape, filter); cpShapeSetFriction(shape, cpFloat(m_friction)); @@ -44,6 +111,11 @@ namespace Nz { } + Nz::Vector2f BoxCollider2D::ComputeCenterOfMass() const + { + return m_rect.GetCenter(); + } + float BoxCollider2D::ComputeMomentOfInertia(float mass) const { return static_cast(cpMomentForBox2(mass, cpBBNew(m_rect.x, m_rect.y, m_rect.x + m_rect.width, m_rect.y + m_rect.height))); @@ -68,6 +140,11 @@ namespace Nz { } + Nz::Vector2f CircleCollider2D::ComputeCenterOfMass() const + { + return m_offset; + } + float CircleCollider2D::ComputeMomentOfInertia(float mass) const { return static_cast(cpMomentForCircle(mass, 0.f, m_radius, cpv(m_offset.x, m_offset.y))); @@ -92,6 +169,15 @@ namespace Nz { } + Nz::Vector2f CompoundCollider2D::ComputeCenterOfMass() const + { + Nz::Vector2f centerOfMass = Nz::Vector2f::Zero(); + for (const auto& geom : m_geoms) + centerOfMass += geom->ComputeCenterOfMass(); + + return centerOfMass / float(m_geoms.size()); + } + float CompoundCollider2D::ComputeMomentOfInertia(float mass) const { ///TODO: Correctly compute moment using parallel axis theorem: @@ -144,6 +230,15 @@ namespace Nz m_vertices[i].Set(*vertices++); } + Nz::Vector2f ConvexCollider2D::ComputeCenterOfMass() const + { + static_assert(sizeof(cpVect) == sizeof(Vector2d), "Chipmunk vector is not equivalent to Vector2d"); + + cpVect center = cpCentroidForPoly(int(m_vertices.size()), reinterpret_cast(m_vertices.data())); + + return Nz::Vector2f(float(center.x), float(center.y)); + } + float ConvexCollider2D::ComputeMomentOfInertia(float mass) const { static_assert(sizeof(cpVect) == sizeof(Vector2d), "Chipmunk vector is not equivalent to Vector2d"); @@ -169,6 +264,11 @@ namespace Nz return ColliderType2D_Null; } + Nz::Vector2f NullCollider2D::ComputeCenterOfMass() const + { + return Nz::Vector2f::Zero(); + } + float NullCollider2D::ComputeMomentOfInertia(float mass) const { return (mass > 0.f) ? 1.f : 0.f; //< Null inertia is only possible for static/kinematic objects @@ -181,6 +281,11 @@ namespace Nz /******************************** SegmentCollider2D *********************************/ + Nz::Vector2f SegmentCollider2D::ComputeCenterOfMass() const + { + return (m_first + m_second) / 2.f; + } + float SegmentCollider2D::ComputeMomentOfInertia(float mass) const { return static_cast(cpMomentForSegment(mass, cpv(m_first.x, m_first.y), cpv(m_second.x, m_second.y), m_thickness)); diff --git a/src/Nazara/Physics2D/PhysWorld2D.cpp b/src/Nazara/Physics2D/PhysWorld2D.cpp index 7383510f8..6e0c9c22a 100644 --- a/src/Nazara/Physics2D/PhysWorld2D.cpp +++ b/src/Nazara/Physics2D/PhysWorld2D.cpp @@ -205,6 +205,27 @@ namespace Nz } } + void PhysWorld2D::RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, const std::function& callback) + { + using CallbackType = const std::function; + + auto cpCallback = [](cpShape* shape, cpVect point, cpVect normal, cpFloat alpha, void* data) + { + CallbackType& callback = *static_cast(data); + + RaycastHit hitInfo; + hitInfo.fraction = float(alpha); + hitInfo.hitNormal.Set(Nz::Vector2(normal.x, normal.y)); + hitInfo.hitPos.Set(Nz::Vector2(point.x, point.y)); + hitInfo.nearestBody = static_cast(cpShapeGetUserData(shape)); + + callback(hitInfo); + }; + + cpShapeFilter filter = cpShapeFilterNew(collisionGroup, categoryMask, collisionMask); + cpSpaceSegmentQuery(m_handle, { from.x, from.y }, { to.x, to.y }, radius, filter, cpCallback, const_cast(static_cast(&callback))); + } + bool PhysWorld2D::RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector* hitInfos) { using ResultType = decltype(hitInfos); @@ -259,6 +280,20 @@ namespace Nz } } + void PhysWorld2D::RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, const std::function& callback) + { + using CallbackType = const std::function; + + auto cpCallback = [](cpShape* shape, void* data) + { + CallbackType& callback = *static_cast(data); + callback(static_cast(cpShapeGetUserData(shape))); + }; + + cpShapeFilter filter = cpShapeFilterNew(collisionGroup, categoryMask, collisionMask); + cpSpaceBBQuery(m_handle, cpBBNew(boundingBox.x, boundingBox.y, boundingBox.x + boundingBox.width, boundingBox.y + boundingBox.height), filter, cpCallback, const_cast(static_cast(&callback))); + } + void PhysWorld2D::RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector* bodies) { using ResultType = decltype(bodies); @@ -273,14 +308,14 @@ namespace Nz cpSpaceBBQuery(m_handle, cpBBNew(boundingBox.x, boundingBox.y, boundingBox.x + boundingBox.width, boundingBox.y + boundingBox.height), filter, callback, bodies); } - void PhysWorld2D::RegisterCallbacks(unsigned int collisionId, const Callback& callbacks) + void PhysWorld2D::RegisterCallbacks(unsigned int collisionId, Callback callbacks) { - InitCallbacks(cpSpaceAddWildcardHandler(m_handle, collisionId), callbacks); + InitCallbacks(cpSpaceAddWildcardHandler(m_handle, collisionId), std::move(callbacks)); } - void PhysWorld2D::RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, const Callback& callbacks) + void PhysWorld2D::RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, Callback callbacks) { - InitCallbacks(cpSpaceAddCollisionHandler(m_handle, collisionIdA, collisionIdB), callbacks); + InitCallbacks(cpSpaceAddCollisionHandler(m_handle, collisionIdA, collisionIdB), std::move(callbacks)); } void PhysWorld2D::SetDamping(float dampingValue) @@ -341,15 +376,20 @@ namespace Nz cpSpaceUseSpatialHash(m_handle, cpFloat(cellSize), int(entityCount)); } - void PhysWorld2D::InitCallbacks(cpCollisionHandler* handler, const Callback& callbacks) + void PhysWorld2D::InitCallbacks(cpCollisionHandler* handler, Callback callbacks) { - auto it = m_callbacks.emplace(handler, std::make_unique(callbacks)).first; + auto it = m_callbacks.find(handler); + if (it == m_callbacks.end()) + it = m_callbacks.emplace(handler, std::make_unique(std::move(callbacks))).first; + else + it->second = std::make_unique(std::move(callbacks)); - handler->userData = it->second.get(); + Callback* callbackFunctions = it->second.get(); + handler->userData = callbackFunctions; - if (callbacks.startCallback) + if (callbackFunctions->startCallback) { - handler->beginFunc = [](cpArbiter* arb, cpSpace* space, void *data) -> cpBool + handler->beginFunc = [](cpArbiter* arb, cpSpace* space, void* data) -> cpBool { cpBody* firstBody; cpBody* secondBody; @@ -363,19 +403,22 @@ namespace Nz const Callback* customCallbacks = static_cast(data); if (customCallbacks->startCallback(*world, arbiter, *firstRigidBody, *secondRigidBody, customCallbacks->userdata)) - { - cpBool retA = cpArbiterCallWildcardBeginA(arb, space); - cpBool retB = cpArbiterCallWildcardBeginB(arb, space); - return retA && retB; - } + return cpTrue; else return cpFalse; }; } - - if (callbacks.endCallback) + else { - handler->separateFunc = [](cpArbiter* arb, cpSpace* space, void *data) + handler->beginFunc = [](cpArbiter* arb, cpSpace* space, void*) -> cpBool + { + return cpTrue; + }; + } + + if (callbackFunctions->endCallback) + { + handler->separateFunc = [](cpArbiter* arb, cpSpace* space, void* data) { cpBody* firstBody; cpBody* secondBody; @@ -389,15 +432,18 @@ namespace Nz const Callback* customCallbacks = static_cast(data); customCallbacks->endCallback(*world, arbiter, *firstRigidBody, *secondRigidBody, customCallbacks->userdata); - - cpArbiterCallWildcardSeparateA(arb, space); - cpArbiterCallWildcardSeparateB(arb, space); + }; + } + else + { + handler->separateFunc = [](cpArbiter* arb, cpSpace* space, void*) + { }; } - if (callbacks.preSolveCallback) + if (callbackFunctions->preSolveCallback) { - handler->preSolveFunc = [](cpArbiter* arb, cpSpace* space, void *data) -> cpBool + handler->preSolveFunc = [](cpArbiter* arb, cpSpace* space, void* data) -> cpBool { cpBody* firstBody; cpBody* secondBody; @@ -411,17 +457,20 @@ namespace Nz const Callback* customCallbacks = static_cast(data); if (customCallbacks->preSolveCallback(*world, arbiter, *firstRigidBody, *secondRigidBody, customCallbacks->userdata)) - { - cpBool retA = cpArbiterCallWildcardPreSolveA(arb, space); - cpBool retB = cpArbiterCallWildcardPreSolveB(arb, space); - return retA && retB; - } + return cpTrue; else return cpFalse; }; } + else + { + handler->preSolveFunc = [](cpArbiter* arb, cpSpace* space, void* data) -> cpBool + { + return cpTrue; + }; + } - if (callbacks.postSolveCallback) + if (callbackFunctions->postSolveCallback) { handler->postSolveFunc = [](cpArbiter* arb, cpSpace* space, void *data) { @@ -437,9 +486,12 @@ namespace Nz const Callback* customCallbacks = static_cast(data); customCallbacks->postSolveCallback(*world, arbiter, *firstRigidBody, *secondRigidBody, customCallbacks->userdata); - - cpArbiterCallWildcardPostSolveA(arb, space); - cpArbiterCallWildcardPostSolveB(arb, space); + }; + } + else + { + handler->postSolveFunc = [](cpArbiter* arb, cpSpace* space, void* data) + { }; } } diff --git a/src/Nazara/Physics2D/RigidBody2D.cpp b/src/Nazara/Physics2D/RigidBody2D.cpp index 4abd6505b..e4c29b19a 100644 --- a/src/Nazara/Physics2D/RigidBody2D.cpp +++ b/src/Nazara/Physics2D/RigidBody2D.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -18,6 +19,7 @@ namespace Nz } RigidBody2D::RigidBody2D(PhysWorld2D* world, float mass, Collider2DRef geom) : + m_positionOffset(Vector2f::Zero()), m_geom(), m_userData(nullptr), m_world(world), @@ -34,6 +36,7 @@ namespace Nz } RigidBody2D::RigidBody2D(const RigidBody2D& object) : + m_positionOffset(object.m_positionOffset), m_geom(object.m_geom), m_userData(object.m_userData), m_world(object.m_world), @@ -47,7 +50,7 @@ namespace Nz NazaraAssert(m_geom, "Invalid geometry"); m_handle = Create(m_mass, object.GetMomentOfInertia()); - SetGeom(object.GetGeom(), false); + SetGeom(object.GetGeom(), false, false); CopyBodyData(object.GetHandle(), m_handle); @@ -58,9 +61,10 @@ namespace Nz } } - RigidBody2D::RigidBody2D(RigidBody2D&& object) : + RigidBody2D::RigidBody2D(RigidBody2D&& object) noexcept : OnRigidBody2DMove(std::move(object.OnRigidBody2DMove)), OnRigidBody2DRelease(std::move(object.OnRigidBody2DRelease)), + m_positionOffset(std::move(object.m_positionOffset)), m_shapes(std::move(object.m_shapes)), m_geom(std::move(object.m_geom)), m_handle(object.m_handle), @@ -105,7 +109,7 @@ namespace Nz cpBodyApplyForceAtLocalPoint(m_handle, cpv(force.x, force.y), cpv(point.x, point.y)); break; } -} + } void RigidBody2D::AddImpulse(const Vector2f& impulse, CoordSys coordSys) { @@ -156,7 +160,7 @@ namespace Nz if (closestPoint) *closestPoint = closest; - if (minDistance) + if (closestDistance) *closestDistance = minDistance; return true; @@ -175,6 +179,21 @@ namespace Nz } } + void RigidBody2D::ForEachArbiter(std::function callback) + { + using CallbackType = decltype(callback); + + auto RealCallback = [](cpBody* body, cpArbiter* arbiter, void* data) + { + CallbackType& cb = *static_cast(data); + + Arbiter2D nzArbiter(arbiter); + cb(nzArbiter); + }; + + cpBodyEachArbiter(m_handle, RealCallback, &callback); + } + Rectf RigidBody2D::GetAABB() const { if (m_shapes.empty()) @@ -244,7 +263,7 @@ namespace Nz Vector2f RigidBody2D::GetPosition() const { - cpVect pos = cpBodyGetPosition(m_handle); + cpVect pos = cpBodyLocalToWorld(m_handle, cpv(-m_positionOffset.x, -m_positionOffset.y)); return Vector2f(static_cast(pos.x), static_cast(pos.y)); } @@ -280,6 +299,11 @@ namespace Nz return Vector2f(static_cast(vel.x), static_cast(vel.y)); } + const RigidBody2D::VelocityFunc& RigidBody2D::GetVelocityFunction() const + { + return m_velocityFunc; + } + PhysWorld2D* RigidBody2D::GetWorld() const { return m_world; @@ -305,6 +329,11 @@ namespace Nz return m_isStatic; } + void RigidBody2D::ResetVelocityFunction() + { + m_handle->velocity_func = cpBodyUpdateVelocity; + } + void RigidBody2D::SetAngularVelocity(const RadianAnglef& angularVelocity) { cpBodySetAngularVelocity(m_handle, angularVelocity.value); @@ -336,7 +365,7 @@ namespace Nz cpShapeSetFriction(m_shapes[shapeIndex], cpFloat(friction)); } - void RigidBody2D::SetGeom(Collider2DRef geom, bool recomputeMoment) + void RigidBody2D::SetGeom(Collider2DRef geom, bool recomputeMoment, bool recomputeMassCenter) { // We have no public way of getting rid of an existing geom without removing the whole body // So let's save some attributes of the body, destroy it and rebuild it @@ -373,6 +402,9 @@ namespace Nz if (!IsStatic() && !IsKinematic()) cpBodySetMoment(m_handle, m_geom->ComputeMomentOfInertia(m_mass)); } + + if (recomputeMassCenter) + SetMassCenter(m_geom->ComputeCenterOfMass()); } void RigidBody2D::SetMass(float mass, bool recomputeMoment) @@ -438,7 +470,8 @@ namespace Nz void RigidBody2D::SetPosition(const Vector2f& position) { - cpBodySetPosition(m_handle, cpv(position.x, position.y)); + // Use cpTransformVect to rotate/scale the position offset + cpBodySetPosition(m_handle, cpvadd(cpv(position.x, position.y), cpTransformVect(m_handle->transform, cpv(m_positionOffset.x, m_positionOffset.y)))); if (m_isStatic) { m_world->RegisterPostStep(this, [](Nz::RigidBody2D* body) @@ -448,6 +481,13 @@ namespace Nz } } + void RigidBody2D::SetPositionOffset(const Vector2f& offset) + { + Nz::Vector2f position = GetPosition(); + m_positionOffset = offset; + SetPosition(position); + } + void RigidBody2D::SetRotation(const RadianAnglef& rotation) { cpBodySetAngle(m_handle, rotation.value); @@ -500,6 +540,30 @@ namespace Nz cpBodySetVelocity(m_handle, cpv(velocity.x, velocity.y)); } + void RigidBody2D::SetVelocityFunction(VelocityFunc velocityFunc) + { + m_velocityFunc = std::move(velocityFunc); + + if (m_velocityFunc) + { + m_handle->velocity_func = [](cpBody* body, cpVect gravity, cpFloat damping, cpFloat dt) + { + RigidBody2D* rigidBody = static_cast(cpBodyGetUserData(body)); + const auto& callback = rigidBody->GetVelocityFunction(); + assert(callback); + + callback(*rigidBody, Nz::Vector2f(float(gravity.x), float(gravity.y)), float(damping), float(dt)); + }; + } + else + m_handle->velocity_func = cpBodyUpdateVelocity; + } + + void RigidBody2D::UpdateVelocity(const Nz::Vector2f & gravity, float damping, float deltaTime) + { + cpBodyUpdateVelocity(m_handle, cpv(gravity.x, gravity.y), damping, deltaTime); + } + RigidBody2D& RigidBody2D::operator=(const RigidBody2D& object) { RigidBody2D physObj(object); @@ -520,8 +584,10 @@ namespace Nz m_geom = std::move(object.m_geom); m_gravityFactor = object.m_gravityFactor; m_mass = object.m_mass; + m_positionOffset = object.m_positionOffset; m_shapes = std::move(object.m_shapes); m_userData = object.m_userData; + m_velocityFunc = std::move(object.m_velocityFunc); m_world = object.m_world; cpBodySetUserData(m_handle, this); @@ -599,9 +665,10 @@ namespace Nz void RigidBody2D::CopyBodyData(cpBody* from, cpBody* to) { + cpBodySetCenterOfGravity(to, cpBodyGetCenterOfGravity(from)); + cpBodySetAngle(to, cpBodyGetAngle(from)); cpBodySetAngularVelocity(to, cpBodyGetAngularVelocity(from)); - cpBodySetCenterOfGravity(to, cpBodyGetCenterOfGravity(from)); cpBodySetForce(to, cpBodyGetForce(from)); cpBodySetPosition(to, cpBodyGetPosition(from)); cpBodySetTorque(to, cpBodyGetTorque(from)); diff --git a/src/Nazara/Physics3D/Collider3D.cpp b/src/Nazara/Physics3D/Collider3D.cpp index 4dc2f6821..be7ceb6be 100644 --- a/src/Nazara/Physics3D/Collider3D.cpp +++ b/src/Nazara/Physics3D/Collider3D.cpp @@ -114,12 +114,14 @@ namespace Nz return volume; } - void Collider3D::ForEachPolygon(const std::function& callback) const + void Collider3D::ForEachPolygon(const std::function& callback) const { auto newtCallback = [](void* const userData, int vertexCount, const dFloat* const faceArray, int /*faceId*/) { + static_assert(sizeof(Vector3f) == 3 * sizeof(float), "Vector3 is expected to contain 3 floats without padding"); + const auto& cb = *static_cast>(userData); - cb(faceArray, vertexCount); + cb(reinterpret_cast(faceArray), vertexCount); }; // Check for existing collision handles, and create a temporary one if none is available diff --git a/src/Nazara/Platform/Win32/CursorImpl.cpp b/src/Nazara/Platform/Win32/CursorImpl.cpp index 399320c59..48d960f92 100644 --- a/src/Nazara/Platform/Win32/CursorImpl.cpp +++ b/src/Nazara/Platform/Win32/CursorImpl.cpp @@ -48,7 +48,7 @@ namespace Nz bool CursorImpl::Create(SystemCursor cursor) { - if (cursor != SystemCursor_Move) + if (cursor != SystemCursor_None) m_cursor = static_cast(LoadImage(nullptr, s_systemCursorIds[cursor], IMAGE_CURSOR, 0, 0, LR_SHARED)); else m_cursor = nullptr; diff --git a/src/Nazara/Platform/Win32/InputImpl.cpp b/src/Nazara/Platform/Win32/InputImpl.cpp index f44594fa4..79a8b6a3c 100644 --- a/src/Nazara/Platform/Win32/InputImpl.cpp +++ b/src/Nazara/Platform/Win32/InputImpl.cpp @@ -292,5 +292,5 @@ namespace Nz } else NazaraError("Invalid window handle"); - } + } } diff --git a/src/Nazara/Platform/Win32/WindowImpl.cpp b/src/Nazara/Platform/Win32/WindowImpl.cpp index 07f952e96..27b4fa889 100644 --- a/src/Nazara/Platform/Win32/WindowImpl.cpp +++ b/src/Nazara/Platform/Win32/WindowImpl.cpp @@ -53,6 +53,7 @@ namespace Nz m_smoothScrolling(false), m_scrolling(0) { + m_cursor = static_cast(LoadImage(nullptr, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED)); } bool WindowImpl::Create(const VideoMode& mode, const String& title, WindowStyleFlags style) @@ -70,8 +71,8 @@ namespace Nz win32Mode.dmBitsPerPel = mode.bitsPerPixel; win32Mode.dmPelsHeight = mode.height; win32Mode.dmPelsWidth = mode.width; - win32Mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; - win32Mode.dmSize = sizeof(DEVMODE); + win32Mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + win32Mode.dmSize = sizeof(DEVMODE); if (ChangeDisplaySettings(&win32Mode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { @@ -269,6 +270,11 @@ namespace Nz return IsWindowVisible(m_handle) == TRUE; } + void WindowImpl::RefreshCursor() + { + ::SetCursor(m_cursor); + } + void WindowImpl::ProcessEvents(bool block) { if (m_ownsWindow) @@ -289,7 +295,8 @@ namespace Nz { m_cursor = cursor.m_impl->GetCursor(); - ::SetCursor(m_cursor); + if (HasFocus()) + RefreshCursor(); } void WindowImpl::SetEventListener(bool listener) @@ -403,12 +410,12 @@ namespace Nz break; - case WM_SETCURSOR: + /*case WM_SETCURSOR: // http://msdn.microsoft.com/en-us/library/windows/desktop/ms648382(v=vs.85).aspx if (LOWORD(lParam) == HTCLIENT) ::SetCursor(m_cursor); - break; + break;*/ case WM_WINDOWPOSCHANGING: { @@ -653,7 +660,7 @@ namespace Nz { m_mouseInside = true; - // On créé un évènement pour être informé de la sortie de la fenêtre + // Track mouse event to be notified when mouse leaves window TRACKMOUSEEVENT mouseEvent; mouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); mouseEvent.dwFlags = TME_LEAVE; diff --git a/src/Nazara/Platform/Win32/WindowImpl.hpp b/src/Nazara/Platform/Win32/WindowImpl.hpp index b9d18dcd9..e05a86fdf 100644 --- a/src/Nazara/Platform/Win32/WindowImpl.hpp +++ b/src/Nazara/Platform/Win32/WindowImpl.hpp @@ -58,6 +58,8 @@ namespace Nz bool IsMinimized() const; bool IsVisible() const; + void RefreshCursor(); + void ProcessEvents(bool block); void SetCursor(const Cursor& cursor); diff --git a/src/Nazara/Platform/Window.cpp b/src/Nazara/Platform/Window.cpp index 69758cdb0..9ce743ad4 100644 --- a/src/Nazara/Platform/Window.cpp +++ b/src/Nazara/Platform/Window.cpp @@ -33,11 +33,28 @@ namespace Nz m_eventPolling(false), m_waitForEvent(false) { - m_cursorController.OnCursorUpdated.Connect([this](const CursorController*, const CursorRef& cursor) - { - if (IsValid()) - SetCursor(cursor); - }); + ConnectSlots(); + } + + Window::Window(Window&& window) : + m_events(std::move(window.m_events)), + m_pendingEvents(std::move(window.m_pendingEvents)), + m_eventCondition(std::move(window.m_eventCondition)), + m_cursorController(std::move(window.m_cursorController)), + m_cursor(std::move(window.m_cursor)), + m_eventHandler(std::move(window.m_eventHandler)), + m_icon(std::move(window.m_icon)), + m_eventMutex(std::move(window.m_eventMutex)), + m_eventConditionMutex(std::move(window.m_eventConditionMutex)), + m_asyncWindow(window.m_asyncWindow), + m_closed(window.m_asyncWindow), + m_closeOnQuit(window.m_closeOnQuit), + m_eventPolling(window.m_eventPolling), + m_ownsWindow(window.m_asyncWindow), + m_waitForEvent(window.m_waitForEvent) + { + window.DisconnectSlots(); + ConnectSlots(); } Window::~Window() @@ -105,8 +122,6 @@ namespace Nz m_impl->SetMinimumSize(-1, -1); m_impl->SetVisible(true); - SetCursor(Cursor::Get(SystemCursor_Default)); - if (opened) m_impl->SetPosition(position.x, position.y); @@ -582,6 +597,30 @@ namespace Nz } } + Window& Window::operator=(Window&& window) + { + m_events = std::move(window.m_events); + m_pendingEvents = std::move(window.m_pendingEvents); + m_eventCondition = std::move(window.m_eventCondition); + m_cursorController = std::move(window.m_cursorController); + m_cursor = std::move(window.m_cursor); + m_eventHandler = std::move(window.m_eventHandler); + m_icon = std::move(window.m_icon); + m_eventMutex = std::move(window.m_eventMutex); + m_eventConditionMutex = std::move(window.m_eventConditionMutex); + m_asyncWindow = window.m_asyncWindow; + m_closed = window.m_asyncWindow; + m_closeOnQuit = window.m_closeOnQuit; + m_eventPolling = window.m_eventPolling; + m_ownsWindow = window.m_asyncWindow; + m_waitForEvent = window.m_waitForEvent; + + window.DisconnectSlots(); + ConnectSlots(); + + return *this; + } + bool Window::OnWindowCreated() { return true; @@ -595,6 +634,20 @@ namespace Nz { } + void Window::ConnectSlots() + { + m_cursorUpdateSlot.Connect(m_cursorController.OnCursorUpdated, [this](const CursorController*, const CursorRef& cursor) + { + if (IsValid()) + SetCursor(cursor); + }); + } + + void Window::DisconnectSlots() + { + m_cursorUpdateSlot.Disconnect(); + } + void Window::IgnoreNextMouseEvent(int mouseX, int mouseY) const { #if NAZARA_PLATFORM_SAFE @@ -608,6 +661,34 @@ namespace Nz m_impl->IgnoreNextMouseEvent(mouseX, mouseY); } + void Window::HandleEvent(const WindowEvent& event) + { + if (m_eventPolling) + m_events.push(event); + + m_eventHandler.Dispatch(event); + + switch (event.type) + { + case WindowEventType_MouseEntered: + m_impl->RefreshCursor(); + break; + + case WindowEventType_Resized: + OnWindowResized(); + break; + + case WindowEventType_Quit: + if (m_closeOnQuit) + Close(); + + break; + + default: + break; + } + } + bool Window::Initialize() { return WindowImpl::Initialize(); diff --git a/src/Nazara/Platform/X11/WindowImpl.cpp b/src/Nazara/Platform/X11/WindowImpl.cpp index 4a5f8208e..0e8215e37 100644 --- a/src/Nazara/Platform/X11/WindowImpl.cpp +++ b/src/Nazara/Platform/X11/WindowImpl.cpp @@ -412,6 +412,10 @@ namespace Nz } } + void WindowImpl::RefreshCursor() + { + } + void WindowImpl::SetCursor(const Cursor& cursor) { xcb_cursor_t cursorImpl = cursor.m_impl->GetCursor(); diff --git a/src/Nazara/Platform/X11/WindowImpl.hpp b/src/Nazara/Platform/X11/WindowImpl.hpp index d9e9d8652..ecf5e0233 100644 --- a/src/Nazara/Platform/X11/WindowImpl.hpp +++ b/src/Nazara/Platform/X11/WindowImpl.hpp @@ -58,6 +58,8 @@ namespace Nz bool IsMinimized() const; bool IsVisible() const; + void RefreshCursor(); + void ProcessEvents(bool block); void SetCursor(const Cursor& cursor); diff --git a/src/Nazara/Renderer/TextureSampler.cpp b/src/Nazara/Renderer/TextureSampler.cpp index 4f1f9233f..3d24e5d1d 100644 --- a/src/Nazara/Renderer/TextureSampler.cpp +++ b/src/Nazara/Renderer/TextureSampler.cpp @@ -389,5 +389,5 @@ namespace Nz UInt8 TextureSampler::s_defaultAnisotropyLevel = 1; SamplerFilter TextureSampler::s_defaultFilterMode = SamplerFilter_Trilinear; - SamplerWrap TextureSampler::s_defaultWrapMode = SamplerWrap_Repeat; + SamplerWrap TextureSampler::s_defaultWrapMode = SamplerWrap_Clamp; } diff --git a/src/Nazara/Utility/Font.cpp b/src/Nazara/Utility/Font.cpp index 731557628..c0868c346 100644 --- a/src/Nazara/Utility/Font.cpp +++ b/src/Nazara/Utility/Font.cpp @@ -109,7 +109,7 @@ namespace Nz } } - bool Font::ExtractGlyph(unsigned int characterSize, char32_t character, UInt32 style, FontGlyph* glyph) const + bool Font::ExtractGlyph(unsigned int characterSize, char32_t character, TextStyleFlags style, float outlineThickness, FontGlyph* glyph) const { #if NAZARA_UTILITY_SAFE if (!IsValid()) @@ -119,7 +119,7 @@ namespace Nz } #endif - return m_data->ExtractGlyph(characterSize, character, style, glyph); + return m_data->ExtractGlyph(characterSize, character, style, outlineThickness, glyph); } const std::shared_ptr& Font::GetAtlas() const @@ -127,9 +127,9 @@ namespace Nz return m_atlas; } - std::size_t Font::GetCachedGlyphCount(unsigned int characterSize, UInt32 style) const + std::size_t Font::GetCachedGlyphCount(unsigned int characterSize, TextStyleFlags style, float outlineThickness) const { - UInt64 key = ComputeKey(characterSize, style); + UInt64 key = ComputeKey(characterSize, style, outlineThickness); auto it = m_glyphes.find(key); if (it == m_glyphes.end()) return 0; @@ -169,28 +169,27 @@ namespace Nz } #endif - // On utilise un cache car la méthode interne QueryKerning peut se révéler coûteuse (car pouvant induire un changement de taille) + // Use a cache as QueryKerning may be costly (may induce an internal size change) auto& map = m_kerningCache[characterSize]; - UInt64 key = (static_cast(first) << 32) | second; // Combinaison de deux caractères 32 bits dans un nombre 64 bits + UInt64 key = (static_cast(first) << 32) | second; auto it = map.find(key); if (it == map.end()) { - // Absent du cache: on va demander l'information à la police int kerning = m_data->QueryKerning(characterSize, first, second); map.insert(std::make_pair(key, kerning)); return kerning; } else - return it->second; // Présent dans le cache, tout va bien + return it->second; } - const Font::Glyph& Font::GetGlyph(unsigned int characterSize, UInt32 style, char32_t character) const + const Font::Glyph& Font::GetGlyph(unsigned int characterSize, TextStyleFlags style, float outlineThickness, char32_t character) const { - UInt64 key = ComputeKey(characterSize, style); - return PrecacheGlyph(m_glyphes[key], characterSize, style, character); + UInt64 key = ComputeKey(characterSize, style, outlineThickness); + return PrecacheGlyph(m_glyphes[key], characterSize, style, outlineThickness, character); } unsigned int Font::GetGlyphBorder() const @@ -224,11 +223,11 @@ namespace Nz sizeInfo.underlineThickness = m_data->QueryUnderlineThickness(characterSize); FontGlyph glyph; - if (m_data->ExtractGlyph(characterSize, ' ', TextStyle_Regular, &glyph)) + if (m_data->ExtractGlyph(characterSize, ' ', TextStyle_Regular, 0.f, &glyph)) sizeInfo.spaceAdvance = glyph.advance; else { - NazaraWarning("Failed to extract space character from font, using half the size"); + NazaraWarning("Failed to extract space character from font, using half the character size"); sizeInfo.spaceAdvance = characterSize/2; } @@ -256,13 +255,13 @@ namespace Nz return m_data != nullptr; } - bool Font::Precache(unsigned int characterSize, UInt32 style, char32_t character) const + bool Font::Precache(unsigned int characterSize, TextStyleFlags style, float outlineThickness, char32_t character) const { - UInt64 key = ComputeKey(characterSize, style); - return PrecacheGlyph(m_glyphes[key], characterSize, style, character).valid; + UInt64 key = ComputeKey(characterSize, style, outlineThickness); + return PrecacheGlyph(m_glyphes[key], characterSize, style, outlineThickness, character).valid; } - bool Font::Precache(unsigned int characterSize, UInt32 style, const String& characterSet) const + bool Font::Precache(unsigned int characterSize, TextStyleFlags style, float outlineThickness, const String& characterSet) const { ///TODO: Itération UTF-8 => UTF-32 sans allocation de buffer (Exposer utf8cpp ?) std::u32string set = characterSet.GetUtf32String(); @@ -272,10 +271,10 @@ namespace Nz return false; } - UInt64 key = ComputeKey(characterSize, style); + UInt64 key = ComputeKey(characterSize, style, outlineThickness); auto& glyphMap = m_glyphes[key]; for (char32_t character : set) - PrecacheGlyph(glyphMap, characterSize, style, character); + PrecacheGlyph(glyphMap, characterSize, style, outlineThickness, character); return true; } @@ -317,13 +316,7 @@ namespace Nz { if (m_minimumStepSize != minimumStepSize) { - #if NAZARA_UTILITY_SAFE - if (minimumStepSize == 0) - { - NazaraError("Minimum step size cannot be zero as it implies division by zero"); - return; - } - #endif + NazaraAssert(minimumStepSize != 0, "Minimum step size cannot be zero"); m_minimumStepSize = minimumStepSize; ClearGlyphCache(); @@ -399,21 +392,21 @@ namespace Nz s_defaultMinimumStepSize = minimumStepSize; } - UInt64 Font::ComputeKey(unsigned int characterSize, UInt32 style) const + UInt64 Font::ComputeKey(unsigned int characterSize, TextStyleFlags style, float outlineThickness) const { - // On prend le pas en compte - UInt64 sizePart = static_cast((characterSize/m_minimumStepSize)*m_minimumStepSize); - - // Ainsi que le style (uniquement le gras et l'italique, les autres sont gérés par un TextDrawer) - UInt64 stylePart = 0; + // Adjust size to step size + UInt64 sizeStylePart = static_cast((characterSize/m_minimumStepSize)*m_minimumStepSize); + sizeStylePart = std::min(sizeStylePart, Nz::IntegralPow(2, 30)); //< 2^30 should be more than enough as a max size + sizeStylePart <<= 2; + // Store bold and italic flags (other style are handled directly by a TextDrawer) if (style & TextStyle_Bold) - stylePart |= TextStyle_Bold; + sizeStylePart |= 1 << 0; if (style & TextStyle_Italic) - stylePart |= TextStyle_Italic; + sizeStylePart |= 1 << 1; - return (stylePart << 32) | sizePart; + return (sizeStylePart << 32) | reinterpret_cast(outlineThickness); } void Font::OnAtlasCleared(const AbstractAtlas* atlas) @@ -471,13 +464,13 @@ namespace Nz NazaraError("Atlas has been released while in use"); } - const Font::Glyph& Font::PrecacheGlyph(GlyphMap& glyphMap, unsigned int characterSize, UInt32 style, char32_t character) const + const Font::Glyph& Font::PrecacheGlyph(GlyphMap& glyphMap, unsigned int characterSize, TextStyleFlags style, float outlineThickness, char32_t character) const { auto it = glyphMap.find(character); - if (it != glyphMap.end()) // Si le glyphe n'est pas déjà chargé + if (it != glyphMap.end()) return it->second; - Glyph& glyph = glyphMap[character]; // Insertion du glyphe + Glyph& glyph = glyphMap[character]; //< Insert a new glyph glyph.valid = false; #if NAZARA_UTILITY_SAFE @@ -488,11 +481,12 @@ namespace Nz } #endif - // On vérifie que le style demandé est supporté par la police (dans le cas contraire il devra être simulé au rendu) + // Check if requested style is supported by our font (otherwise it will need to be simulated) + glyph.fauxOutlineThickness = 0.f; glyph.requireFauxBold = false; glyph.requireFauxItalic = false; - UInt32 supportedStyle = style; + TextStyleFlags supportedStyle = style; if (style & TextStyle_Bold && !m_data->SupportsStyle(TextStyle_Bold)) { glyph.requireFauxBold = true; @@ -505,12 +499,18 @@ namespace Nz supportedStyle &= ~TextStyle_Italic; } - // Est-ce que la police supporte le style demandé ? - if (style == supportedStyle) + float supportedOutlineThickness = outlineThickness; + if (outlineThickness > 0.f && !m_data->SupportsOutline(outlineThickness)) + { + glyph.fauxOutlineThickness = supportedOutlineThickness; + supportedOutlineThickness = 0.f; + } + + // Does font support requested style? + if (style == supportedStyle && outlineThickness == supportedOutlineThickness) { - // On extrait le glyphe depuis la police FontGlyph fontGlyph; - if (ExtractGlyph(characterSize, character, style, &fontGlyph)) + if (ExtractGlyph(characterSize, character, style, outlineThickness, &fontGlyph)) { if (fontGlyph.image.IsValid()) { @@ -523,21 +523,20 @@ namespace Nz glyph.atlasRect.height = 0; } - // Insertion du rectangle dans l'un des atlas - if (glyph.atlasRect.width > 0 && glyph.atlasRect.height > 0) // Si l'image contient quelque chose + // Insert rectangle (if not empty) into our atlas + if (glyph.atlasRect.width > 0 && glyph.atlasRect.height > 0) { - // Bordure (pour éviter le débordement lors du filtrage) + // Add a small border to prevent GPU to sample another glyph pixel glyph.atlasRect.width += m_glyphBorder*2; glyph.atlasRect.height += m_glyphBorder*2; - // Insertion du rectangle dans l'atlas virtuel if (!m_atlas->Insert(fontGlyph.image, &glyph.atlasRect, &glyph.flipped, &glyph.layerIndex)) { NazaraError("Failed to insert glyph into atlas"); return glyph; } - // Compensation de la bordure (centrage du glyphe) + // Recenter and remove glyph border glyph.atlasRect.x += m_glyphBorder; glyph.atlasRect.y += m_glyphBorder; glyph.atlasRect.width -= m_glyphBorder*2; @@ -549,16 +548,13 @@ namespace Nz glyph.valid = true; } else - { NazaraWarning("Failed to extract glyph \"" + String::Unicode(character) + "\""); - } } else { - // La police ne supporte pas le style demandé, nous allons donc précharger le glyphe supportant le style "minimum" supporté - // et copier ses données - UInt64 newKey = ComputeKey(characterSize, supportedStyle); - const Glyph& referenceGlyph = PrecacheGlyph(m_glyphes[newKey], characterSize, supportedStyle, character); + // Font doesn't support request style, precache the minimal supported version and copy its data + UInt64 newKey = ComputeKey(characterSize, supportedStyle, supportedOutlineThickness); + const Glyph& referenceGlyph = PrecacheGlyph(m_glyphes[newKey], characterSize, supportedStyle, supportedOutlineThickness, character); if (referenceGlyph.valid) { glyph.aabb = referenceGlyph.aabb; diff --git a/src/Nazara/Utility/Formats/FreeTypeLoader.cpp b/src/Nazara/Utility/Formats/FreeTypeLoader.cpp index 2cd2c51e3..05814a903 100644 --- a/src/Nazara/Utility/Formats/FreeTypeLoader.cpp +++ b/src/Nazara/Utility/Formats/FreeTypeLoader.cpp @@ -6,7 +6,9 @@ #include #include FT_FREETYPE_H #include FT_BITMAP_H +#include FT_STROKER_H #include FT_OUTLINE_H +#include #include #include #include @@ -24,8 +26,10 @@ namespace Nz class FreeTypeLibrary; FT_Library s_library; + FT_Stroker s_stroker; std::shared_ptr s_libraryOwner; - float s_invScaleFactor = 1.f / (1 << 6); // 1/64 + constexpr float s_scaleFactor = 1 << 6; + constexpr float s_invScaleFactor = 1.f / s_scaleFactor; extern "C" unsigned long FT_StreamRead(FT_Stream stream, unsigned long offset, unsigned char* buffer, unsigned long count) @@ -66,9 +70,23 @@ namespace Nz // pour ne libérer FreeType que lorsque plus personne ne l'utilise public: - FreeTypeLibrary() = default; + FreeTypeLibrary() + { + if (FT_Stroker_New(s_library, &s_stroker) != 0) + { + NazaraWarning("Failed to load FreeType stroker, outline will not be possible"); + s_stroker = nullptr; //< Just in case + } + } + ~FreeTypeLibrary() { + if (s_stroker) + { + FT_Stroker_Done(s_stroker); + s_stroker = nullptr; + } + FT_Done_FreeType(s_library); s_library = nullptr; } @@ -96,7 +114,7 @@ namespace Nz return FT_Open_Face(s_library, &m_args, -1, nullptr) == 0; } - bool ExtractGlyph(unsigned int characterSize, char32_t character, UInt32 style, FontGlyph* dst) override + bool ExtractGlyph(unsigned int characterSize, char32_t character, TextStyleFlags style, float outlineThickness, FontGlyph* dst) override { #ifdef NAZARA_DEBUG if (!dst) @@ -114,61 +132,85 @@ namespace Nz return false; } - FT_GlyphSlot& glyph = m_face->glyph; + FT_GlyphSlot glyphSlot = m_face->glyph; + + FT_Glyph glyph; + if (FT_Get_Glyph(glyphSlot, &glyph) != 0) + { + NazaraError("Failed to extract glyph"); + return false; + } + CallOnExit destroyGlyph([&]() { FT_Done_Glyph(glyph); }); const FT_Pos boldStrength = 2 << 6; - bool embolden = (style & TextStyle_Bold); + bool embolden = (style & TextStyle_Bold) != 0; + bool hasOutlineFormat = (glyph->format == FT_GLYPH_FORMAT_OUTLINE); dst->advance = (embolden) ? boldStrength >> 6 : 0; - if (embolden && glyph->format == FT_GLYPH_FORMAT_OUTLINE) + if (hasOutlineFormat) { - // http://www.freetype.org/freetype2/docs/reference/ft2-outline_processing.html#FT_Outline_Embolden - FT_Outline_Embolden(&glyph->outline, boldStrength); - embolden = false; + if (embolden) + { + // FT_Glyph can be casted to FT_OutlineGlyph if format is FT_GLYPH_FORMAT_OUTLINE + FT_OutlineGlyph outlineGlyph = reinterpret_cast(glyph); + if (FT_Outline_Embolden(&outlineGlyph->outline, boldStrength) != 0) + { + NazaraError("Failed to embolden glyph"); + return false; + } + } + + if (outlineThickness > 0.f) + { + FT_Stroker_Set(s_stroker, static_cast(s_scaleFactor * outlineThickness), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); + if (FT_Glyph_Stroke(&glyph, s_stroker, 1) != 0) + { + NazaraError("Failed to outline glyph"); + return false; + } + } } - // http://www.freetype.org/freetype2/docs/reference/ft2-glyph_management.html#FT_Glyph_To_Bitmap - // Conversion du glyphe vers le format bitmap - // Cette fonction ne fait rien dans le cas où le glyphe est déjà un bitmap - if (FT_Render_Glyph(glyph, FT_RENDER_MODE_NORMAL) != 0) + if (FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, nullptr, 1) != 0) { NazaraError("Failed to convert glyph to bitmap"); return false; } + FT_Bitmap& bitmap = reinterpret_cast(glyph)->bitmap; + // Dans le cas où nous voulons des caractères gras mais que nous n'avons pas pu agir plus tôt // nous demandons à FreeType d'agir directement sur le bitmap généré if (embolden) { // http://www.freetype.org/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden - // "If you want to embolden the bitmap owned by a FT_GlyphSlot_Rec, you should call FT_GlyphSlot_Own_Bitmap on the slot first" - FT_GlyphSlot_Own_Bitmap(glyph); - FT_Bitmap_Embolden(s_library, &glyph->bitmap, boldStrength, boldStrength); + FT_Bitmap_Embolden(s_library, &bitmap, boldStrength, boldStrength); } - dst->advance += glyph->metrics.horiAdvance >> 6; - dst->aabb.x = glyph->metrics.horiBearingX >> 6; - dst->aabb.y = -(glyph->metrics.horiBearingY >> 6); // Inversion du repère - dst->aabb.width = glyph->metrics.width >> 6; - dst->aabb.height = glyph->metrics.height >> 6; + int outlineThicknessInt = static_cast(outlineThickness * 2.f + 0.5f); //< round it + dst->advance += glyphSlot->metrics.horiAdvance >> 6; + dst->aabb.x = glyphSlot->metrics.horiBearingX >> 6; + dst->aabb.y = -(glyphSlot->metrics.horiBearingY >> 6); // Inversion du repère + dst->aabb.width = (glyphSlot->metrics.width >> 6) + outlineThicknessInt; + dst->aabb.height = (glyphSlot->metrics.height >> 6) + outlineThicknessInt; - unsigned int width = glyph->bitmap.width; - unsigned int height = glyph->bitmap.rows; + unsigned int width = bitmap.width; + unsigned int height = bitmap.rows; if (width > 0 && height > 0) { dst->image.Create(ImageType_2D, PixelFormatType_A8, width, height); UInt8* pixels = dst->image.GetPixels(); - const UInt8* data = glyph->bitmap.buffer; + const UInt8* data = bitmap.buffer; // Selon la documentation FreeType, le glyphe peut être encodé en format A8 (huit bits d'alpha par pixel) // ou au format A1 (un bit d'alpha par pixel). // Cependant dans un cas comme dans l'autre, il nous faut gérer le pitch (les données peuvent ne pas être contigues) // ainsi que le padding dans le cas du format A1 (Chaque ligne prends un nombre fixe d'octets) - if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) + if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { // Format A1 for (unsigned int y = 0; y < height; ++y) @@ -176,20 +218,20 @@ namespace Nz for (unsigned int x = 0; x < width; ++x) *pixels++ = (data[x/8] & ((1 << (7 - x%8)) ? 255 : 0)); - data += glyph->bitmap.pitch; + data += bitmap.pitch; } } else { // Format A8 - if (glyph->bitmap.pitch == static_cast(width*sizeof(UInt8))) // Pouvons-nous copier directement ? - dst->image.Update(glyph->bitmap.buffer); + if (bitmap.pitch == static_cast(width*sizeof(UInt8))) // Pouvons-nous copier directement ? + dst->image.Update(bitmap.buffer); //< Small optimization else { for (unsigned int y = 0; y < height; ++y) { std::memcpy(pixels, data, width*sizeof(UInt8)); - data += glyph->bitmap.pitch; + data += bitmap.pitch; pixels += width*sizeof(UInt8); } } @@ -312,7 +354,12 @@ namespace Nz m_args.stream = &m_stream; } - bool SupportsStyle(UInt32 style) const override + bool SupportsOutline(float /*outlineThickness*/) const override + { + return s_stroker != 0; + } + + bool SupportsStyle(TextStyleFlags style) const override { ///TODO return style == TextStyle_Regular || style == TextStyle_Bold; diff --git a/src/Nazara/Utility/GuillotineImageAtlas.cpp b/src/Nazara/Utility/GuillotineImageAtlas.cpp index 4660a69b8..2593bc368 100644 --- a/src/Nazara/Utility/GuillotineImageAtlas.cpp +++ b/src/Nazara/Utility/GuillotineImageAtlas.cpp @@ -19,8 +19,6 @@ namespace Nz { } - GuillotineImageAtlas::~GuillotineImageAtlas() = default; - void GuillotineImageAtlas::Clear() { m_layers.clear(); diff --git a/src/Nazara/Utility/SimpleTextDrawer.cpp b/src/Nazara/Utility/SimpleTextDrawer.cpp index 89accf012..86ba50764 100644 --- a/src/Nazara/Utility/SimpleTextDrawer.cpp +++ b/src/Nazara/Utility/SimpleTextDrawer.cpp @@ -10,9 +10,11 @@ namespace Nz { SimpleTextDrawer::SimpleTextDrawer() : m_color(Color::White), + m_outlineColor(Color::Black), m_style(TextStyle_Regular), m_colorUpdated(true), m_glyphUpdated(true), + m_outlineThickness(0.f), m_characterSize(24) { SetFont(Font::GetDefault()); @@ -24,6 +26,8 @@ namespace Nz m_style(drawer.m_style), m_colorUpdated(false), m_glyphUpdated(false), + m_outlineColor(drawer.m_outlineColor), + m_outlineThickness(drawer.m_outlineThickness), m_characterSize(drawer.m_characterSize) { SetFont(drawer.m_font); @@ -120,7 +124,17 @@ namespace Nz return m_lines.size(); } - UInt32 SimpleTextDrawer::GetStyle() const + const Color& SimpleTextDrawer::GetOutlineColor() const + { + return m_outlineColor; + } + + float SimpleTextDrawer::GetOutlineThickness() const + { + return m_outlineThickness; + } + + TextStyleFlags SimpleTextDrawer::GetStyle() const { return m_style; } @@ -159,7 +173,23 @@ namespace Nz } } - void SimpleTextDrawer::SetStyle(UInt32 style) + void SimpleTextDrawer::SetOutlineColor(const Color& color) + { + m_outlineColor = color; + + m_colorUpdated = false; + } + + void SimpleTextDrawer::SetOutlineThickness(float thickness) + { + NazaraAssert(thickness >= 0.f, "Thickness must be zero or positive"); + + m_outlineThickness = thickness; + + m_glyphUpdated = false; + } + + void SimpleTextDrawer::SetStyle(TextStyleFlags style) { m_style = style; @@ -177,6 +207,8 @@ namespace Nz { m_characterSize = drawer.m_characterSize; m_color = drawer.m_color; + m_outlineColor = drawer.m_outlineColor; + m_outlineThickness = drawer.m_outlineThickness; m_style = drawer.m_style; m_text = drawer.m_text; @@ -198,6 +230,8 @@ namespace Nz m_glyphs = std::move(drawer.m_glyphs); m_glyphUpdated = std::move(drawer.m_glyphUpdated); m_font = std::move(drawer.m_font); + m_outlineColor = std::move(drawer.m_outlineColor); + m_outlineThickness = std::move(drawer.m_outlineThickness); m_style = std::move(drawer.m_style); m_text = std::move(drawer.m_text); @@ -207,7 +241,7 @@ namespace Nz return *this; } - SimpleTextDrawer SimpleTextDrawer::Draw(const String& str, unsigned int characterSize, UInt32 style, const Color& color) + SimpleTextDrawer SimpleTextDrawer::Draw(const String& str, unsigned int characterSize, TextStyleFlags style, const Color& color) { SimpleTextDrawer drawer; drawer.SetCharacterSize(characterSize); @@ -218,7 +252,20 @@ namespace Nz return drawer; } - SimpleTextDrawer SimpleTextDrawer::Draw(Font* font, const String& str, unsigned int characterSize, UInt32 style, const Color& color) + SimpleTextDrawer SimpleTextDrawer::Draw(const String& str, unsigned int characterSize, TextStyleFlags style, const Color& color, float outlineThickness, const Color& outlineColor) + { + SimpleTextDrawer drawer; + drawer.SetCharacterSize(characterSize); + drawer.SetColor(color); + drawer.SetOutlineColor(outlineColor); + drawer.SetOutlineThickness(outlineThickness); + drawer.SetStyle(style); + drawer.SetText(str); + + return drawer; + } + + SimpleTextDrawer SimpleTextDrawer::Draw(Font* font, const String& str, unsigned int characterSize, TextStyleFlags style, const Color& color) { SimpleTextDrawer drawer; drawer.SetCharacterSize(characterSize); @@ -230,6 +277,20 @@ namespace Nz return drawer; } + SimpleTextDrawer SimpleTextDrawer::Draw(Font* font, const String& str, unsigned int characterSize, TextStyleFlags style, const Color& color, float outlineThickness, const Color& outlineColor) + { + SimpleTextDrawer drawer; + drawer.SetCharacterSize(characterSize); + drawer.SetColor(color); + drawer.SetFont(font); + drawer.SetOutlineColor(outlineColor); + drawer.SetOutlineThickness(outlineThickness); + drawer.SetStyle(style); + drawer.SetText(str); + + return drawer; + } + void SimpleTextDrawer::ClearGlyphs() const { m_bounds.MakeZero(); @@ -278,7 +339,7 @@ namespace Nz const Font::SizeInfo& sizeInfo = m_font->GetSizeInfo(m_characterSize); - m_glyphs.reserve(m_glyphs.size() + characters.size()); + m_glyphs.reserve(m_glyphs.size() + characters.size() * (m_outlineThickness > 0.f) ? 2 : 1); for (char32_t character : characters) { if (m_previousCharacter != 0) @@ -304,51 +365,57 @@ namespace Nz break; } + auto GenerateGlyph = [this](Glyph& glyph, char32_t character, float outlineThickness, Nz::Color color, int renderOrder, int* advance) + { + const Font::Glyph& fontGlyph = m_font->GetGlyph(m_characterSize, m_style, outlineThickness, character); + if (fontGlyph.valid && fontGlyph.fauxOutlineThickness <= 0.f) + { + glyph.atlas = m_font->GetAtlas()->GetLayer(fontGlyph.layerIndex); + glyph.atlasRect = fontGlyph.atlasRect; + glyph.color = color; + glyph.flipped = fontGlyph.flipped; + glyph.renderOrder = renderOrder; + + glyph.bounds.Set(fontGlyph.aabb); + glyph.bounds.x += m_drawPos.x; + glyph.bounds.y += m_drawPos.y; + + // Faux bold and faux outline thickness are not supported + + // We "lean" the glyph to simulate italics style + float italic = (fontGlyph.requireFauxItalic) ? 0.208f : 0.f; + float italicTop = italic * glyph.bounds.y; + float italicBottom = italic * glyph.bounds.GetMaximum().y; + + glyph.corners[0].Set(glyph.bounds.x - italicTop - outlineThickness, glyph.bounds.y - outlineThickness); + glyph.corners[1].Set(glyph.bounds.x + glyph.bounds.width - italicTop - outlineThickness, glyph.bounds.y - outlineThickness); + glyph.corners[2].Set(glyph.bounds.x - italicBottom - outlineThickness, glyph.bounds.y + glyph.bounds.height - outlineThickness); + glyph.corners[3].Set(glyph.bounds.x + glyph.bounds.width - italicBottom - outlineThickness, glyph.bounds.y + glyph.bounds.height - outlineThickness); + + if (advance) + *advance = fontGlyph.advance; + + return true; + } + else + return false; + }; + Glyph glyph; if (!whitespace) { - const Font::Glyph& fontGlyph = m_font->GetGlyph(m_characterSize, m_style, character); - if (!fontGlyph.valid) + if (!GenerateGlyph(glyph, character, 0.f, m_color, 0, &advance)) continue; // Glyph failed to load, just skip it (can't do much) - advance = fontGlyph.advance; - - glyph.atlas = m_font->GetAtlas()->GetLayer(fontGlyph.layerIndex); - glyph.atlasRect = fontGlyph.atlasRect; - glyph.color = m_color; - glyph.flipped = fontGlyph.flipped; - - glyph.bounds.Set(fontGlyph.aabb); - glyph.bounds.x += m_drawPos.x; - glyph.bounds.y += m_drawPos.y; - - if (fontGlyph.requireFauxBold) + if (m_outlineThickness > 0.f) { - // Let's simulate bold by enlarging the glyph (not a neat idea, but should work) - Vector2f center = glyph.bounds.GetCenter(); - - // Enlarge by 10% - glyph.bounds.width *= 1.1f; - glyph.bounds.height *= 1.1f; - - // Replace it at the correct height - Vector2f offset(glyph.bounds.GetCenter() - center); - glyph.bounds.x -= offset.x; - glyph.bounds.y -= offset.y; - - // Adjust advance (+10%) - advance += advance / 10; + Glyph outlineGlyph; + if (GenerateGlyph(outlineGlyph, character, m_outlineThickness, m_outlineColor, -1, nullptr)) + { + m_lines.back().bounds.ExtendTo(outlineGlyph.bounds); + m_glyphs.push_back(outlineGlyph); + } } - - // We "lean" the glyph to simulate italics style - float italic = (fontGlyph.requireFauxItalic) ? 0.208f : 0.f; - float italicTop = italic * glyph.bounds.y; - float italicBottom = italic * glyph.bounds.GetMaximum().y; - - glyph.corners[0].Set(glyph.bounds.x - italicTop, glyph.bounds.y); - glyph.corners[1].Set(glyph.bounds.x + glyph.bounds.width - italicTop, glyph.bounds.y); - glyph.corners[2].Set(glyph.bounds.x - italicBottom, glyph.bounds.y + glyph.bounds.height); - glyph.corners[3].Set(glyph.bounds.x + glyph.bounds.width - italicBottom, glyph.bounds.y + glyph.bounds.height); } else { @@ -363,7 +430,7 @@ namespace Nz } m_lines.back().bounds.ExtendTo(glyph.bounds); - + switch (character) { case '\n': @@ -448,8 +515,22 @@ namespace Nz void SimpleTextDrawer::UpdateGlyphColor() const { - for (Glyph& glyph : m_glyphs) - glyph.color = m_color; + if (m_outlineThickness > 0.f) + { + for (std::size_t glyphIndex = 0; glyphIndex < m_glyphs.size(); ++glyphIndex) + { + Glyph& glyph = m_glyphs[glyphIndex]; + if (glyphIndex % 2 == 0) + glyph.color = m_outlineColor; + else + glyph.color = m_color; + } + } + else + { + for (Glyph& glyph : m_glyphs) + glyph.color = m_color; + } m_colorUpdated = true; } diff --git a/tests/Engine/Physics2D/RigidBody2D.cpp b/tests/Engine/Physics2D/RigidBody2D.cpp index 7eae99908..e554746d8 100644 --- a/tests/Engine/Physics2D/RigidBody2D.cpp +++ b/tests/Engine/Physics2D/RigidBody2D.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include Nz::RigidBody2D CreateBody(Nz::PhysWorld2D& world); @@ -93,6 +94,7 @@ SCENARIO("RigidBody2D", "[PHYSICS2D][RIGIDBODY2D]") std::vector tmp; tmp.push_back(CreateBody(world)); tmp.push_back(CreateBody(world)); + world.Step(1.f); THEN("They should be valid") @@ -112,11 +114,14 @@ SCENARIO("RigidBody2D", "[PHYSICS2D][RIGIDBODY2D]") Nz::Rectf aabb(positionAABB.x, positionAABB.y, 1.f, 2.f); Nz::Collider2DRef box = Nz::BoxCollider2D::New(aabb); float mass = 1.f; - Nz::RigidBody2D body(&world, mass, box); + Nz::RigidBody2D body(&world, mass); + body.SetGeom(box, true, false); + bool userData = false; body.SetUserdata(&userData); Nz::Vector2f position = Nz::Vector2f::Zero(); + body.SetPosition(position); world.Step(1.f); @@ -126,7 +131,7 @@ SCENARIO("RigidBody2D", "[PHYSICS2D][RIGIDBODY2D]") { CHECK(body.GetAABB() == aabb); CHECK(body.GetAngularVelocity() == 0.f); - CHECK(body.GetMassCenter() == Nz::Vector2f::Zero()); + CHECK(body.GetMassCenter(Nz::CoordSys_Global) == position); CHECK(body.GetGeom() == box); CHECK(body.GetMass() == Approx(mass)); CHECK(body.GetPosition() == position); @@ -150,7 +155,7 @@ SCENARIO("RigidBody2D", "[PHYSICS2D][RIGIDBODY2D]") { aabb.Translate(velocity); CHECK(body.GetAABB() == aabb); - CHECK(body.GetMassCenter() == Nz::Vector2f::Zero()); + CHECK(body.GetMassCenter(Nz::CoordSys_Global) == position); CHECK(body.GetPosition() == position); CHECK(body.GetVelocity() == velocity); } @@ -211,7 +216,9 @@ SCENARIO("RigidBody2D", "[PHYSICS2D][RIGIDBODY2D]") float radius = 5.f; Nz::Collider2DRef circle = Nz::CircleCollider2D::New(radius, position); float mass = 1.f; - Nz::RigidBody2D body(&world, mass, circle); + Nz::RigidBody2D body(&world, mass); + body.SetGeom(circle, true, false); + world.Step(1.f); WHEN("We ask for the aabb of the circle") @@ -240,7 +247,9 @@ SCENARIO("RigidBody2D", "[PHYSICS2D][RIGIDBODY2D]") Nz::CompoundCollider2DRef compound = Nz::CompoundCollider2D::New(colliders); float mass = 1.f; - Nz::RigidBody2D body(&world, mass, compound); + Nz::RigidBody2D body(&world, mass); + body.SetGeom(compound, true, false); + world.Step(1.f); WHEN("We ask for the aabb of the compound") @@ -267,7 +276,9 @@ SCENARIO("RigidBody2D", "[PHYSICS2D][RIGIDBODY2D]") Nz::SparsePtr sparsePtr(vertices.data()); Nz::ConvexCollider2DRef convex = Nz::ConvexCollider2D::New(sparsePtr, vertices.size()); float mass = 1.f; - Nz::RigidBody2D body(&world, mass, convex); + Nz::RigidBody2D body(&world, mass); + body.SetGeom(convex, true, false); + world.Step(1.f); WHEN("We ask for the aabb of the convex") @@ -289,7 +300,9 @@ SCENARIO("RigidBody2D", "[PHYSICS2D][RIGIDBODY2D]") Nz::Vector2f positionB(1.f, -4.f); Nz::Collider2DRef segment = Nz::SegmentCollider2D::New(positionA, positionB, 0.f); float mass = 1.f; - Nz::RigidBody2D body(&world, mass, segment); + Nz::RigidBody2D body(&world, mass); + body.SetGeom(segment, true, false); + world.Step(1.f); WHEN("We ask for the aabb of the segment") @@ -309,7 +322,11 @@ Nz::RigidBody2D CreateBody(Nz::PhysWorld2D& world) Nz::Rectf aabb(positionAABB.x, positionAABB.y, 1.f, 2.f); Nz::Collider2DRef box = Nz::BoxCollider2D::New(aabb); float mass = 1.f; - return Nz::RigidBody2D(&world, mass, box); + + Nz::RigidBody2D body(&world, mass, box); + body.SetPosition(Nz::Vector2f::Zero()); + + return body; } void EQUALITY(const Nz::RigidBody2D& left, const Nz::RigidBody2D& right) diff --git a/tests/SDK/NDK/EntityOwner.cpp b/tests/SDK/NDK/EntityOwner.cpp index 2d240a6d0..b7ce87e15 100644 --- a/tests/SDK/NDK/EntityOwner.cpp +++ b/tests/SDK/NDK/EntityOwner.cpp @@ -105,4 +105,25 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]") } } } + + GIVEN("A vector of EntityOwner") + { + Ndk::World world(false); + + std::vector entityOwners; + for (std::size_t i = 1; i <= 10; ++i) + { + for (const Ndk::EntityHandle& entity : world.CreateEntities(10 * i)) + entityOwners.emplace_back(entity); + + entityOwners.clear(); + world.Refresh(); + + std::size_t aliveEntities = 0; + for (const Ndk::EntityHandle& entity : world.GetEntities()) + aliveEntities++; + + CHECK(aliveEntities == 0); + } + } } diff --git a/tests/SDK/NDK/Systems/PhysicsSystem2D.cpp b/tests/SDK/NDK/Systems/PhysicsSystem2D.cpp index d94797065..770f1e190 100644 --- a/tests/SDK/NDK/Systems/PhysicsSystem2D.cpp +++ b/tests/SDK/NDK/Systems/PhysicsSystem2D.cpp @@ -6,7 +6,7 @@ #include #include -Ndk::EntityHandle CreateBaseEntity(Ndk::World& world, const Nz::Vector2f& position, const Nz::Rectf AABB); +Ndk::EntityHandle CreateBaseEntity(Ndk::World& world, const Nz::Vector2f& position, const Nz::Rectf& AABB); SCENARIO("PhysicsSystem2D", "[NDK][PHYSICSSYSTEM2D]") { @@ -80,6 +80,8 @@ SCENARIO("PhysicsSystem2D", "[NDK][PHYSICSSYSTEM2D]") Ndk::EntityHandle movingEntity = CreateBaseEntity(world, position, movingAABB); Ndk::NodeComponent& nodeComponent = movingEntity->GetComponent(); Ndk::PhysicsComponent2D& physicsComponent2D = movingEntity->AddComponent(); + physicsComponent2D.SetMassCenter(Nz::Vector2f::Zero()); + physicsComponent2D.SetPosition(position); world.GetSystem().SetFixedUpdateRate(30.f); @@ -124,6 +126,8 @@ SCENARIO("PhysicsSystem2D", "[NDK][PHYSICSSYSTEM2D]") Ndk::EntityHandle movingEntity = CreateBaseEntity(world, position, movingAABB); Ndk::NodeComponent& nodeComponent = movingEntity->GetComponent(); Ndk::PhysicsComponent2D& physicsComponent2D = movingEntity->AddComponent(); + physicsComponent2D.SetMassCenter(Nz::Vector2f::Zero()); + physicsComponent2D.SetPosition(position); world.GetSystem().SetFixedUpdateRate(30.f); @@ -145,7 +149,7 @@ SCENARIO("PhysicsSystem2D", "[NDK][PHYSICSSYSTEM2D]") } } -Ndk::EntityHandle CreateBaseEntity(Ndk::World& world, const Nz::Vector2f& position, const Nz::Rectf AABB) +Ndk::EntityHandle CreateBaseEntity(Ndk::World& world, const Nz::Vector2f& position, const Nz::Rectf& AABB) { Ndk::EntityHandle entity = world.CreateEntity(); Ndk::NodeComponent& nodeComponent = entity->AddComponent(); diff --git a/tests/SDK/NDK/Systems/RenderSystem.cpp b/tests/SDK/NDK/Systems/RenderSystem.cpp index 329818f74..e30fa3a03 100644 --- a/tests/SDK/NDK/Systems/RenderSystem.cpp +++ b/tests/SDK/NDK/Systems/RenderSystem.cpp @@ -90,17 +90,17 @@ SCENARIO("RenderSystem", "[NDK][RenderSystem]") { CHECK(physicsComponent2D.GetAngularVelocity() == angularSpeed); CHECK(physicsComponent2D.GetRotation() == angularSpeed); - CHECK(physicsComponent2D.GetAABB() == Nz::Rectf(1.f, 4.f, 2.f, 1.f)); + CHECK(physicsComponent2D.GetAABB() == Nz::Rectf(2.5f, 4.5f, 2.f, 1.f)); CompareAABB(physicsComponent2D.GetAABB(), graphicsComponent.GetAABB()); world.Update(1.f); CHECK(physicsComponent2D.GetRotation() == 2.f * angularSpeed); - CHECK(physicsComponent2D.GetAABB() == Nz::Rectf(2.f, 2.f, 1.f, 2.f)); + CHECK(physicsComponent2D.GetAABB() == Nz::Rectf(3.f, 4.0f, 1.f, 2.f)); CompareAABB(physicsComponent2D.GetAABB(), graphicsComponent.GetAABB()); world.Update(1.f); CHECK(physicsComponent2D.GetRotation() == 3.f * angularSpeed); - CHECK(physicsComponent2D.GetAABB() == Nz::Rectf(3.f, 3.f, 2.f, 1.f)); + CHECK(physicsComponent2D.GetAABB() == Nz::Rectf(2.5f, 4.5f, 2.f, 1.f)); CompareAABB(physicsComponent2D.GetAABB(), graphicsComponent.GetAABB()); world.Update(1.f); diff --git a/tests/SDK/NDK/World.cpp b/tests/SDK/NDK/World.cpp index 185717609..f98a7e2e1 100644 --- a/tests/SDK/NDK/World.cpp +++ b/tests/SDK/NDK/World.cpp @@ -126,4 +126,37 @@ SCENARIO("World", "[NDK][WORLD]") } } } + + GIVEN("An empty world") + { + Ndk::World world(false); + + WHEN("We create two entities") + { + Ndk::EntityHandle a = world.CreateEntity(); + REQUIRE(a->GetId() == 0); + Ndk::EntityHandle b = world.CreateEntity(); + REQUIRE(b->GetId() == 1); + + b->OnEntityDestruction.Connect([a](Ndk::Entity*) + { + REQUIRE(a.IsValid()); + a->Kill(); + }); + + THEN("We kill the second entity which will kill the first one") + { + b->Kill(); + world.Refresh(); + + AND_THEN("Both entities should be dead next refresh") + { + world.Refresh(); + + REQUIRE_FALSE(a.IsValid()); + REQUIRE_FALSE(b.IsValid()); + } + } + } + } } diff --git a/thirdparty/include/GL3/glcorearb.h b/thirdparty/include/GL/glcorearb.h similarity index 100% rename from thirdparty/include/GL3/glcorearb.h rename to thirdparty/include/GL/glcorearb.h diff --git a/thirdparty/include/GL3/glext.h b/thirdparty/include/GL/glext.h similarity index 100% rename from thirdparty/include/GL3/glext.h rename to thirdparty/include/GL/glext.h diff --git a/thirdparty/include/GL3/glxext.h b/thirdparty/include/GL/glxext.h similarity index 100% rename from thirdparty/include/GL3/glxext.h rename to thirdparty/include/GL/glxext.h diff --git a/thirdparty/include/GL3/wglext.h b/thirdparty/include/GL/wglext.h similarity index 100% rename from thirdparty/include/GL3/wglext.h rename to thirdparty/include/GL/wglext.h diff --git a/thirdparty/src/Lua/loadlib.cpp b/thirdparty/src/Lua/loadlib.cpp index a20e68efc..f5319da42 100644 --- a/thirdparty/src/Lua/loadlib.cpp +++ b/thirdparty/src/Lua/loadlib.cpp @@ -176,6 +176,8 @@ static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { ** ======================================================================= */ +#define WIN32_LEAN_AND_MEAN + #include #undef setprogdir diff --git a/thirdparty/src/chipmunk/chipmunk.c b/thirdparty/src/chipmunk/chipmunk.c index 503265cf6..a6cc9d6d4 100644 --- a/thirdparty/src/chipmunk/chipmunk.c +++ b/thirdparty/src/chipmunk/chipmunk.c @@ -89,7 +89,7 @@ cpAreaForSegment(cpVect a, cpVect b, cpFloat r) } cpFloat -cpMomentForPoly(cpFloat m, const int count, const cpVect *verts, cpVect offset, cpFloat r) +cpMomentForPoly(cpFloat m, int count, const cpVect *verts, cpVect offset, cpFloat r) { // TODO account for radius. if(count == 2) return cpMomentForSegment(m, verts[0], verts[1], 0.0f);