Merge branch 'master' into console-widget
This commit is contained in:
commit
2c3bf482de
59
ChangeLog.md
59
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)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <NDK/Components/ConstraintComponent2D.hpp>
|
||||
#include <NDK/Components/DebugComponent.hpp>
|
||||
#include <NDK/Components/GraphicsComponent.hpp>
|
||||
#include <NDK/Components/LifetimeComponent.hpp>
|
||||
#include <NDK/Components/LightComponent.hpp>
|
||||
#include <NDK/Components/ListenerComponent.hpp>
|
||||
#include <NDK/Components/NodeComponent.hpp>
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ namespace Ndk
|
|||
|
||||
class NDK_API CollisionComponent2D : public Component<CollisionComponent2D>
|
||||
{
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace Ndk
|
|||
{
|
||||
enum class DebugDraw
|
||||
{
|
||||
//TODO: Collider2D
|
||||
Collider2D,
|
||||
Collider3D,
|
||||
GraphicsAABB,
|
||||
GraphicsOBB,
|
||||
|
|
|
|||
|
|
@ -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 <NDK/Component.hpp>
|
||||
|
||||
namespace Ndk
|
||||
{
|
||||
class LifetimeComponent;
|
||||
|
||||
using LifetimeComponentHandle = Nz::ObjectHandle<LifetimeComponent>;
|
||||
|
||||
class NDK_API LifetimeComponent : public Component<LifetimeComponent>
|
||||
{
|
||||
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 <NDK/Components/LifetimeComponent.inl>
|
||||
|
||||
#endif // NDK_COMPONENTS_LIFETIMECOMPONENT_HPP
|
||||
|
|
@ -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 <NDK/Components/LifetimeComponent.hpp>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<void(Nz::Arbiter2D&)>& 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,15 @@ namespace Ndk
|
|||
m_entity->Invalidate();
|
||||
}
|
||||
|
||||
/*!
|
||||
TODO
|
||||
*/
|
||||
inline void PhysicsComponent2D::ForEachArbiter(const std::function<void(Nz::Arbiter2D&)>& 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
|
||||
|
|
|
|||
|
|
@ -19,10 +19,11 @@ namespace Ndk
|
|||
class NDK_API VelocityComponent : public Component<VelocityComponent>
|
||||
{
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#define NDK_SYSTEMS_GLOBAL_HPP
|
||||
|
||||
#include <NDK/Systems/DebugSystem.hpp>
|
||||
#include <NDK/Systems/LifetimeSystem.hpp>
|
||||
#include <NDK/Systems/ListenerSystem.hpp>
|
||||
#include <NDK/Systems/ParticleSystem.hpp>
|
||||
#include <NDK/Systems/PhysicsSystem2D.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();
|
||||
|
|
|
|||
|
|
@ -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 <NDK/System.hpp>
|
||||
|
||||
namespace Ndk
|
||||
{
|
||||
class NDK_API LifetimeSystem : public System<LifetimeSystem>
|
||||
{
|
||||
public:
|
||||
LifetimeSystem();
|
||||
~LifetimeSystem() = default;
|
||||
|
||||
static SystemIndex systemIndex;
|
||||
|
||||
private:
|
||||
void OnUpdate(float elapsedTime) override;
|
||||
};
|
||||
}
|
||||
|
||||
#include <NDK/Systems/LifetimeSystem.inl>
|
||||
|
||||
#endif // NDK_SYSTEMS_LIFETIMESYSTEM_HPP
|
||||
|
|
@ -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
|
||||
|
|
@ -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<void(const RaycastHit&)>& callback);
|
||||
bool RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector<RaycastHit>* 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<void(const EntityHandle&)>& callback);
|
||||
void RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector<EntityHandle>* bodies);
|
||||
|
||||
void RegisterCallbacks(unsigned int collisionId, Callback callbacks);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef NDK_WIDGETS_GLOBAL_HPP
|
||||
#define NDK_WIDGETS_GLOBAL_HPP
|
||||
|
||||
#include <NDK/Widgets/BoxLayout.hpp>
|
||||
#include <NDK/Widgets/ButtonWidget.hpp>
|
||||
#include <NDK/Widgets/CheckboxWidget.hpp>
|
||||
#include <NDK/Widgets/Enums.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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -98,6 +98,12 @@ namespace Ndk
|
|||
inline void InvalidateSystemOrder();
|
||||
void ReorderSystems();
|
||||
|
||||
struct DoubleBitset
|
||||
{
|
||||
Nz::Bitset<Nz::UInt64> front;
|
||||
Nz::Bitset<Nz::UInt64> back;
|
||||
};
|
||||
|
||||
struct EntityBlock
|
||||
{
|
||||
EntityBlock(Entity&& e) :
|
||||
|
|
@ -119,9 +125,9 @@ namespace Ndk
|
|||
std::vector<std::unique_ptr<EntityBlock>> m_waitingEntities;
|
||||
EntityList m_aliveEntities;
|
||||
ProfilerData m_profilerData;
|
||||
Nz::Bitset<Nz::UInt64> m_dirtyEntities;
|
||||
DoubleBitset m_dirtyEntities;
|
||||
Nz::Bitset<Nz::UInt64> m_freeEntityIds;
|
||||
Nz::Bitset<Nz::UInt64> m_killedEntities;
|
||||
DoubleBitset m_killedEntities;
|
||||
bool m_orderedSystemsUpdated;
|
||||
bool m_isProfilerEnabled;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<PhysicsComponent2D>())
|
||||
{
|
||||
// We update the geometry of the PhysiscsObject linked to the PhysicsComponent2D
|
||||
PhysicsComponent2D& physComponent = m_entity->GetComponent<PhysicsComponent2D>();
|
||||
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>())
|
||||
{
|
||||
PhysicsComponent2D& physComponent = m_entity->GetComponent<PhysicsComponent2D>();
|
||||
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>())
|
||||
{
|
||||
PhysicsComponent2D& physComponent = m_entity->GetComponent<PhysicsComponent2D>();
|
||||
return physComponent.GetRigidBody();
|
||||
}
|
||||
else
|
||||
{
|
||||
NazaraAssert(m_staticBody, "An entity without physics component should have a static body");
|
||||
return m_staticBody.get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -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<RenderSystem>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <NDK/Components/LifetimeComponent.hpp>
|
||||
|
||||
namespace Ndk
|
||||
{
|
||||
ComponentIndex LifetimeComponent::componentIndex;
|
||||
}
|
||||
|
|
@ -31,9 +31,17 @@ namespace Ndk
|
|||
|
||||
Nz::PhysWorld2D& world = entityWorld->GetSystem<PhysicsSystem2D>().GetPhysWorld();
|
||||
|
||||
Nz::Vector2f positionOffset;
|
||||
|
||||
Nz::Collider2DRef geom;
|
||||
if (m_entity->HasComponent<CollisionComponent2D>())
|
||||
geom = m_entity->GetComponent<CollisionComponent2D>().GetGeom();
|
||||
{
|
||||
const CollisionComponent2D& entityCollision = m_entity->GetComponent<CollisionComponent2D>();
|
||||
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<NodeComponent>())
|
||||
|
|
@ -42,6 +50,7 @@ namespace Ndk
|
|||
matrix.MakeIdentity();
|
||||
|
||||
m_object = std::make_unique<Nz::RigidBody2D>(&world, 1.f, geom);
|
||||
m_object->SetPositionOffset(positionOffset);
|
||||
m_object->SetPosition(Nz::Vector2f(matrix.GetTranslation()));
|
||||
m_object->SetUserdata(reinterpret_cast<void*>(static_cast<std::ptrdiff_t>(m_entity->GetId())));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,12 +123,13 @@ namespace Ndk
|
|||
lua.Push(instance->GetCachedGlyphCount());
|
||||
return 1;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
unsigned int characterSize = lua.Check<unsigned int>(&argIndex);
|
||||
Nz::UInt32 style = lua.Check<Nz::UInt32>(&argIndex);
|
||||
Nz::TextStyleFlags style = lua.Check<Nz::TextStyleFlags>(&argIndex);
|
||||
float outlineThickness = lua.Check<float>(&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);
|
||||
|
|
|
|||
|
|
@ -17,11 +17,13 @@
|
|||
#include <NDK/BaseSystem.hpp>
|
||||
#include <NDK/Components/CollisionComponent2D.hpp>
|
||||
#include <NDK/Components/CollisionComponent3D.hpp>
|
||||
#include <NDK/Components/LifetimeComponent.hpp>
|
||||
#include <NDK/Components/NodeComponent.hpp>
|
||||
#include <NDK/Components/PhysicsComponent2D.hpp>
|
||||
#include <NDK/Components/PhysicsComponent3D.hpp>
|
||||
#include <NDK/Components/VelocityComponent.hpp>
|
||||
#include <NDK/Components/ConstraintComponent2D.hpp>
|
||||
#include <NDK/Systems/LifetimeSystem.hpp>
|
||||
#include <NDK/Systems/PhysicsSystem2D.hpp>
|
||||
#include <NDK/Systems/PhysicsSystem3D.hpp>
|
||||
#include <NDK/Systems/VelocitySystem.hpp>
|
||||
|
|
@ -88,6 +90,7 @@ namespace Ndk
|
|||
// Shared components
|
||||
InitializeComponent<CollisionComponent2D>("NdkColl2");
|
||||
InitializeComponent<CollisionComponent3D>("NdkColl3");
|
||||
InitializeComponent<LifetimeComponent>("NdkLiftm");
|
||||
InitializeComponent<NodeComponent>("NdkNode");
|
||||
InitializeComponent<PhysicsComponent2D>("NdkPhys2");
|
||||
InitializeComponent<PhysicsComponent3D>("NdkPhys3");
|
||||
|
|
@ -110,6 +113,7 @@ namespace Ndk
|
|||
BaseSystem::Initialize();
|
||||
|
||||
// Shared systems
|
||||
InitializeSystem<LifetimeSystem>();
|
||||
InitializeSystem<PhysicsSystem2D>();
|
||||
InitializeSystem<PhysicsSystem3D>();
|
||||
InitializeSystem<VelocitySystem>();
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@
|
|||
#include <Nazara/Utility/IndexIterator.hpp>
|
||||
#include <Nazara/Utility/Mesh.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
#include <NDK/Components/CollisionComponent2D.hpp>
|
||||
#include <NDK/Components/CollisionComponent3D.hpp>
|
||||
#include <NDK/Components/DebugComponent.hpp>
|
||||
#include <NDK/Components/GraphicsComponent.hpp>
|
||||
#include <NDK/Components/NodeComponent.hpp>
|
||||
#include <NDK/Components/PhysicsComponent2D.hpp>
|
||||
|
||||
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>())
|
||||
{
|
||||
CollisionComponent2D& entityCollision = entity->GetComponent<CollisionComponent2D>();
|
||||
const Nz::Collider2DRef& geom = entityCollision.GetGeom();
|
||||
|
||||
std::vector<Nz::Vector3f> vertices;
|
||||
std::vector<std::size_t> 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<Nz::UInt32>(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<PhysicsComponent2D>())
|
||||
{
|
||||
const PhysicsComponent2D& entityPhys = entity->GetComponent<PhysicsComponent2D>();
|
||||
*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<Nz::Vector3f> vertices;
|
||||
std::vector<std::size_t> 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;
|
||||
|
|
|
|||
|
|
@ -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 <NDK/Systems/LifetimeSystem.hpp>
|
||||
#include <NDK/Components/LifetimeComponent.hpp>
|
||||
|
||||
namespace Ndk
|
||||
{
|
||||
LifetimeSystem::LifetimeSystem()
|
||||
{
|
||||
Requires<LifetimeComponent>();
|
||||
}
|
||||
|
||||
void LifetimeSystem::OnUpdate(float elapsedTime)
|
||||
{
|
||||
for (const Ndk::EntityHandle& entity : GetEntities())
|
||||
{
|
||||
auto& lifetime = entity->GetComponent<LifetimeComponent>();
|
||||
|
||||
if (lifetime.UpdateLifetime(elapsedTime))
|
||||
entity->Kill();
|
||||
}
|
||||
}
|
||||
|
||||
SystemIndex LifetimeSystem::systemIndex;
|
||||
}
|
||||
|
|
@ -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<void(const RaycastHit&)>& 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<RaycastHit>* 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<void(const EntityHandle&)>& 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<EntityHandle>* 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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace Ndk
|
|||
NodeComponent& node = entity->GetComponent<NodeComponent>();
|
||||
const VelocityComponent& velocity = entity->GetComponent<VelocityComponent>();
|
||||
|
||||
node.Move(velocity.linearVelocity * elapsedTime, Nz::CoordSys_Global);
|
||||
node.Move(velocity.linearVelocity * elapsedTime, velocity.coordSys);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Ndk
|
|||
BaseWidget(parent)
|
||||
{
|
||||
m_entity = CreateEntity();
|
||||
m_entity->AddComponent<NodeComponent>();
|
||||
m_entity->AddComponent<NodeComponent>().SetParent(this);
|
||||
auto& gfx = m_entity->AddComponent<GraphicsComponent>();
|
||||
|
||||
m_sprite = Nz::Sprite::New();
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <Nazara/Core/Clock.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <NDK/BaseComponent.hpp>
|
||||
#include <NDK/Systems/LifetimeSystem.hpp>
|
||||
#include <NDK/Systems/PhysicsSystem2D.hpp>
|
||||
#include <NDK/Systems/PhysicsSystem3D.hpp>
|
||||
#include <NDK/Systems/VelocitySystem.hpp>
|
||||
|
|
@ -43,6 +44,7 @@ namespace Ndk
|
|||
|
||||
void World::AddDefaultSystems()
|
||||
{
|
||||
AddSystem<LifetimeSystem>();
|
||||
AddSystem<PhysicsSystem2D>();
|
||||
AddSystem<PhysicsSystem3D>();
|
||||
AddSystem<VelocitySystem>();
|
||||
|
|
@ -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<Ndk::ComponentIndex>(j));
|
||||
removedComponents.Reset();
|
||||
|
||||
|
|
@ -260,7 +264,7 @@ namespace Ndk
|
|||
}
|
||||
}
|
||||
}
|
||||
m_dirtyEntities.Reset();
|
||||
m_dirtyEntities.back.Clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Ndk::NodeComponent>();
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -18,13 +18,23 @@ namespace Nz
|
|||
/*!
|
||||
* \brief Constructs a Signal object by default
|
||||
*/
|
||||
|
||||
template<typename... Args>
|
||||
Signal<Args...>::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<typename ...Args>
|
||||
Signal<Args...>::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<typename... Args>
|
||||
Signal<Args...>& Signal<Args...>::operator=(const Signal&)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Moves the signal into this
|
||||
* \return A reference to this
|
||||
*
|
||||
* \param signal Signal to move in this
|
||||
*/
|
||||
|
||||
template<typename... Args>
|
||||
Signal<Args...>& Signal<Args...>::operator=(Signal&& signal) noexcept
|
||||
{
|
||||
|
|
|
|||
|
|
@ -408,12 +408,7 @@ namespace Nz
|
|||
|
||||
template<typename T>
|
||||
template<CullTest Type>
|
||||
#ifdef NAZARA_COMPILER_MSVC
|
||||
// MSVC bug
|
||||
typename CullingList<T>::Entry<Type>& CullingList<T>::Entry<Type>::operator=(Entry&& entry)
|
||||
#else
|
||||
typename CullingList<T>::template Entry<Type>& CullingList<T>::Entry<Type>::operator=(Entry&& entry)
|
||||
#endif
|
||||
{
|
||||
m_index = entry.m_index;
|
||||
m_parent = entry.m_parent;
|
||||
|
|
|
|||
|
|
@ -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<const Shader*, ShaderUniforms> m_shaderUniforms;
|
||||
mutable std::vector<std::pair<const VertexStruct_XYZ_Color_UV*, std::size_t>> m_spriteChains;
|
||||
mutable std::vector<SpriteBatch> m_spriteBatches;
|
||||
Buffer m_vertexBuffer;
|
||||
RenderStates m_clearStates;
|
||||
ShaderRef m_clearShader;
|
||||
|
|
|
|||
|
|
@ -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<const Shader*, ShaderUniforms> m_shaderUniforms;
|
||||
mutable std::vector<std::pair<const VertexStruct_XYZ_Color_UV*, std::size_t>> m_spriteChains;
|
||||
mutable std::vector<SpriteBatch> m_spriteBatches;
|
||||
Buffer m_vertexBuffer;
|
||||
RenderStates m_clearStates;
|
||||
ShaderRef m_clearShader;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Texture*>()(key.texture) + key.renderOrder;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderIndices
|
||||
{
|
||||
unsigned int first;
|
||||
|
|
@ -74,7 +99,7 @@ namespace Nz
|
|||
};
|
||||
|
||||
std::unordered_map<const AbstractAtlas*, AtlasSlots> m_atlases;
|
||||
mutable std::unordered_map<Texture*, RenderIndices> m_renderInfos;
|
||||
mutable std::unordered_map<RenderKey, RenderIndices, HashRenderKey> m_renderInfos;
|
||||
mutable std::vector<VertexStruct_XY_Color_UV> m_localVertices;
|
||||
Color m_color;
|
||||
Recti m_localBounds;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,15 +35,15 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
template<typename T> /*constexpr*/ T Approach(T value, T objective, T increment);
|
||||
template<typename T> constexpr T Approach(T value, T objective, T increment);
|
||||
template<typename T> constexpr T Clamp(T value, T min, T max);
|
||||
template<typename T> /*constexpr*/ std::size_t CountBits(T value);
|
||||
template<typename T> constexpr std::size_t CountBits(T value);
|
||||
template<typename T> constexpr T FromDegrees(T degrees);
|
||||
template<typename T> constexpr T FromRadians(T radians);
|
||||
template<typename T> constexpr T DegreeToRadian(T degrees);
|
||||
template<typename T> /*constexpr*/ T GetNearestPowerOfTwo(T number);
|
||||
/*constexpr*/ unsigned int GetNumberLength(signed char number);
|
||||
/*constexpr*/ unsigned int GetNumberLength(unsigned char number);
|
||||
template<typename T> 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<typename T> /*constexpr*/ unsigned int IntegralLog2(T number);
|
||||
template<typename T> /*constexpr*/ unsigned int IntegralLog2Pot(T pot);
|
||||
template<typename T> /*constexpr*/ T IntegralPow(T base, unsigned int exponent);
|
||||
template<typename T> constexpr T IntegralPow(T base, unsigned int exponent);
|
||||
template<typename T, typename T2> constexpr T Lerp(const T& from, const T& to, const T2& interpolation);
|
||||
template<typename T> constexpr T MultiplyAdd(T x, T y, T z);
|
||||
template<typename T> /*constexpr*/ T NormalizeAngle(T angle);
|
||||
template<typename T> /*constexpr*/ bool NumberEquals(T a, T b);
|
||||
template<typename T> /*constexpr*/ bool NumberEquals(T a, T b, T maxDifference);
|
||||
template<typename T> constexpr T NormalizeAngle(T angle);
|
||||
template<typename T> constexpr bool NumberEquals(T a, T b);
|
||||
template<typename T> constexpr bool NumberEquals(T a, T b, T maxDifference);
|
||||
String NumberToString(long long number, UInt8 radix = 10);
|
||||
template<typename T> constexpr T RadianToDegree(T radians);
|
||||
long long StringToNumber(String str, UInt8 radix = 10, bool* ok = nullptr);
|
||||
|
|
|
|||
|
|
@ -98,13 +98,13 @@ namespace Nz
|
|||
return 0;
|
||||
}
|
||||
|
||||
template<typename T> /*constexpr*/ std::enable_if_t<std::is_floating_point<T>::value, bool> NumberEquals(T a, T b, T maxDifference)
|
||||
template<typename T> constexpr std::enable_if_t<std::is_floating_point<T>::value, bool> NumberEquals(T a, T b, T maxDifference)
|
||||
{
|
||||
T diff = std::abs(a - b);
|
||||
return diff <= maxDifference;
|
||||
}
|
||||
|
||||
template<typename T> /*constexpr*/ std::enable_if_t<!std::is_signed<T>::value || (!std::is_integral<T>::value && !std::is_floating_point<T>::value), bool> NumberEquals(T a, T b, T maxDifference)
|
||||
template<typename T> constexpr std::enable_if_t<!std::is_signed<T>::value || (!std::is_integral<T>::value && !std::is_floating_point<T>::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<typename T> /*constexpr*/ std::enable_if_t<std::is_signed<T>::value && std::is_integral<T>::value, bool> NumberEquals(T a, T b, T maxDifference)
|
||||
template<typename T> constexpr std::enable_if_t<std::is_signed<T>::value && std::is_integral<T>::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<typename T>
|
||||
//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<typename T>
|
||||
constexpr T Clamp(T value, T min, T max)
|
||||
{
|
||||
|
|
@ -168,10 +165,8 @@ namespace Nz
|
|||
*
|
||||
* \param value The value to count bits
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
//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<typename T>
|
||||
constexpr T DegreeToRadian(T degrees)
|
||||
{
|
||||
|
|
@ -205,7 +199,6 @@ namespace Nz
|
|||
*
|
||||
* \param degrees Convert degree to NAZARA_MATH_ANGLE_RADIAN unit
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
constexpr T FromDegrees(T degrees)
|
||||
{
|
||||
|
|
@ -223,7 +216,6 @@ namespace Nz
|
|||
*
|
||||
* \param radians Convert radian to NAZARA_MATH_ANGLE_RADIAN unit
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
constexpr T FromRadians(T radians)
|
||||
{
|
||||
|
|
@ -241,10 +233,8 @@ namespace Nz
|
|||
*
|
||||
* \param number Number to get nearest power
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
//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<typename T>
|
||||
//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<typename T>
|
||||
//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<typename T>
|
||||
/*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<typename T, typename T2>
|
||||
constexpr T Lerp(const T& from, const T& to, const T2& interpolation)
|
||||
{
|
||||
|
|
@ -540,10 +514,8 @@ namespace Nz
|
|||
*
|
||||
* \param angle Angle to normalize
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
//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<typename T>
|
||||
//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<T>::epsilon());
|
||||
}
|
||||
|
|
@ -584,10 +554,8 @@ namespace Nz
|
|||
* \param b Second value
|
||||
* \param maxDifference Epsilon of comparison (expected to be positive)
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
//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<typename T>
|
||||
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<typename T>
|
||||
constexpr T ToDegrees(T angle)
|
||||
{
|
||||
|
|
@ -745,7 +709,6 @@ namespace Nz
|
|||
*
|
||||
* \param angle Convert degree from NAZARA_MATH_ANGLE_RADIAN unit to radians
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
constexpr T ToRadians(T angle)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -109,7 +109,9 @@ namespace Nz
|
|||
using Vector2i = Vector2<int>;
|
||||
using Vector2ui = Vector2<unsigned int>;
|
||||
using Vector2i32 = Vector2<Int32>;
|
||||
using Vector2i64 = Vector2<Int64>;
|
||||
using Vector2ui32 = Vector2<UInt32>;
|
||||
using Vector2ui64 = Vector2<UInt64>;
|
||||
|
||||
template<typename T> bool Serialize(SerializationContext& context, const Vector2<T>& vector, TypeTag<Vector2<T>>);
|
||||
template<typename T> bool Unserialize(SerializationContext& context, Vector2<T>* vector, TypeTag<Vector2<T>>);
|
||||
|
|
|
|||
|
|
@ -129,7 +129,9 @@ namespace Nz
|
|||
using Vector3i = Vector3<int>;
|
||||
using Vector3ui = Vector3<unsigned int>;
|
||||
using Vector3i32 = Vector3<Int32>;
|
||||
using Vector3i64 = Vector3<Int64>;
|
||||
using Vector3ui32 = Vector3<UInt32>;
|
||||
using Vector3ui64 = Vector3<UInt64>;
|
||||
|
||||
template<typename T> bool Serialize(SerializationContext& context, const Vector3<T>& vector, TypeTag<Vector3<T>>);
|
||||
template<typename T> bool Unserialize(SerializationContext& context, Vector3<T>* vector, TypeTag<Vector3<T>>);
|
||||
|
|
|
|||
|
|
@ -106,7 +106,9 @@ namespace Nz
|
|||
using Vector4i = Vector4<int>;
|
||||
using Vector4ui = Vector4<unsigned int>;
|
||||
using Vector4i32 = Vector4<Int32>;
|
||||
using Vector4i64 = Vector4<Int64>;
|
||||
using Vector4ui32 = Vector4<UInt32>;
|
||||
using Vector4ui64 = Vector4<UInt64>;
|
||||
|
||||
template<typename T> bool Serialize(SerializationContext& context, const Vector4<T>& vector, TypeTag<Vector4<T>>);
|
||||
template<typename T> bool Unserialize(SerializationContext& context, Vector4<T>* vector, TypeTag<Vector4<T>>);
|
||||
|
|
|
|||
|
|
@ -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<typename T>
|
||||
T Vector4<T>::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<typename T>
|
||||
Vector4<T> Vector4<T>::Lerp(const Vector4& from, const Vector4& to, T interpolation)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<RigidBody2D*, RigidBody2D*> 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;
|
||||
|
|
|
|||
|
|
@ -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<void(const Vector2f* vertices, std::size_t vertexCount)>& 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<Collider2DRef> 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<const Vector2f> 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;
|
||||
|
|
|
|||
|
|
@ -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<void(const RaycastHit&)>& callback);
|
||||
bool RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector<RaycastHit>* 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<void(Nz::RigidBody2D*)>& callback);
|
||||
void RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector<Nz::RigidBody2D*>* 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<void(Nz::RigidBody2D* body)>;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,21 +14,25 @@
|
|||
#include <Nazara/Math/Rect.hpp>
|
||||
#include <Nazara/Physics2D/Config.hpp>
|
||||
#include <Nazara/Physics2D/Collider2D.hpp>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
struct cpBody;
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class Arbiter2D;
|
||||
class PhysWorld2D;
|
||||
|
||||
class NAZARA_PHYSICS2D_API RigidBody2D
|
||||
{
|
||||
public:
|
||||
using VelocityFunc = std::function<void(RigidBody2D& body2D, const Nz::Vector2f& gravity, float damping, float deltaTime)>;
|
||||
|
||||
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<void(Nz::Arbiter2D& /*arbiter*/)> 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<cpShape*> m_shapes;
|
||||
Collider2DRef m_geom;
|
||||
cpBody* m_handle;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ namespace Nz
|
|||
virtual void ComputeInertialMatrix(Vector3f* inertia, Vector3f* center) const;
|
||||
virtual float ComputeVolume() const;
|
||||
|
||||
virtual void ForEachPolygon(const std::function<void(const float* vertices, std::size_t vertexCount)>& callback) const;
|
||||
virtual void ForEachPolygon(const std::function<void(const Vector3f* vertices, std::size_t vertexCount)>& callback) const;
|
||||
|
||||
NewtonCollision* GetHandle(PhysWorld3D* world) const;
|
||||
virtual ColliderType3D GetType() const = 0;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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<WindowImpl> 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<WindowEvent> m_events;
|
||||
std::vector<WindowEvent> m_pendingEvents;
|
||||
ConditionVariable m_eventCondition;
|
||||
|
|
|
|||
|
|
@ -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 <Nazara/Platform/Window.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/Core/LockGuard.hpp>
|
||||
#include <Nazara/Platform/Debug.hpp>
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 <Nazara/Core/Config.hpp>
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -17,16 +17,18 @@
|
|||
#include <Nazara/Utility/Enums.hpp>
|
||||
|
||||
// Inclusion des headers OpenGL
|
||||
#include <GL3/glcorearb.h>
|
||||
#include <GL3/glext.h>
|
||||
|
||||
#include <GL/glcorearb.h>
|
||||
#include <GL/glext.h>
|
||||
|
||||
#if defined(NAZARA_PLATFORM_WINDOWS)
|
||||
#include <GL3/wglext.h>
|
||||
#include <GL/wglext.h>
|
||||
#elif defined(NAZARA_PLATFORM_GLX)
|
||||
namespace GLX
|
||||
{
|
||||
#include <GL/glx.h> // Defined in a namespace to avoid conflict
|
||||
}
|
||||
#include <GL3/glxext.h>
|
||||
#include <GL/glxext.h>
|
||||
#endif
|
||||
|
||||
namespace Nz
|
||||
|
|
|
|||
|
|
@ -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*/);
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ namespace Nz
|
|||
Vector2f corners[4];
|
||||
AbstractImage* atlas;
|
||||
bool flipped;
|
||||
int renderOrder;
|
||||
};
|
||||
|
||||
struct Line
|
||||
|
|
|
|||
|
|
@ -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<TextStyle>
|
||||
{
|
||||
static constexpr TextStyle max = TextStyle_Max;
|
||||
};
|
||||
|
||||
using TextStyleFlags = Flags<TextStyle>;
|
||||
|
||||
constexpr TextStyleFlags TextStyle_Regular = 0;
|
||||
|
||||
enum VertexComponent
|
||||
{
|
||||
VertexComponent_Unused = -1,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <Nazara/Core/ResourceLoader.hpp>
|
||||
#include <Nazara/Core/ResourceParameters.hpp>
|
||||
#include <Nazara/Utility/AbstractAtlas.hpp>
|
||||
#include <Nazara/Utility/Enums.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
|
|
@ -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<AbstractAtlas>& 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<AbstractAtlas>& 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<char32_t, Glyph>;
|
||||
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Core/String.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <Nazara/Utility/Enums.hpp>
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Glyph> m_glyphs;
|
||||
mutable std::vector<Line> 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;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,11 +25,14 @@ SOFTWARE.
|
|||
#include <CustomStream.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/String.hpp>
|
||||
#include <Nazara/Utility/Animation.hpp>
|
||||
#include <Nazara/Utility/Mesh.hpp>
|
||||
#include <Nazara/Utility/IndexIterator.hpp>
|
||||
#include <Nazara/Utility/IndexMapper.hpp>
|
||||
#include <Nazara/Utility/Joint.hpp>
|
||||
#include <Nazara/Utility/MaterialData.hpp>
|
||||
#include <Nazara/Utility/Sequence.hpp>
|
||||
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||
#include <Nazara/Utility/Skeleton.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
#include <Nazara/Utility/VertexMapper.hpp>
|
||||
|
|
@ -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<char*>(&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<unsigned int, std::pair<UInt32, ParameterList>> 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<UInt16>::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<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_ReadWrite);
|
||||
SkeletalMeshVertex* vertices = static_cast<SkeletalMeshVertex*>(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<UInt8>(color.r * 255), static_cast<UInt8>(color.g * 255), static_cast<UInt8>(color.b * 255), static_cast<UInt8>(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<long long>(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>(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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<std::size_t N>
|
||||
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
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ namespace Nz
|
|||
Vector2f uv;
|
||||
};
|
||||
|
||||
UInt32 s_maxQuads = std::numeric_limits<UInt16>::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<std::size_t>(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4);
|
||||
|
||||
const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
|
||||
const std::size_t maxSpriteCount = std::min<std::size_t>(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<VertexBuffer> vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite);
|
||||
VertexStruct_XYZ_Color_UV* vertices = static_cast<VertexStruct_XYZ_Color_UV*>(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<VertexBuffer> 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<VertexStruct_XYZ_Color_UV*>(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<IndexBuffer> mapper(s_quadIndexBuffer, BufferAccess_WriteOnly);
|
||||
UInt16* indices = static_cast<UInt16*>(mapper.GetPointer());
|
||||
UInt32* indices = static_cast<UInt32*>(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;
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ namespace Nz
|
|||
Vector2f uv;
|
||||
};
|
||||
|
||||
unsigned int s_maxQuads = std::numeric_limits<UInt16>::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<IndexBuffer> mapper(s_quadIndexBuffer, BufferAccess_WriteOnly);
|
||||
UInt16* indices = static_cast<UInt16*>(mapper.GetPointer());
|
||||
UInt32* indices = static_cast<UInt32*>(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<std::size_t>(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4);
|
||||
|
||||
const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
|
||||
const std::size_t maxSpriteCount = std::min<std::size_t>(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<VertexBuffer> vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite);
|
||||
VertexStruct_XYZ_Color_UV* vertices = static_cast<VertexStruct_XYZ_Color_UV*>(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<VertexBuffer> 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<VertexStruct_XYZ_Color_UV*>(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();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ namespace Nz
|
|||
Vector2f uv;
|
||||
};
|
||||
|
||||
UInt32 s_maxQuads = std::numeric_limits<UInt16>::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<IndexBuffer> mapper(s_quadIndexBuffer, BufferAccess_WriteOnly);
|
||||
UInt16* indices = static_cast<UInt16*>(mapper.GetPointer());
|
||||
UInt32* indices = static_cast<UInt32*>(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<std::size_t>(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4);
|
||||
|
||||
const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
|
||||
const std::size_t maxSpriteCount = std::min<std::size_t>(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4);
|
||||
|
||||
m_spriteBatches.clear();
|
||||
{
|
||||
BufferMapper<VertexBuffer> vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite);
|
||||
VertexStruct_XYZ_Color_UV* vertices = static_cast<VertexStruct_XYZ_Color_UV*>(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<VertexBuffer> 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<VertexStruct_XYZ_Color_UV*>(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
|
||||
|
|
|
|||
|
|
@ -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<const VertexStruct_XYZ_Color_UV*>(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<Texture*>(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<Texture*>(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<Texture*>(oldLayer);
|
||||
Texture* newTexture = static_cast<Texture*>(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<Vector2f> texCoordPtr(&m_localVertices[indices.first].uv, sizeof(VertexStruct_XYZ_Color_UV));
|
||||
// Adjust texture coordinates by size ratio
|
||||
SparsePtr<Vector2f> 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...
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,14 @@ namespace Nz
|
|||
{
|
||||
const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<const VertexStruct_XYZ_Color_UV*>(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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<UInt16>(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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,19 @@ namespace Nz
|
|||
return Nz::Vector2f(Nz::Vector2<cpFloat>(impulse.x, impulse.y));
|
||||
}
|
||||
|
||||
std::pair<RigidBody2D*, RigidBody2D*> Arbiter2D::GetBodies() const
|
||||
{
|
||||
std::pair<RigidBody2D*, RigidBody2D*> bodies;
|
||||
cpBody* firstBody;
|
||||
cpBody* secondBody;
|
||||
cpArbiterGetBodies(m_arbiter, &firstBody, &secondBody);
|
||||
|
||||
bodies.first = static_cast<RigidBody2D*>(cpBodyGetUserData(firstBody));
|
||||
bodies.second = static_cast<RigidBody2D*>(cpBodyGetUserData(secondBody));
|
||||
|
||||
return bodies;
|
||||
}
|
||||
|
||||
std::size_t Arbiter2D::GetContactCount() const
|
||||
{
|
||||
return cpArbiterGetCount(m_arbiter);
|
||||
|
|
|
|||
|
|
@ -3,14 +3,81 @@
|
|||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Physics2D/Collider2D.hpp>
|
||||
#include <Nazara/Math/Quaternion.hpp>
|
||||
#include <Nazara/Physics2D/RigidBody2D.hpp>
|
||||
#include <Nazara/Physics2D/PhysWorld2D.hpp>
|
||||
#include <chipmunk/chipmunk.h>
|
||||
#include <Nazara/Physics3D/Debug.hpp>
|
||||
#include <chipmunk/chipmunk_structs.h>
|
||||
#include <array>
|
||||
#include <Nazara/Physics2D/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
Collider2D::~Collider2D() = default;
|
||||
|
||||
void Collider2D::ForEachPolygon(const std::function<void(const Vector2f* vertices, std::size_t vertexCount)>& 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<cpShape*> shapeVector;
|
||||
rigidBody.SetGeom(const_cast<Collider2D*>(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<Vector2f, circleVerticesCount> 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<float, float> 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<Vector2f, 2> 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<float, float> 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<Vector2f, 4> 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<cpShape*>* 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<float>(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<float>(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<const cpVect*>(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<float>(cpMomentForSegment(mass, cpv(m_first.x, m_first.y), cpv(m_second.x, m_second.y), m_thickness));
|
||||
|
|
|
|||
|
|
@ -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<void(const RaycastHit&)>& callback)
|
||||
{
|
||||
using CallbackType = const std::function<void(const RaycastHit&)>;
|
||||
|
||||
auto cpCallback = [](cpShape* shape, cpVect point, cpVect normal, cpFloat alpha, void* data)
|
||||
{
|
||||
CallbackType& callback = *static_cast<CallbackType*>(data);
|
||||
|
||||
RaycastHit hitInfo;
|
||||
hitInfo.fraction = float(alpha);
|
||||
hitInfo.hitNormal.Set(Nz::Vector2<cpFloat>(normal.x, normal.y));
|
||||
hitInfo.hitPos.Set(Nz::Vector2<cpFloat>(point.x, point.y));
|
||||
hitInfo.nearestBody = static_cast<Nz::RigidBody2D*>(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<void*>(static_cast<const void*>(&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<RaycastHit>* 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<void(Nz::RigidBody2D*)>& callback)
|
||||
{
|
||||
using CallbackType = const std::function<void(Nz::RigidBody2D*)>;
|
||||
|
||||
auto cpCallback = [](cpShape* shape, void* data)
|
||||
{
|
||||
CallbackType& callback = *static_cast<CallbackType*>(data);
|
||||
callback(static_cast<Nz::RigidBody2D*>(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<void*>(static_cast<const void*>(&callback)));
|
||||
}
|
||||
|
||||
void PhysWorld2D::RegionQuery(const Nz::Rectf& boundingBox, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector<Nz::RigidBody2D*>* 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<Callback>(callbacks)).first;
|
||||
auto it = m_callbacks.find(handler);
|
||||
if (it == m_callbacks.end())
|
||||
it = m_callbacks.emplace(handler, std::make_unique<Callback>(std::move(callbacks))).first;
|
||||
else
|
||||
it->second = std::make_unique<Callback>(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<const Callback*>(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<const Callback*>(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<const Callback*>(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<const Callback*>(data);
|
||||
customCallbacks->postSolveCallback(*world, arbiter, *firstRigidBody, *secondRigidBody, customCallbacks->userdata);
|
||||
|
||||
cpArbiterCallWildcardPostSolveA(arb, space);
|
||||
cpArbiterCallWildcardPostSolveB(arb, space);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->postSolveFunc = [](cpArbiter* arb, cpSpace* space, void* data)
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Physics2D/RigidBody2D.hpp>
|
||||
#include <Nazara/Physics2D/Arbiter2D.hpp>
|
||||
#include <Nazara/Physics2D/PhysWorld2D.hpp>
|
||||
#include <chipmunk/chipmunk.h>
|
||||
#include <chipmunk/chipmunk_private.h>
|
||||
|
|
@ -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<void(Nz::Arbiter2D&)> callback)
|
||||
{
|
||||
using CallbackType = decltype(callback);
|
||||
|
||||
auto RealCallback = [](cpBody* body, cpArbiter* arbiter, void* data)
|
||||
{
|
||||
CallbackType& cb = *static_cast<CallbackType*>(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<float>(pos.x), static_cast<float>(pos.y));
|
||||
}
|
||||
|
||||
|
|
@ -280,6 +299,11 @@ namespace Nz
|
|||
return Vector2f(static_cast<float>(vel.x), static_cast<float>(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<RigidBody2D*>(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));
|
||||
|
|
|
|||
|
|
@ -114,12 +114,14 @@ namespace Nz
|
|||
return volume;
|
||||
}
|
||||
|
||||
void Collider3D::ForEachPolygon(const std::function<void(const float* vertices, std::size_t vertexCount)>& callback) const
|
||||
void Collider3D::ForEachPolygon(const std::function<void(const Vector3f* vertices, std::size_t vertexCount)>& 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<std::add_pointer_t<decltype(callback)>>(userData);
|
||||
cb(faceArray, vertexCount);
|
||||
cb(reinterpret_cast<const Vector3f*>(faceArray), vertexCount);
|
||||
};
|
||||
|
||||
// Check for existing collision handles, and create a temporary one if none is available
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ namespace Nz
|
|||
|
||||
bool CursorImpl::Create(SystemCursor cursor)
|
||||
{
|
||||
if (cursor != SystemCursor_Move)
|
||||
if (cursor != SystemCursor_None)
|
||||
m_cursor = static_cast<HCURSOR>(LoadImage(nullptr, s_systemCursorIds[cursor], IMAGE_CURSOR, 0, 0, LR_SHARED));
|
||||
else
|
||||
m_cursor = nullptr;
|
||||
|
|
|
|||
|
|
@ -292,5 +292,5 @@ namespace Nz
|
|||
}
|
||||
else
|
||||
NazaraError("Invalid window handle");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ namespace Nz
|
|||
m_smoothScrolling(false),
|
||||
m_scrolling(0)
|
||||
{
|
||||
m_cursor = static_cast<HCURSOR>(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;
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ namespace Nz
|
|||
bool IsMinimized() const;
|
||||
bool IsVisible() const;
|
||||
|
||||
void RefreshCursor();
|
||||
|
||||
void ProcessEvents(bool block);
|
||||
|
||||
void SetCursor(const Cursor& cursor);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -412,6 +412,10 @@ namespace Nz
|
|||
}
|
||||
}
|
||||
|
||||
void WindowImpl::RefreshCursor()
|
||||
{
|
||||
}
|
||||
|
||||
void WindowImpl::SetCursor(const Cursor& cursor)
|
||||
{
|
||||
xcb_cursor_t cursorImpl = cursor.m_impl->GetCursor();
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ namespace Nz
|
|||
bool IsMinimized() const;
|
||||
bool IsVisible() const;
|
||||
|
||||
void RefreshCursor();
|
||||
|
||||
void ProcessEvents(bool block);
|
||||
|
||||
void SetCursor(const Cursor& cursor);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<AbstractAtlas>& 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<UInt64>(first) << 32) | second; // Combinaison de deux caractères 32 bits dans un nombre 64 bits
|
||||
UInt64 key = (static_cast<UInt64>(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<UInt32>((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<UInt32>((characterSize/m_minimumStepSize)*m_minimumStepSize);
|
||||
sizeStylePart = std::min<UInt64>(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<Nz::UInt32&>(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;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
#include <freetype/ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_BITMAP_H
|
||||
#include FT_STROKER_H
|
||||
#include FT_OUTLINE_H
|
||||
#include <Nazara/Core/CallOnExit.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/MemoryView.hpp>
|
||||
#include <Nazara/Core/Stream.hpp>
|
||||
|
|
@ -24,8 +26,10 @@ namespace Nz
|
|||
class FreeTypeLibrary;
|
||||
|
||||
FT_Library s_library;
|
||||
FT_Stroker s_stroker;
|
||||
std::shared_ptr<FreeTypeLibrary> 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<FT_OutlineGlyph>(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<FT_Fixed>(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<FT_BitmapGlyph>(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<int>(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<int>(width*sizeof(UInt8))) // Pouvons-nous copier directement ?
|
||||
dst->image.Update(glyph->bitmap.buffer);
|
||||
if (bitmap.pitch == static_cast<int>(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;
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ namespace Nz
|
|||
{
|
||||
}
|
||||
|
||||
GuillotineImageAtlas::~GuillotineImageAtlas() = default;
|
||||
|
||||
void GuillotineImageAtlas::Clear()
|
||||
{
|
||||
m_layers.clear();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue