Compare commits
8 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
| f59dd8498e | |||
| f4a63f1235 | |||
| f9185e7b26 | |||
| 5fdcb6989f | |||
| a453612b9a | |||
| 23e7707122 | |||
| b73d09f09f | |||
| dc5b58d7d3 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -77,7 +77,6 @@ CMakeUserPresets.json
|
|||||||
*.dSYM/
|
*.dSYM/
|
||||||
*.su
|
*.su
|
||||||
*.idb
|
*.idb
|
||||||
*.pdb
|
|
||||||
|
|
||||||
# Kernel Module Compile Results
|
# Kernel Module Compile Results
|
||||||
*.mod*
|
*.mod*
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ std::ofstream TSE::Debug::logFile;
|
|||||||
const std::string fileName = "log.txt";
|
const std::string fileName = "log.txt";
|
||||||
const std::string fileNameOld = "lastlog.txt";
|
const std::string fileNameOld = "lastlog.txt";
|
||||||
|
|
||||||
|
std::vector<void(*)(const std::string&, const TSE::LogEntryType&)> TSE::Debug::onLogCallbacks = std::vector<void(*)(const std::string&, const LogEntryType&)>();
|
||||||
|
|
||||||
int luaPrintRedirect(lua_State* L)
|
int luaPrintRedirect(lua_State* L)
|
||||||
{
|
{
|
||||||
int n = lua_gettop(L);
|
int n = lua_gettop(L);
|
||||||
@@ -61,18 +63,30 @@ void TSE::Debug::Log(string msg)
|
|||||||
{
|
{
|
||||||
std::cout << msg << std::endl;
|
std::cout << msg << std::endl;
|
||||||
logFile << msg << std::endl;
|
logFile << msg << std::endl;
|
||||||
|
for(auto fnc : onLogCallbacks)
|
||||||
|
{
|
||||||
|
fnc(msg, LogEntryType::Log);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSE::Debug::Error(string msg)
|
void TSE::Debug::Error(string msg)
|
||||||
{
|
{
|
||||||
std::cerr << "[Error] " << msg << std::endl;
|
std::cerr << "[Error] " << msg << std::endl;
|
||||||
logFile << "[Error] " << msg << std::endl;
|
logFile << "[Error] " << msg << std::endl;
|
||||||
|
for(auto fnc : onLogCallbacks)
|
||||||
|
{
|
||||||
|
fnc("[Error] " + msg, LogEntryType::Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSE::Debug::Warning(string msg)
|
void TSE::Debug::Warning(string msg)
|
||||||
{
|
{
|
||||||
std::cout << "[Warning] " << msg << std::endl;
|
std::cout << "[Warning] " << msg << std::endl;
|
||||||
logFile << "[Warning] " << msg << std::endl;
|
logFile << "[Warning] " << msg << std::endl;
|
||||||
|
for(auto fnc : onLogCallbacks)
|
||||||
|
{
|
||||||
|
fnc("[Warning] " + msg, LogEntryType::Warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSE::Debug::Close()
|
void TSE::Debug::Close()
|
||||||
@@ -80,3 +94,8 @@ void TSE::Debug::Close()
|
|||||||
logFile.flush();
|
logFile.flush();
|
||||||
logFile.close();
|
logFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TSE::Debug::AddCallback(void (*func)(const std::string &, const LogEntryType &))
|
||||||
|
{
|
||||||
|
onLogCallbacks.push_back(func);
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,13 @@
|
|||||||
|
|
||||||
namespace TSE
|
namespace TSE
|
||||||
{
|
{
|
||||||
|
enum LogEntryType
|
||||||
|
{
|
||||||
|
Log,
|
||||||
|
Error,
|
||||||
|
Warning,
|
||||||
|
};
|
||||||
|
|
||||||
class Debug
|
class Debug
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -25,9 +32,11 @@ namespace TSE
|
|||||||
static void Warning(string msg);
|
static void Warning(string msg);
|
||||||
/// @brief closes the log file
|
/// @brief closes the log file
|
||||||
static void Close();
|
static void Close();
|
||||||
|
static void AddCallback(void(*func)(const std::string&, const LogEntryType&));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::ofstream logFile;
|
static std::ofstream logFile;
|
||||||
|
static std::vector<void(*)(const std::string&, const LogEntryType&)> onLogCallbacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace TSE
|
} // namespace TSE
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace TSE
|
|||||||
{
|
{
|
||||||
#define TSE_VERSION_MAJOR 0
|
#define TSE_VERSION_MAJOR 0
|
||||||
#define TSE_VERSION_MINOR 1
|
#define TSE_VERSION_MINOR 1
|
||||||
#define TSE_VERSION_BUILD 0
|
#define TSE_VERSION_BUILD 2
|
||||||
|
|
||||||
#define TSE_VERSION_STRING std::to_string(TSE_VERSION_MAJOR) + "." + std::to_string(TSE_VERSION_MINOR) + "." + std::to_string(TSE_VERSION_BUILD)
|
#define TSE_VERSION_STRING std::to_string(TSE_VERSION_MAJOR) + "." + std::to_string(TSE_VERSION_MINOR) + "." + std::to_string(TSE_VERSION_BUILD)
|
||||||
|
|
||||||
|
|||||||
BIN
TSE_Core/include/box2d.lib
Normal file
BIN
TSE_Core/include/box2d.lib
Normal file
Binary file not shown.
131
TSE_Core/include/box2d/base.h
Normal file
131
TSE_Core/include/box2d/base.h
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Erin Catto
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
//
|
||||||
|
// Shared library macros
|
||||||
|
#if defined( _MSC_VER ) && defined( box2d_EXPORTS )
|
||||||
|
// build the Windows DLL
|
||||||
|
#define BOX2D_EXPORT __declspec( dllexport )
|
||||||
|
#elif defined( _MSC_VER ) && defined( BOX2D_DLL )
|
||||||
|
// using the Windows DLL
|
||||||
|
#define BOX2D_EXPORT __declspec( dllimport )
|
||||||
|
#elif defined( box2d_EXPORTS )
|
||||||
|
// building or using the shared library
|
||||||
|
#define BOX2D_EXPORT __attribute__( ( visibility( "default" ) ) )
|
||||||
|
#else
|
||||||
|
// static library
|
||||||
|
#define BOX2D_EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// C++ macros
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define B2_API extern "C" BOX2D_EXPORT
|
||||||
|
#define B2_INLINE inline
|
||||||
|
#define B2_LITERAL(T) T
|
||||||
|
#define B2_ZERO_INIT {}
|
||||||
|
#else
|
||||||
|
#define B2_API BOX2D_EXPORT
|
||||||
|
#define B2_INLINE static inline
|
||||||
|
/// Used for C literals like (b2Vec2){1.0f, 2.0f} where C++ requires b2Vec2{1.0f, 2.0f}
|
||||||
|
#define B2_LITERAL(T) (T)
|
||||||
|
#define B2_ZERO_INIT {0}
|
||||||
|
#endif
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup base Base
|
||||||
|
* Base functionality
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Prototype for user allocation function
|
||||||
|
/// @param size the allocation size in bytes
|
||||||
|
/// @param alignment the required alignment, guaranteed to be a power of 2
|
||||||
|
typedef void* b2AllocFcn( unsigned int size, int alignment );
|
||||||
|
|
||||||
|
/// Prototype for user free function
|
||||||
|
/// @param mem the memory previously allocated through `b2AllocFcn`
|
||||||
|
typedef void b2FreeFcn( void* mem );
|
||||||
|
|
||||||
|
/// Prototype for the user assert callback. Return 0 to skip the debugger break.
|
||||||
|
typedef int b2AssertFcn( const char* condition, const char* fileName, int lineNumber );
|
||||||
|
|
||||||
|
/// This allows the user to override the allocation functions. These should be
|
||||||
|
/// set during application startup.
|
||||||
|
B2_API void b2SetAllocator( b2AllocFcn* allocFcn, b2FreeFcn* freeFcn );
|
||||||
|
|
||||||
|
/// @return the total bytes allocated by Box2D
|
||||||
|
B2_API int b2GetByteCount( void );
|
||||||
|
|
||||||
|
/// Override the default assert callback
|
||||||
|
/// @param assertFcn a non-null assert callback
|
||||||
|
B2_API void b2SetAssertFcn( b2AssertFcn* assertFcn );
|
||||||
|
|
||||||
|
/// Version numbering scheme.
|
||||||
|
/// See https://semver.org/
|
||||||
|
typedef struct b2Version
|
||||||
|
{
|
||||||
|
/// Significant changes
|
||||||
|
int major;
|
||||||
|
|
||||||
|
/// Incremental changes
|
||||||
|
int minor;
|
||||||
|
|
||||||
|
/// Bug fixes
|
||||||
|
int revision;
|
||||||
|
} b2Version;
|
||||||
|
|
||||||
|
/// Get the current version of Box2D
|
||||||
|
B2_API b2Version b2GetVersion( void );
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
//! @cond
|
||||||
|
|
||||||
|
// see https://github.com/scottt/debugbreak
|
||||||
|
#if defined( _MSC_VER )
|
||||||
|
#define B2_BREAKPOINT __debugbreak()
|
||||||
|
#elif defined( __GNUC__ ) || defined( __clang__ )
|
||||||
|
#define B2_BREAKPOINT __builtin_trap()
|
||||||
|
#else
|
||||||
|
// Unknown compiler
|
||||||
|
#include <assert.h>
|
||||||
|
#define B2_BREAKPOINT assert( 0 )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined( NDEBUG ) || defined( B2_ENABLE_ASSERT )
|
||||||
|
B2_API int b2InternalAssertFcn( const char* condition, const char* fileName, int lineNumber );
|
||||||
|
#define B2_ASSERT( condition ) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if ( !( condition ) && b2InternalAssertFcn( #condition, __FILE__, (int)__LINE__ ) ) \
|
||||||
|
B2_BREAKPOINT; \
|
||||||
|
} \
|
||||||
|
while ( 0 )
|
||||||
|
#else
|
||||||
|
#define B2_ASSERT( ... ) ( (void)0 )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Get the absolute number of system ticks. The value is platform specific.
|
||||||
|
B2_API uint64_t b2GetTicks( void );
|
||||||
|
|
||||||
|
/// Get the milliseconds passed from an initial tick value.
|
||||||
|
B2_API float b2GetMilliseconds( uint64_t ticks );
|
||||||
|
|
||||||
|
/// Get the milliseconds passed from an initial tick value. Resets the passed in
|
||||||
|
/// value to the current tick value.
|
||||||
|
B2_API float b2GetMillisecondsAndReset( uint64_t* ticks );
|
||||||
|
|
||||||
|
/// Yield to be used in a busy loop.
|
||||||
|
B2_API void b2Yield( void );
|
||||||
|
|
||||||
|
/// Simple djb2 hash function for determinism testing
|
||||||
|
#define B2_HASH_INIT 5381
|
||||||
|
B2_API uint32_t b2Hash( uint32_t hash, const uint8_t* data, int count );
|
||||||
|
|
||||||
|
//! @endcond
|
||||||
1261
TSE_Core/include/box2d/box2d.h
Normal file
1261
TSE_Core/include/box2d/box2d.h
Normal file
File diff suppressed because it is too large
Load Diff
833
TSE_Core/include/box2d/collision.h
Normal file
833
TSE_Core/include/box2d/collision.h
Normal file
@@ -0,0 +1,833 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Erin Catto
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base.h"
|
||||||
|
#include "math_functions.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct b2SimplexCache b2SimplexCache;
|
||||||
|
typedef struct b2Hull b2Hull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup geometry Geometry
|
||||||
|
* @brief Geometry types and algorithms
|
||||||
|
*
|
||||||
|
* Definitions of circles, capsules, segments, and polygons. Various algorithms to compute hulls, mass properties, and so on.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// The maximum number of vertices on a convex polygon. Changing this affects performance even if you
|
||||||
|
/// don't use more vertices.
|
||||||
|
#define B2_MAX_POLYGON_VERTICES 8
|
||||||
|
|
||||||
|
/// Low level ray cast input data
|
||||||
|
typedef struct b2RayCastInput
|
||||||
|
{
|
||||||
|
/// Start point of the ray cast
|
||||||
|
b2Vec2 origin;
|
||||||
|
|
||||||
|
/// Translation of the ray cast
|
||||||
|
b2Vec2 translation;
|
||||||
|
|
||||||
|
/// The maximum fraction of the translation to consider, typically 1
|
||||||
|
float maxFraction;
|
||||||
|
} b2RayCastInput;
|
||||||
|
|
||||||
|
/// A distance proxy is used by the GJK algorithm. It encapsulates any shape.
|
||||||
|
/// You can provide between 1 and B2_MAX_POLYGON_VERTICES and a radius.
|
||||||
|
typedef struct b2ShapeProxy
|
||||||
|
{
|
||||||
|
/// The point cloud
|
||||||
|
b2Vec2 points[B2_MAX_POLYGON_VERTICES];
|
||||||
|
|
||||||
|
/// The number of points. Must be greater than 0.
|
||||||
|
int count;
|
||||||
|
|
||||||
|
/// The external radius of the point cloud. May be zero.
|
||||||
|
float radius;
|
||||||
|
} b2ShapeProxy;
|
||||||
|
|
||||||
|
/// Low level shape cast input in generic form. This allows casting an arbitrary point
|
||||||
|
/// cloud wrap with a radius. For example, a circle is a single point with a non-zero radius.
|
||||||
|
/// A capsule is two points with a non-zero radius. A box is four points with a zero radius.
|
||||||
|
typedef struct b2ShapeCastInput
|
||||||
|
{
|
||||||
|
/// A generic shape
|
||||||
|
b2ShapeProxy proxy;
|
||||||
|
|
||||||
|
/// The translation of the shape cast
|
||||||
|
b2Vec2 translation;
|
||||||
|
|
||||||
|
/// The maximum fraction of the translation to consider, typically 1
|
||||||
|
float maxFraction;
|
||||||
|
|
||||||
|
/// Allow shape cast to encroach when initially touching. This only works if the radius is greater than zero.
|
||||||
|
bool canEncroach;
|
||||||
|
} b2ShapeCastInput;
|
||||||
|
|
||||||
|
/// Low level ray cast or shape-cast output data. Returns a zero fraction and normal in the case of initial overlap.
|
||||||
|
typedef struct b2CastOutput
|
||||||
|
{
|
||||||
|
/// The surface normal at the hit point
|
||||||
|
b2Vec2 normal;
|
||||||
|
|
||||||
|
/// The surface hit point
|
||||||
|
b2Vec2 point;
|
||||||
|
|
||||||
|
/// The fraction of the input translation at collision
|
||||||
|
float fraction;
|
||||||
|
|
||||||
|
/// The number of iterations used
|
||||||
|
int iterations;
|
||||||
|
|
||||||
|
/// Did the cast hit?
|
||||||
|
bool hit;
|
||||||
|
} b2CastOutput;
|
||||||
|
|
||||||
|
/// This holds the mass data computed for a shape.
|
||||||
|
typedef struct b2MassData
|
||||||
|
{
|
||||||
|
/// The mass of the shape, usually in kilograms.
|
||||||
|
float mass;
|
||||||
|
|
||||||
|
/// The position of the shape's centroid relative to the shape's origin.
|
||||||
|
b2Vec2 center;
|
||||||
|
|
||||||
|
/// The rotational inertia of the shape about the local origin.
|
||||||
|
float rotationalInertia;
|
||||||
|
} b2MassData;
|
||||||
|
|
||||||
|
/// A solid circle
|
||||||
|
typedef struct b2Circle
|
||||||
|
{
|
||||||
|
/// The local center
|
||||||
|
b2Vec2 center;
|
||||||
|
|
||||||
|
/// The radius
|
||||||
|
float radius;
|
||||||
|
} b2Circle;
|
||||||
|
|
||||||
|
/// A solid capsule can be viewed as two semicircles connected
|
||||||
|
/// by a rectangle.
|
||||||
|
typedef struct b2Capsule
|
||||||
|
{
|
||||||
|
/// Local center of the first semicircle
|
||||||
|
b2Vec2 center1;
|
||||||
|
|
||||||
|
/// Local center of the second semicircle
|
||||||
|
b2Vec2 center2;
|
||||||
|
|
||||||
|
/// The radius of the semicircles
|
||||||
|
float radius;
|
||||||
|
} b2Capsule;
|
||||||
|
|
||||||
|
/// A solid convex polygon. It is assumed that the interior of the polygon is to
|
||||||
|
/// the left of each edge.
|
||||||
|
/// Polygons have a maximum number of vertices equal to B2_MAX_POLYGON_VERTICES.
|
||||||
|
/// In most cases you should not need many vertices for a convex polygon.
|
||||||
|
/// @warning DO NOT fill this out manually, instead use a helper function like
|
||||||
|
/// b2MakePolygon or b2MakeBox.
|
||||||
|
typedef struct b2Polygon
|
||||||
|
{
|
||||||
|
/// The polygon vertices
|
||||||
|
b2Vec2 vertices[B2_MAX_POLYGON_VERTICES];
|
||||||
|
|
||||||
|
/// The outward normal vectors of the polygon sides
|
||||||
|
b2Vec2 normals[B2_MAX_POLYGON_VERTICES];
|
||||||
|
|
||||||
|
/// The centroid of the polygon
|
||||||
|
b2Vec2 centroid;
|
||||||
|
|
||||||
|
/// The external radius for rounded polygons
|
||||||
|
float radius;
|
||||||
|
|
||||||
|
/// The number of polygon vertices
|
||||||
|
int count;
|
||||||
|
} b2Polygon;
|
||||||
|
|
||||||
|
/// A line segment with two-sided collision.
|
||||||
|
typedef struct b2Segment
|
||||||
|
{
|
||||||
|
/// The first point
|
||||||
|
b2Vec2 point1;
|
||||||
|
|
||||||
|
/// The second point
|
||||||
|
b2Vec2 point2;
|
||||||
|
} b2Segment;
|
||||||
|
|
||||||
|
/// A line segment with one-sided collision. Only collides on the right side.
|
||||||
|
/// Several of these are generated for a chain shape.
|
||||||
|
/// ghost1 -> point1 -> point2 -> ghost2
|
||||||
|
typedef struct b2ChainSegment
|
||||||
|
{
|
||||||
|
/// The tail ghost vertex
|
||||||
|
b2Vec2 ghost1;
|
||||||
|
|
||||||
|
/// The line segment
|
||||||
|
b2Segment segment;
|
||||||
|
|
||||||
|
/// The head ghost vertex
|
||||||
|
b2Vec2 ghost2;
|
||||||
|
|
||||||
|
/// The owning chain shape index (internal usage only)
|
||||||
|
int chainId;
|
||||||
|
} b2ChainSegment;
|
||||||
|
|
||||||
|
/// Validate ray cast input data (NaN, etc)
|
||||||
|
B2_API bool b2IsValidRay( const b2RayCastInput* input );
|
||||||
|
|
||||||
|
/// Make a convex polygon from a convex hull. This will assert if the hull is not valid.
|
||||||
|
/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull
|
||||||
|
B2_API b2Polygon b2MakePolygon( const b2Hull* hull, float radius );
|
||||||
|
|
||||||
|
/// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid.
|
||||||
|
/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull
|
||||||
|
B2_API b2Polygon b2MakeOffsetPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation );
|
||||||
|
|
||||||
|
/// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid.
|
||||||
|
/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull
|
||||||
|
B2_API b2Polygon b2MakeOffsetRoundedPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation, float radius );
|
||||||
|
|
||||||
|
/// Make a square polygon, bypassing the need for a convex hull.
|
||||||
|
/// @param halfWidth the half-width
|
||||||
|
B2_API b2Polygon b2MakeSquare( float halfWidth );
|
||||||
|
|
||||||
|
/// Make a box (rectangle) polygon, bypassing the need for a convex hull.
|
||||||
|
/// @param halfWidth the half-width (x-axis)
|
||||||
|
/// @param halfHeight the half-height (y-axis)
|
||||||
|
B2_API b2Polygon b2MakeBox( float halfWidth, float halfHeight );
|
||||||
|
|
||||||
|
/// Make a rounded box, bypassing the need for a convex hull.
|
||||||
|
/// @param halfWidth the half-width (x-axis)
|
||||||
|
/// @param halfHeight the half-height (y-axis)
|
||||||
|
/// @param radius the radius of the rounded extension
|
||||||
|
B2_API b2Polygon b2MakeRoundedBox( float halfWidth, float halfHeight, float radius );
|
||||||
|
|
||||||
|
/// Make an offset box, bypassing the need for a convex hull.
|
||||||
|
/// @param halfWidth the half-width (x-axis)
|
||||||
|
/// @param halfHeight the half-height (y-axis)
|
||||||
|
/// @param center the local center of the box
|
||||||
|
/// @param rotation the local rotation of the box
|
||||||
|
B2_API b2Polygon b2MakeOffsetBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation );
|
||||||
|
|
||||||
|
/// Make an offset rounded box, bypassing the need for a convex hull.
|
||||||
|
/// @param halfWidth the half-width (x-axis)
|
||||||
|
/// @param halfHeight the half-height (y-axis)
|
||||||
|
/// @param center the local center of the box
|
||||||
|
/// @param rotation the local rotation of the box
|
||||||
|
/// @param radius the radius of the rounded extension
|
||||||
|
B2_API b2Polygon b2MakeOffsetRoundedBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation, float radius );
|
||||||
|
|
||||||
|
/// Transform a polygon. This is useful for transferring a shape from one body to another.
|
||||||
|
B2_API b2Polygon b2TransformPolygon( b2Transform transform, const b2Polygon* polygon );
|
||||||
|
|
||||||
|
/// Compute mass properties of a circle
|
||||||
|
B2_API b2MassData b2ComputeCircleMass( const b2Circle* shape, float density );
|
||||||
|
|
||||||
|
/// Compute mass properties of a capsule
|
||||||
|
B2_API b2MassData b2ComputeCapsuleMass( const b2Capsule* shape, float density );
|
||||||
|
|
||||||
|
/// Compute mass properties of a polygon
|
||||||
|
B2_API b2MassData b2ComputePolygonMass( const b2Polygon* shape, float density );
|
||||||
|
|
||||||
|
/// Compute the bounding box of a transformed circle
|
||||||
|
B2_API b2AABB b2ComputeCircleAABB( const b2Circle* shape, b2Transform transform );
|
||||||
|
|
||||||
|
/// Compute the bounding box of a transformed capsule
|
||||||
|
B2_API b2AABB b2ComputeCapsuleAABB( const b2Capsule* shape, b2Transform transform );
|
||||||
|
|
||||||
|
/// Compute the bounding box of a transformed polygon
|
||||||
|
B2_API b2AABB b2ComputePolygonAABB( const b2Polygon* shape, b2Transform transform );
|
||||||
|
|
||||||
|
/// Compute the bounding box of a transformed line segment
|
||||||
|
B2_API b2AABB b2ComputeSegmentAABB( const b2Segment* shape, b2Transform transform );
|
||||||
|
|
||||||
|
/// Test a point for overlap with a circle in local space
|
||||||
|
B2_API bool b2PointInCircle( b2Vec2 point, const b2Circle* shape );
|
||||||
|
|
||||||
|
/// Test a point for overlap with a capsule in local space
|
||||||
|
B2_API bool b2PointInCapsule( b2Vec2 point, const b2Capsule* shape );
|
||||||
|
|
||||||
|
/// Test a point for overlap with a convex polygon in local space
|
||||||
|
B2_API bool b2PointInPolygon( b2Vec2 point, const b2Polygon* shape );
|
||||||
|
|
||||||
|
/// Ray cast versus circle shape in local space. Initial overlap is treated as a miss.
|
||||||
|
B2_API b2CastOutput b2RayCastCircle( const b2RayCastInput* input, const b2Circle* shape );
|
||||||
|
|
||||||
|
/// Ray cast versus capsule shape in local space. Initial overlap is treated as a miss.
|
||||||
|
B2_API b2CastOutput b2RayCastCapsule( const b2RayCastInput* input, const b2Capsule* shape );
|
||||||
|
|
||||||
|
/// Ray cast versus segment shape in local space. Optionally treat the segment as one-sided with hits from
|
||||||
|
/// the left side being treated as a miss.
|
||||||
|
B2_API b2CastOutput b2RayCastSegment( const b2RayCastInput* input, const b2Segment* shape, bool oneSided );
|
||||||
|
|
||||||
|
/// Ray cast versus polygon shape in local space. Initial overlap is treated as a miss.
|
||||||
|
B2_API b2CastOutput b2RayCastPolygon( const b2RayCastInput* input, const b2Polygon* shape );
|
||||||
|
|
||||||
|
/// Shape cast versus a circle. Initial overlap is treated as a miss.
|
||||||
|
B2_API b2CastOutput b2ShapeCastCircle( const b2ShapeCastInput* input, const b2Circle* shape );
|
||||||
|
|
||||||
|
/// Shape cast versus a capsule. Initial overlap is treated as a miss.
|
||||||
|
B2_API b2CastOutput b2ShapeCastCapsule( const b2ShapeCastInput* input, const b2Capsule* shape );
|
||||||
|
|
||||||
|
/// Shape cast versus a line segment. Initial overlap is treated as a miss.
|
||||||
|
B2_API b2CastOutput b2ShapeCastSegment( const b2ShapeCastInput* input, const b2Segment* shape );
|
||||||
|
|
||||||
|
/// Shape cast versus a convex polygon. Initial overlap is treated as a miss.
|
||||||
|
B2_API b2CastOutput b2ShapeCastPolygon( const b2ShapeCastInput* input, const b2Polygon* shape );
|
||||||
|
|
||||||
|
/// A convex hull. Used to create convex polygons.
|
||||||
|
/// @warning Do not modify these values directly, instead use b2ComputeHull()
|
||||||
|
typedef struct b2Hull
|
||||||
|
{
|
||||||
|
/// The final points of the hull
|
||||||
|
b2Vec2 points[B2_MAX_POLYGON_VERTICES];
|
||||||
|
|
||||||
|
/// The number of points
|
||||||
|
int count;
|
||||||
|
} b2Hull;
|
||||||
|
|
||||||
|
/// Compute the convex hull of a set of points. Returns an empty hull if it fails.
|
||||||
|
/// Some failure cases:
|
||||||
|
/// - all points very close together
|
||||||
|
/// - all points on a line
|
||||||
|
/// - less than 3 points
|
||||||
|
/// - more than B2_MAX_POLYGON_VERTICES points
|
||||||
|
/// This welds close points and removes collinear points.
|
||||||
|
/// @warning Do not modify a hull once it has been computed
|
||||||
|
B2_API b2Hull b2ComputeHull( const b2Vec2* points, int count );
|
||||||
|
|
||||||
|
/// This determines if a hull is valid. Checks for:
|
||||||
|
/// - convexity
|
||||||
|
/// - collinear points
|
||||||
|
/// This is expensive and should not be called at runtime.
|
||||||
|
B2_API bool b2ValidateHull( const b2Hull* hull );
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup distance Distance
|
||||||
|
* Functions for computing the distance between shapes.
|
||||||
|
*
|
||||||
|
* These are advanced functions you can use to perform distance calculations. There
|
||||||
|
* are functions for computing the closest points between shapes, doing linear shape casts,
|
||||||
|
* and doing rotational shape casts. The latter is called time of impact (TOI).
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Result of computing the distance between two line segments
|
||||||
|
typedef struct b2SegmentDistanceResult
|
||||||
|
{
|
||||||
|
/// The closest point on the first segment
|
||||||
|
b2Vec2 closest1;
|
||||||
|
|
||||||
|
/// The closest point on the second segment
|
||||||
|
b2Vec2 closest2;
|
||||||
|
|
||||||
|
/// The barycentric coordinate on the first segment
|
||||||
|
float fraction1;
|
||||||
|
|
||||||
|
/// The barycentric coordinate on the second segment
|
||||||
|
float fraction2;
|
||||||
|
|
||||||
|
/// The squared distance between the closest points
|
||||||
|
float distanceSquared;
|
||||||
|
} b2SegmentDistanceResult;
|
||||||
|
|
||||||
|
/// Compute the distance between two line segments, clamping at the end points if needed.
|
||||||
|
B2_API b2SegmentDistanceResult b2SegmentDistance( b2Vec2 p1, b2Vec2 q1, b2Vec2 p2, b2Vec2 q2 );
|
||||||
|
|
||||||
|
/// Used to warm start the GJK simplex. If you call this function multiple times with nearby
|
||||||
|
/// transforms this might improve performance. Otherwise you can zero initialize this.
|
||||||
|
/// The distance cache must be initialized to zero on the first call.
|
||||||
|
/// Users should generally just zero initialize this structure for each call.
|
||||||
|
typedef struct b2SimplexCache
|
||||||
|
{
|
||||||
|
/// The number of stored simplex points
|
||||||
|
uint16_t count;
|
||||||
|
|
||||||
|
/// The cached simplex indices on shape A
|
||||||
|
uint8_t indexA[3];
|
||||||
|
|
||||||
|
/// The cached simplex indices on shape B
|
||||||
|
uint8_t indexB[3];
|
||||||
|
} b2SimplexCache;
|
||||||
|
|
||||||
|
static const b2SimplexCache b2_emptySimplexCache = B2_ZERO_INIT;
|
||||||
|
|
||||||
|
/// Input for b2ShapeDistance
|
||||||
|
typedef struct b2DistanceInput
|
||||||
|
{
|
||||||
|
/// The proxy for shape A
|
||||||
|
b2ShapeProxy proxyA;
|
||||||
|
|
||||||
|
/// The proxy for shape B
|
||||||
|
b2ShapeProxy proxyB;
|
||||||
|
|
||||||
|
/// The world transform for shape A
|
||||||
|
b2Transform transformA;
|
||||||
|
|
||||||
|
/// The world transform for shape B
|
||||||
|
b2Transform transformB;
|
||||||
|
|
||||||
|
/// Should the proxy radius be considered?
|
||||||
|
bool useRadii;
|
||||||
|
} b2DistanceInput;
|
||||||
|
|
||||||
|
/// Output for b2ShapeDistance
|
||||||
|
typedef struct b2DistanceOutput
|
||||||
|
{
|
||||||
|
b2Vec2 pointA; ///< Closest point on shapeA
|
||||||
|
b2Vec2 pointB; ///< Closest point on shapeB
|
||||||
|
b2Vec2 normal; ///< Normal vector that points from A to B. Invalid if distance is zero.
|
||||||
|
float distance; ///< The final distance, zero if overlapped
|
||||||
|
int iterations; ///< Number of GJK iterations used
|
||||||
|
int simplexCount; ///< The number of simplexes stored in the simplex array
|
||||||
|
} b2DistanceOutput;
|
||||||
|
|
||||||
|
/// Simplex vertex for debugging the GJK algorithm
|
||||||
|
typedef struct b2SimplexVertex
|
||||||
|
{
|
||||||
|
b2Vec2 wA; ///< support point in proxyA
|
||||||
|
b2Vec2 wB; ///< support point in proxyB
|
||||||
|
b2Vec2 w; ///< wB - wA
|
||||||
|
float a; ///< barycentric coordinate for closest point
|
||||||
|
int indexA; ///< wA index
|
||||||
|
int indexB; ///< wB index
|
||||||
|
} b2SimplexVertex;
|
||||||
|
|
||||||
|
/// Simplex from the GJK algorithm
|
||||||
|
typedef struct b2Simplex
|
||||||
|
{
|
||||||
|
b2SimplexVertex v1, v2, v3; ///< vertices
|
||||||
|
int count; ///< number of valid vertices
|
||||||
|
} b2Simplex;
|
||||||
|
|
||||||
|
/// Compute the closest points between two shapes represented as point clouds.
|
||||||
|
/// b2SimplexCache cache is input/output. On the first call set b2SimplexCache.count to zero.
|
||||||
|
/// The underlying GJK algorithm may be debugged by passing in debug simplexes and capacity. You may pass in NULL and 0 for these.
|
||||||
|
B2_API b2DistanceOutput b2ShapeDistance( const b2DistanceInput* input, b2SimplexCache* cache, b2Simplex* simplexes,
|
||||||
|
int simplexCapacity );
|
||||||
|
|
||||||
|
/// Input parameters for b2ShapeCast
|
||||||
|
typedef struct b2ShapeCastPairInput
|
||||||
|
{
|
||||||
|
b2ShapeProxy proxyA; ///< The proxy for shape A
|
||||||
|
b2ShapeProxy proxyB; ///< The proxy for shape B
|
||||||
|
b2Transform transformA; ///< The world transform for shape A
|
||||||
|
b2Transform transformB; ///< The world transform for shape B
|
||||||
|
b2Vec2 translationB; ///< The translation of shape B
|
||||||
|
float maxFraction; ///< The fraction of the translation to consider, typically 1
|
||||||
|
bool canEncroach; ///< Allows shapes with a radius to move slightly closer if already touching
|
||||||
|
} b2ShapeCastPairInput;
|
||||||
|
|
||||||
|
/// Perform a linear shape cast of shape B moving and shape A fixed. Determines the hit point, normal, and translation fraction.
|
||||||
|
/// Initially touching shapes are treated as a miss.
|
||||||
|
B2_API b2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input );
|
||||||
|
|
||||||
|
/// Make a proxy for use in overlap, shape cast, and related functions. This is a deep copy of the points.
|
||||||
|
B2_API b2ShapeProxy b2MakeProxy( const b2Vec2* points, int count, float radius );
|
||||||
|
|
||||||
|
/// Make a proxy with a transform. This is a deep copy of the points.
|
||||||
|
B2_API b2ShapeProxy b2MakeOffsetProxy( const b2Vec2* points, int count, float radius, b2Vec2 position, b2Rot rotation );
|
||||||
|
|
||||||
|
/// This describes the motion of a body/shape for TOI computation. Shapes are defined with respect to the body origin,
|
||||||
|
/// which may not coincide with the center of mass. However, to support dynamics we must interpolate the center of mass
|
||||||
|
/// position.
|
||||||
|
typedef struct b2Sweep
|
||||||
|
{
|
||||||
|
b2Vec2 localCenter; ///< Local center of mass position
|
||||||
|
b2Vec2 c1; ///< Starting center of mass world position
|
||||||
|
b2Vec2 c2; ///< Ending center of mass world position
|
||||||
|
b2Rot q1; ///< Starting world rotation
|
||||||
|
b2Rot q2; ///< Ending world rotation
|
||||||
|
} b2Sweep;
|
||||||
|
|
||||||
|
/// Evaluate the transform sweep at a specific time.
|
||||||
|
B2_API b2Transform b2GetSweepTransform( const b2Sweep* sweep, float time );
|
||||||
|
|
||||||
|
/// Input parameters for b2TimeOfImpact
|
||||||
|
typedef struct b2TOIInput
|
||||||
|
{
|
||||||
|
b2ShapeProxy proxyA; ///< The proxy for shape A
|
||||||
|
b2ShapeProxy proxyB; ///< The proxy for shape B
|
||||||
|
b2Sweep sweepA; ///< The movement of shape A
|
||||||
|
b2Sweep sweepB; ///< The movement of shape B
|
||||||
|
float maxFraction; ///< Defines the sweep interval [0, maxFraction]
|
||||||
|
} b2TOIInput;
|
||||||
|
|
||||||
|
/// Describes the TOI output
|
||||||
|
typedef enum b2TOIState
|
||||||
|
{
|
||||||
|
b2_toiStateUnknown,
|
||||||
|
b2_toiStateFailed,
|
||||||
|
b2_toiStateOverlapped,
|
||||||
|
b2_toiStateHit,
|
||||||
|
b2_toiStateSeparated
|
||||||
|
} b2TOIState;
|
||||||
|
|
||||||
|
/// Output parameters for b2TimeOfImpact.
|
||||||
|
typedef struct b2TOIOutput
|
||||||
|
{
|
||||||
|
b2TOIState state; ///< The type of result
|
||||||
|
float fraction; ///< The sweep time of the collision
|
||||||
|
} b2TOIOutput;
|
||||||
|
|
||||||
|
/// Compute the upper bound on time before two shapes penetrate. Time is represented as
|
||||||
|
/// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate,
|
||||||
|
/// non-tunneling collisions. If you change the time interval, you should call this function
|
||||||
|
/// again.
|
||||||
|
B2_API b2TOIOutput b2TimeOfImpact( const b2TOIInput* input );
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup collision Collision
|
||||||
|
* @brief Functions for colliding pairs of shapes
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// A manifold point is a contact point belonging to a contact manifold.
|
||||||
|
/// It holds details related to the geometry and dynamics of the contact points.
|
||||||
|
/// Box2D uses speculative collision so some contact points may be separated.
|
||||||
|
/// You may use the totalNormalImpulse to determine if there was an interaction during
|
||||||
|
/// the time step.
|
||||||
|
typedef struct b2ManifoldPoint
|
||||||
|
{
|
||||||
|
/// Location of the contact point in world space. Subject to precision loss at large coordinates.
|
||||||
|
/// @note Should only be used for debugging.
|
||||||
|
b2Vec2 point;
|
||||||
|
|
||||||
|
/// Location of the contact point relative to shapeA's origin in world space
|
||||||
|
/// @note When used internally to the Box2D solver, this is relative to the body center of mass.
|
||||||
|
b2Vec2 anchorA;
|
||||||
|
|
||||||
|
/// Location of the contact point relative to shapeB's origin in world space
|
||||||
|
/// @note When used internally to the Box2D solver, this is relative to the body center of mass.
|
||||||
|
b2Vec2 anchorB;
|
||||||
|
|
||||||
|
/// The separation of the contact point, negative if penetrating
|
||||||
|
float separation;
|
||||||
|
|
||||||
|
/// The impulse along the manifold normal vector.
|
||||||
|
float normalImpulse;
|
||||||
|
|
||||||
|
/// The friction impulse
|
||||||
|
float tangentImpulse;
|
||||||
|
|
||||||
|
/// The total normal impulse applied across sub-stepping and restitution. This is important
|
||||||
|
/// to identify speculative contact points that had an interaction in the time step.
|
||||||
|
float totalNormalImpulse;
|
||||||
|
|
||||||
|
/// Relative normal velocity pre-solve. Used for hit events. If the normal impulse is
|
||||||
|
/// zero then there was no hit. Negative means shapes are approaching.
|
||||||
|
float normalVelocity;
|
||||||
|
|
||||||
|
/// Uniquely identifies a contact point between two shapes
|
||||||
|
uint16_t id;
|
||||||
|
|
||||||
|
/// Did this contact point exist the previous step?
|
||||||
|
bool persisted;
|
||||||
|
} b2ManifoldPoint;
|
||||||
|
|
||||||
|
/// A contact manifold describes the contact points between colliding shapes.
|
||||||
|
/// @note Box2D uses speculative collision so some contact points may be separated.
|
||||||
|
typedef struct b2Manifold
|
||||||
|
{
|
||||||
|
/// The unit normal vector in world space, points from shape A to bodyB
|
||||||
|
b2Vec2 normal;
|
||||||
|
|
||||||
|
/// Angular impulse applied for rolling resistance. N * m * s = kg * m^2 / s
|
||||||
|
float rollingImpulse;
|
||||||
|
|
||||||
|
/// The manifold points, up to two are possible in 2D
|
||||||
|
b2ManifoldPoint points[2];
|
||||||
|
|
||||||
|
/// The number of contacts points, will be 0, 1, or 2
|
||||||
|
int pointCount;
|
||||||
|
|
||||||
|
} b2Manifold;
|
||||||
|
|
||||||
|
/// Compute the contact manifold between two circles
|
||||||
|
B2_API b2Manifold b2CollideCircles( const b2Circle* circleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB );
|
||||||
|
|
||||||
|
/// Compute the contact manifold between a capsule and circle
|
||||||
|
B2_API b2Manifold b2CollideCapsuleAndCircle( const b2Capsule* capsuleA, b2Transform xfA, const b2Circle* circleB,
|
||||||
|
b2Transform xfB );
|
||||||
|
|
||||||
|
/// Compute the contact manifold between an segment and a circle
|
||||||
|
B2_API b2Manifold b2CollideSegmentAndCircle( const b2Segment* segmentA, b2Transform xfA, const b2Circle* circleB,
|
||||||
|
b2Transform xfB );
|
||||||
|
|
||||||
|
/// Compute the contact manifold between a polygon and a circle
|
||||||
|
B2_API b2Manifold b2CollidePolygonAndCircle( const b2Polygon* polygonA, b2Transform xfA, const b2Circle* circleB,
|
||||||
|
b2Transform xfB );
|
||||||
|
|
||||||
|
/// Compute the contact manifold between a capsule and circle
|
||||||
|
B2_API b2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB );
|
||||||
|
|
||||||
|
/// Compute the contact manifold between an segment and a capsule
|
||||||
|
B2_API b2Manifold b2CollideSegmentAndCapsule( const b2Segment* segmentA, b2Transform xfA, const b2Capsule* capsuleB,
|
||||||
|
b2Transform xfB );
|
||||||
|
|
||||||
|
/// Compute the contact manifold between a polygon and capsule
|
||||||
|
B2_API b2Manifold b2CollidePolygonAndCapsule( const b2Polygon* polygonA, b2Transform xfA, const b2Capsule* capsuleB,
|
||||||
|
b2Transform xfB );
|
||||||
|
|
||||||
|
/// Compute the contact manifold between two polygons
|
||||||
|
B2_API b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB );
|
||||||
|
|
||||||
|
/// Compute the contact manifold between an segment and a polygon
|
||||||
|
B2_API b2Manifold b2CollideSegmentAndPolygon( const b2Segment* segmentA, b2Transform xfA, const b2Polygon* polygonB,
|
||||||
|
b2Transform xfB );
|
||||||
|
|
||||||
|
/// Compute the contact manifold between a chain segment and a circle
|
||||||
|
B2_API b2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA, b2Transform xfA, const b2Circle* circleB,
|
||||||
|
b2Transform xfB );
|
||||||
|
|
||||||
|
/// Compute the contact manifold between a chain segment and a capsule
|
||||||
|
B2_API b2Manifold b2CollideChainSegmentAndCapsule( const b2ChainSegment* segmentA, b2Transform xfA, const b2Capsule* capsuleB,
|
||||||
|
b2Transform xfB, b2SimplexCache* cache );
|
||||||
|
|
||||||
|
/// Compute the contact manifold between a chain segment and a rounded polygon
|
||||||
|
B2_API b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Transform xfA, const b2Polygon* polygonB,
|
||||||
|
b2Transform xfB, b2SimplexCache* cache );
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup tree Dynamic Tree
|
||||||
|
* The dynamic tree is a binary AABB tree to organize and query large numbers of geometric objects
|
||||||
|
*
|
||||||
|
* Box2D uses the dynamic tree internally to sort collision shapes into a binary bounding volume hierarchy.
|
||||||
|
* This data structure may have uses in games for organizing other geometry data and may be used independently
|
||||||
|
* of Box2D rigid body simulation.
|
||||||
|
*
|
||||||
|
* A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt.
|
||||||
|
* A dynamic tree arranges data in a binary tree to accelerate
|
||||||
|
* queries such as AABB queries and ray casts. Leaf nodes are proxies
|
||||||
|
* with an AABB. These are used to hold a user collision object.
|
||||||
|
* Nodes are pooled and relocatable, so I use node indices rather than pointers.
|
||||||
|
* The dynamic tree is made available for advanced users that would like to use it to organize
|
||||||
|
* spatial game data besides rigid bodies.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// The dynamic tree structure. This should be considered private data.
|
||||||
|
/// It is placed here for performance reasons.
|
||||||
|
typedef struct b2DynamicTree
|
||||||
|
{
|
||||||
|
/// The tree nodes
|
||||||
|
struct b2TreeNode* nodes;
|
||||||
|
|
||||||
|
/// The root index
|
||||||
|
int root;
|
||||||
|
|
||||||
|
/// The number of nodes
|
||||||
|
int nodeCount;
|
||||||
|
|
||||||
|
/// The allocated node space
|
||||||
|
int nodeCapacity;
|
||||||
|
|
||||||
|
/// Node free list
|
||||||
|
int freeList;
|
||||||
|
|
||||||
|
/// Number of proxies created
|
||||||
|
int proxyCount;
|
||||||
|
|
||||||
|
/// Leaf indices for rebuild
|
||||||
|
int* leafIndices;
|
||||||
|
|
||||||
|
/// Leaf bounding boxes for rebuild
|
||||||
|
b2AABB* leafBoxes;
|
||||||
|
|
||||||
|
/// Leaf bounding box centers for rebuild
|
||||||
|
b2Vec2* leafCenters;
|
||||||
|
|
||||||
|
/// Bins for sorting during rebuild
|
||||||
|
int* binIndices;
|
||||||
|
|
||||||
|
/// Allocated space for rebuilding
|
||||||
|
int rebuildCapacity;
|
||||||
|
} b2DynamicTree;
|
||||||
|
|
||||||
|
/// These are performance results returned by dynamic tree queries.
|
||||||
|
typedef struct b2TreeStats
|
||||||
|
{
|
||||||
|
/// Number of internal nodes visited during the query
|
||||||
|
int nodeVisits;
|
||||||
|
|
||||||
|
/// Number of leaf nodes visited during the query
|
||||||
|
int leafVisits;
|
||||||
|
} b2TreeStats;
|
||||||
|
|
||||||
|
/// Constructing the tree initializes the node pool.
|
||||||
|
B2_API b2DynamicTree b2DynamicTree_Create( void );
|
||||||
|
|
||||||
|
/// Destroy the tree, freeing the node pool.
|
||||||
|
B2_API void b2DynamicTree_Destroy( b2DynamicTree* tree );
|
||||||
|
|
||||||
|
/// Create a proxy. Provide an AABB and a userData value.
|
||||||
|
B2_API int b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, uint64_t userData );
|
||||||
|
|
||||||
|
/// Destroy a proxy. This asserts if the id is invalid.
|
||||||
|
B2_API void b2DynamicTree_DestroyProxy( b2DynamicTree* tree, int proxyId );
|
||||||
|
|
||||||
|
/// Move a proxy to a new AABB by removing and reinserting into the tree.
|
||||||
|
B2_API void b2DynamicTree_MoveProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb );
|
||||||
|
|
||||||
|
/// Enlarge a proxy and enlarge ancestors as necessary.
|
||||||
|
B2_API void b2DynamicTree_EnlargeProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb );
|
||||||
|
|
||||||
|
/// Modify the category bits on a proxy. This is an expensive operation.
|
||||||
|
B2_API void b2DynamicTree_SetCategoryBits( b2DynamicTree* tree, int proxyId, uint64_t categoryBits );
|
||||||
|
|
||||||
|
/// Get the category bits on a proxy.
|
||||||
|
B2_API uint64_t b2DynamicTree_GetCategoryBits( b2DynamicTree* tree, int proxyId );
|
||||||
|
|
||||||
|
/// This function receives proxies found in the AABB query.
|
||||||
|
/// @return true if the query should continue
|
||||||
|
typedef bool b2TreeQueryCallbackFcn( int proxyId, uint64_t userData, void* context );
|
||||||
|
|
||||||
|
/// Query an AABB for overlapping proxies. The callback class is called for each proxy that overlaps the supplied AABB.
|
||||||
|
/// @return performance data
|
||||||
|
B2_API b2TreeStats b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits,
|
||||||
|
b2TreeQueryCallbackFcn* callback, void* context );
|
||||||
|
|
||||||
|
/// This function receives clipped ray cast input for a proxy. The function
|
||||||
|
/// returns the new ray fraction.
|
||||||
|
/// - return a value of 0 to terminate the ray cast
|
||||||
|
/// - return a value less than input->maxFraction to clip the ray
|
||||||
|
/// - return a value of input->maxFraction to continue the ray cast without clipping
|
||||||
|
typedef float b2TreeRayCastCallbackFcn( const b2RayCastInput* input, int proxyId, uint64_t userData, void* context );
|
||||||
|
|
||||||
|
/// Ray cast against the proxies in the tree. This relies on the callback
|
||||||
|
/// to perform a exact ray cast in the case were the proxy contains a shape.
|
||||||
|
/// The callback also performs the any collision filtering. This has performance
|
||||||
|
/// roughly equal to k * log(n), where k is the number of collisions and n is the
|
||||||
|
/// number of proxies in the tree.
|
||||||
|
/// Bit-wise filtering using mask bits can greatly improve performance in some scenarios.
|
||||||
|
/// However, this filtering may be approximate, so the user should still apply filtering to results.
|
||||||
|
/// @param tree the dynamic tree to ray cast
|
||||||
|
/// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1)
|
||||||
|
/// @param maskBits mask bit hint: `bool accept = (maskBits & node->categoryBits) != 0;`
|
||||||
|
/// @param callback a callback class that is called for each proxy that is hit by the ray
|
||||||
|
/// @param context user context that is passed to the callback
|
||||||
|
/// @return performance data
|
||||||
|
B2_API b2TreeStats b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits,
|
||||||
|
b2TreeRayCastCallbackFcn* callback, void* context );
|
||||||
|
|
||||||
|
/// This function receives clipped ray cast input for a proxy. The function
|
||||||
|
/// returns the new ray fraction.
|
||||||
|
/// - return a value of 0 to terminate the ray cast
|
||||||
|
/// - return a value less than input->maxFraction to clip the ray
|
||||||
|
/// - return a value of input->maxFraction to continue the ray cast without clipping
|
||||||
|
typedef float b2TreeShapeCastCallbackFcn( const b2ShapeCastInput* input, int proxyId, uint64_t userData, void* context );
|
||||||
|
|
||||||
|
/// Ray cast against the proxies in the tree. This relies on the callback
|
||||||
|
/// to perform a exact ray cast in the case were the proxy contains a shape.
|
||||||
|
/// The callback also performs the any collision filtering. This has performance
|
||||||
|
/// roughly equal to k * log(n), where k is the number of collisions and n is the
|
||||||
|
/// number of proxies in the tree.
|
||||||
|
/// @param tree the dynamic tree to ray cast
|
||||||
|
/// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
|
||||||
|
/// @param maskBits filter bits: `bool accept = (maskBits & node->categoryBits) != 0;`
|
||||||
|
/// @param callback a callback class that is called for each proxy that is hit by the shape
|
||||||
|
/// @param context user context that is passed to the callback
|
||||||
|
/// @return performance data
|
||||||
|
B2_API b2TreeStats b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits,
|
||||||
|
b2TreeShapeCastCallbackFcn* callback, void* context );
|
||||||
|
|
||||||
|
/// Get the height of the binary tree.
|
||||||
|
B2_API int b2DynamicTree_GetHeight( const b2DynamicTree* tree );
|
||||||
|
|
||||||
|
/// Get the ratio of the sum of the node areas to the root area.
|
||||||
|
B2_API float b2DynamicTree_GetAreaRatio( const b2DynamicTree* tree );
|
||||||
|
|
||||||
|
/// Get the bounding box that contains the entire tree
|
||||||
|
B2_API b2AABB b2DynamicTree_GetRootBounds( const b2DynamicTree* tree );
|
||||||
|
|
||||||
|
/// Get the number of proxies created
|
||||||
|
B2_API int b2DynamicTree_GetProxyCount( const b2DynamicTree* tree );
|
||||||
|
|
||||||
|
/// Rebuild the tree while retaining subtrees that haven't changed. Returns the number of boxes sorted.
|
||||||
|
B2_API int b2DynamicTree_Rebuild( b2DynamicTree* tree, bool fullBuild );
|
||||||
|
|
||||||
|
/// Get the number of bytes used by this tree
|
||||||
|
B2_API int b2DynamicTree_GetByteCount( const b2DynamicTree* tree );
|
||||||
|
|
||||||
|
/// Get proxy user data
|
||||||
|
B2_API uint64_t b2DynamicTree_GetUserData( const b2DynamicTree* tree, int proxyId );
|
||||||
|
|
||||||
|
/// Get the AABB of a proxy
|
||||||
|
B2_API b2AABB b2DynamicTree_GetAABB( const b2DynamicTree* tree, int proxyId );
|
||||||
|
|
||||||
|
/// Validate this tree. For testing.
|
||||||
|
B2_API void b2DynamicTree_Validate( const b2DynamicTree* tree );
|
||||||
|
|
||||||
|
/// Validate this tree has no enlarged AABBs. For testing.
|
||||||
|
B2_API void b2DynamicTree_ValidateNoEnlarged( const b2DynamicTree* tree );
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup character Character mover
|
||||||
|
* Character movement solver
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// These are the collision planes returned from b2World_CollideMover
|
||||||
|
typedef struct b2PlaneResult
|
||||||
|
{
|
||||||
|
/// The collision plane between the mover and a convex shape
|
||||||
|
b2Plane plane;
|
||||||
|
|
||||||
|
// The collision point on the shape.
|
||||||
|
b2Vec2 point;
|
||||||
|
|
||||||
|
/// Did the collision register a hit? If not this plane should be ignored.
|
||||||
|
bool hit;
|
||||||
|
} b2PlaneResult;
|
||||||
|
|
||||||
|
/// These are collision planes that can be fed to b2SolvePlanes. Normally
|
||||||
|
/// this is assembled by the user from plane results in b2PlaneResult
|
||||||
|
typedef struct b2CollisionPlane
|
||||||
|
{
|
||||||
|
/// The collision plane between the mover and some shape
|
||||||
|
b2Plane plane;
|
||||||
|
|
||||||
|
/// Setting this to FLT_MAX makes the plane as rigid as possible. Lower values can
|
||||||
|
/// make the plane collision soft. Usually in meters.
|
||||||
|
float pushLimit;
|
||||||
|
|
||||||
|
/// The push on the mover determined by b2SolvePlanes. Usually in meters.
|
||||||
|
float push;
|
||||||
|
|
||||||
|
/// Indicates if b2ClipVector should clip against this plane. Should be false for soft collision.
|
||||||
|
bool clipVelocity;
|
||||||
|
} b2CollisionPlane;
|
||||||
|
|
||||||
|
/// Result returned by b2SolvePlanes
|
||||||
|
typedef struct b2PlaneSolverResult
|
||||||
|
{
|
||||||
|
/// The translation of the mover
|
||||||
|
b2Vec2 translation;
|
||||||
|
|
||||||
|
/// The number of iterations used by the plane solver. For diagnostics.
|
||||||
|
int iterationCount;
|
||||||
|
} b2PlaneSolverResult;
|
||||||
|
|
||||||
|
/// Solves the position of a mover that satisfies the given collision planes.
|
||||||
|
/// @param targetDelta the desired movement from the position used to generate the collision planes
|
||||||
|
/// @param planes the collision planes
|
||||||
|
/// @param count the number of collision planes
|
||||||
|
B2_API b2PlaneSolverResult b2SolvePlanes( b2Vec2 targetDelta, b2CollisionPlane* planes, int count );
|
||||||
|
|
||||||
|
/// Clips the velocity against the given collision planes. Planes with zero push or clipVelocity
|
||||||
|
/// set to false are skipped.
|
||||||
|
B2_API b2Vec2 b2ClipVector( b2Vec2 vector, const b2CollisionPlane* planes, int count );
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
157
TSE_Core/include/box2d/id.h
Normal file
157
TSE_Core/include/box2d/id.h
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Erin Catto
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup id Ids
|
||||||
|
* These ids serve as handles to internal Box2D objects.
|
||||||
|
* These should be considered opaque data and passed by value.
|
||||||
|
* Include this header if you need the id types and not the whole Box2D API.
|
||||||
|
* All ids are considered null if initialized to zero.
|
||||||
|
*
|
||||||
|
* For example in C++:
|
||||||
|
*
|
||||||
|
* @code{.cxx}
|
||||||
|
* b2WorldId worldId = {};
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Or in C:
|
||||||
|
*
|
||||||
|
* @code{.c}
|
||||||
|
* b2WorldId worldId = {0};
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* These are both considered null.
|
||||||
|
*
|
||||||
|
* @warning Do not use the internals of these ids. They are subject to change. Ids should be treated as opaque objects.
|
||||||
|
* @warning You should use ids to access objects in Box2D. Do not access files within the src folder. Such usage is unsupported.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// World id references a world instance. This should be treated as an opaque handle.
|
||||||
|
typedef struct b2WorldId
|
||||||
|
{
|
||||||
|
uint16_t index1;
|
||||||
|
uint16_t generation;
|
||||||
|
} b2WorldId;
|
||||||
|
|
||||||
|
/// Body id references a body instance. This should be treated as an opaque handle.
|
||||||
|
typedef struct b2BodyId
|
||||||
|
{
|
||||||
|
int32_t index1;
|
||||||
|
uint16_t world0;
|
||||||
|
uint16_t generation;
|
||||||
|
} b2BodyId;
|
||||||
|
|
||||||
|
/// Shape id references a shape instance. This should be treated as an opaque handle.
|
||||||
|
typedef struct b2ShapeId
|
||||||
|
{
|
||||||
|
int32_t index1;
|
||||||
|
uint16_t world0;
|
||||||
|
uint16_t generation;
|
||||||
|
} b2ShapeId;
|
||||||
|
|
||||||
|
/// Chain id references a chain instances. This should be treated as an opaque handle.
|
||||||
|
typedef struct b2ChainId
|
||||||
|
{
|
||||||
|
int32_t index1;
|
||||||
|
uint16_t world0;
|
||||||
|
uint16_t generation;
|
||||||
|
} b2ChainId;
|
||||||
|
|
||||||
|
/// Joint id references a joint instance. This should be treated as an opaque handle.
|
||||||
|
typedef struct b2JointId
|
||||||
|
{
|
||||||
|
int32_t index1;
|
||||||
|
uint16_t world0;
|
||||||
|
uint16_t generation;
|
||||||
|
} b2JointId;
|
||||||
|
|
||||||
|
/// Use these to make your identifiers null.
|
||||||
|
/// You may also use zero initialization to get null.
|
||||||
|
static const b2WorldId b2_nullWorldId = B2_ZERO_INIT;
|
||||||
|
static const b2BodyId b2_nullBodyId = B2_ZERO_INIT;
|
||||||
|
static const b2ShapeId b2_nullShapeId = B2_ZERO_INIT;
|
||||||
|
static const b2ChainId b2_nullChainId = B2_ZERO_INIT;
|
||||||
|
static const b2JointId b2_nullJointId = B2_ZERO_INIT;
|
||||||
|
|
||||||
|
/// Macro to determine if any id is null.
|
||||||
|
#define B2_IS_NULL( id ) ( id.index1 == 0 )
|
||||||
|
|
||||||
|
/// Macro to determine if any id is non-null.
|
||||||
|
#define B2_IS_NON_NULL( id ) ( id.index1 != 0 )
|
||||||
|
|
||||||
|
/// Compare two ids for equality. Doesn't work for b2WorldId.
|
||||||
|
#define B2_ID_EQUALS( id1, id2 ) ( id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.generation == id2.generation )
|
||||||
|
|
||||||
|
/// Store a world id into a uint32_t.
|
||||||
|
B2_INLINE uint32_t b2StoreWorldId( b2WorldId id )
|
||||||
|
{
|
||||||
|
return ( (uint32_t)id.index1 << 16 ) | (uint32_t)id.generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a uint32_t into a world id.
|
||||||
|
B2_INLINE b2WorldId b2LoadWorldId( uint32_t x )
|
||||||
|
{
|
||||||
|
b2WorldId id = { (uint16_t)( x >> 16 ), (uint16_t)( x ) };
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store a body id into a uint64_t.
|
||||||
|
B2_INLINE uint64_t b2StoreBodyId( b2BodyId id )
|
||||||
|
{
|
||||||
|
return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a uint64_t into a body id.
|
||||||
|
B2_INLINE b2BodyId b2LoadBodyId( uint64_t x )
|
||||||
|
{
|
||||||
|
b2BodyId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store a shape id into a uint64_t.
|
||||||
|
B2_INLINE uint64_t b2StoreShapeId( b2ShapeId id )
|
||||||
|
{
|
||||||
|
return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a uint64_t into a shape id.
|
||||||
|
B2_INLINE b2ShapeId b2LoadShapeId( uint64_t x )
|
||||||
|
{
|
||||||
|
b2ShapeId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store a chain id into a uint64_t.
|
||||||
|
B2_INLINE uint64_t b2StoreChainId( b2ChainId id )
|
||||||
|
{
|
||||||
|
return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a uint64_t into a chain id.
|
||||||
|
B2_INLINE b2ChainId b2LoadChainId( uint64_t x )
|
||||||
|
{
|
||||||
|
b2ChainId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store a joint id into a uint64_t.
|
||||||
|
B2_INLINE uint64_t b2StoreJointId( b2JointId id )
|
||||||
|
{
|
||||||
|
return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a uint64_t into a joint id.
|
||||||
|
B2_INLINE b2JointId b2LoadJointId( uint64_t x )
|
||||||
|
{
|
||||||
|
b2JointId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
756
TSE_Core/include/box2d/math_functions.h
Normal file
756
TSE_Core/include/box2d/math_functions.h
Normal file
@@ -0,0 +1,756 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Erin Catto
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base.h"
|
||||||
|
|
||||||
|
#include <float.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup math Math
|
||||||
|
* @brief Vector math types and functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// 2D vector
|
||||||
|
/// This can be used to represent a point or free vector
|
||||||
|
typedef struct b2Vec2
|
||||||
|
{
|
||||||
|
/// coordinates
|
||||||
|
float x, y;
|
||||||
|
} b2Vec2;
|
||||||
|
|
||||||
|
/// Cosine and sine pair
|
||||||
|
/// This uses a custom implementation designed for cross-platform determinism
|
||||||
|
typedef struct b2CosSin
|
||||||
|
{
|
||||||
|
/// cosine and sine
|
||||||
|
float cosine;
|
||||||
|
float sine;
|
||||||
|
} b2CosSin;
|
||||||
|
|
||||||
|
/// 2D rotation
|
||||||
|
/// This is similar to using a complex number for rotation
|
||||||
|
typedef struct b2Rot
|
||||||
|
{
|
||||||
|
/// cosine and sine
|
||||||
|
float c, s;
|
||||||
|
} b2Rot;
|
||||||
|
|
||||||
|
/// A 2D rigid transform
|
||||||
|
typedef struct b2Transform
|
||||||
|
{
|
||||||
|
b2Vec2 p;
|
||||||
|
b2Rot q;
|
||||||
|
} b2Transform;
|
||||||
|
|
||||||
|
/// A 2-by-2 Matrix
|
||||||
|
typedef struct b2Mat22
|
||||||
|
{
|
||||||
|
/// columns
|
||||||
|
b2Vec2 cx, cy;
|
||||||
|
} b2Mat22;
|
||||||
|
|
||||||
|
/// Axis-aligned bounding box
|
||||||
|
typedef struct b2AABB
|
||||||
|
{
|
||||||
|
b2Vec2 lowerBound;
|
||||||
|
b2Vec2 upperBound;
|
||||||
|
} b2AABB;
|
||||||
|
|
||||||
|
/// separation = dot(normal, point) - offset
|
||||||
|
typedef struct b2Plane
|
||||||
|
{
|
||||||
|
b2Vec2 normal;
|
||||||
|
float offset;
|
||||||
|
} b2Plane;
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup math
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// https://en.wikipedia.org/wiki/Pi
|
||||||
|
#define B2_PI 3.14159265359f
|
||||||
|
|
||||||
|
static const b2Vec2 b2Vec2_zero = { 0.0f, 0.0f };
|
||||||
|
static const b2Rot b2Rot_identity = { 1.0f, 0.0f };
|
||||||
|
static const b2Transform b2Transform_identity = { { 0.0f, 0.0f }, { 1.0f, 0.0f } };
|
||||||
|
static const b2Mat22 b2Mat22_zero = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
|
||||||
|
|
||||||
|
/// Is this a valid number? Not NaN or infinity.
|
||||||
|
B2_API bool b2IsValidFloat( float a );
|
||||||
|
|
||||||
|
/// Is this a valid vector? Not NaN or infinity.
|
||||||
|
B2_API bool b2IsValidVec2( b2Vec2 v );
|
||||||
|
|
||||||
|
/// Is this a valid rotation? Not NaN or infinity. Is normalized.
|
||||||
|
B2_API bool b2IsValidRotation( b2Rot q );
|
||||||
|
|
||||||
|
/// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound.
|
||||||
|
B2_API bool b2IsValidAABB( b2AABB aabb );
|
||||||
|
|
||||||
|
/// Is this a valid plane? Normal is a unit vector. Not Nan or infinity.
|
||||||
|
B2_API bool b2IsValidPlane( b2Plane a );
|
||||||
|
|
||||||
|
/// @return the minimum of two integers
|
||||||
|
B2_INLINE int b2MinInt( int a, int b )
|
||||||
|
{
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return the maximum of two integers
|
||||||
|
B2_INLINE int b2MaxInt( int a, int b )
|
||||||
|
{
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return the absolute value of an integer
|
||||||
|
B2_INLINE int b2AbsInt( int a )
|
||||||
|
{
|
||||||
|
return a < 0 ? -a : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return an integer clamped between a lower and upper bound
|
||||||
|
B2_INLINE int b2ClampInt( int a, int lower, int upper )
|
||||||
|
{
|
||||||
|
return a < lower ? lower : ( a > upper ? upper : a );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return the minimum of two floats
|
||||||
|
B2_INLINE float b2MinFloat( float a, float b )
|
||||||
|
{
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return the maximum of two floats
|
||||||
|
B2_INLINE float b2MaxFloat( float a, float b )
|
||||||
|
{
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return the absolute value of a float
|
||||||
|
B2_INLINE float b2AbsFloat( float a )
|
||||||
|
{
|
||||||
|
return a < 0 ? -a : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return a float clamped between a lower and upper bound
|
||||||
|
B2_INLINE float b2ClampFloat( float a, float lower, float upper )
|
||||||
|
{
|
||||||
|
return a < lower ? lower : ( a > upper ? upper : a );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute an approximate arctangent in the range [-pi, pi]
|
||||||
|
/// This is hand coded for cross-platform determinism. The atan2f
|
||||||
|
/// function in the standard library is not cross-platform deterministic.
|
||||||
|
/// Accurate to around 0.0023 degrees
|
||||||
|
B2_API float b2Atan2( float y, float x );
|
||||||
|
|
||||||
|
/// Compute the cosine and sine of an angle in radians. Implemented
|
||||||
|
/// for cross-platform determinism.
|
||||||
|
B2_API b2CosSin b2ComputeCosSin( float radians );
|
||||||
|
|
||||||
|
/// Vector dot product
|
||||||
|
B2_INLINE float b2Dot( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return a.x * b.x + a.y * b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vector cross product. In 2D this yields a scalar.
|
||||||
|
B2_INLINE float b2Cross( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return a.x * b.y - a.y * b.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform the cross product on a vector and a scalar. In 2D this produces a vector.
|
||||||
|
B2_INLINE b2Vec2 b2CrossVS( b2Vec2 v, float s )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ s * v.y, -s * v.x };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform the cross product on a scalar and a vector. In 2D this produces a vector.
|
||||||
|
B2_INLINE b2Vec2 b2CrossSV( float s, b2Vec2 v )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ -s * v.y, s * v.x };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a left pointing perpendicular vector. Equivalent to b2CrossSV(1.0f, v)
|
||||||
|
B2_INLINE b2Vec2 b2LeftPerp( b2Vec2 v )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ -v.y, v.x };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a right pointing perpendicular vector. Equivalent to b2CrossVS(v, 1.0f)
|
||||||
|
B2_INLINE b2Vec2 b2RightPerp( b2Vec2 v )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ v.y, -v.x };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vector addition
|
||||||
|
B2_INLINE b2Vec2 b2Add( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ a.x + b.x, a.y + b.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vector subtraction
|
||||||
|
B2_INLINE b2Vec2 b2Sub( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ a.x - b.x, a.y - b.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vector negation
|
||||||
|
B2_INLINE b2Vec2 b2Neg( b2Vec2 a )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ -a.x, -a.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vector linear interpolation
|
||||||
|
/// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
|
||||||
|
B2_INLINE b2Vec2 b2Lerp( b2Vec2 a, b2Vec2 b, float t )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ ( 1.0f - t ) * a.x + t * b.x, ( 1.0f - t ) * a.y + t * b.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Component-wise multiplication
|
||||||
|
B2_INLINE b2Vec2 b2Mul( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ a.x * b.x, a.y * b.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply a scalar and vector
|
||||||
|
B2_INLINE b2Vec2 b2MulSV( float s, b2Vec2 v )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ s * v.x, s * v.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// a + s * b
|
||||||
|
B2_INLINE b2Vec2 b2MulAdd( b2Vec2 a, float s, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ a.x + s * b.x, a.y + s * b.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// a - s * b
|
||||||
|
B2_INLINE b2Vec2 b2MulSub( b2Vec2 a, float s, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ a.x - s * b.x, a.y - s * b.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Component-wise absolute vector
|
||||||
|
B2_INLINE b2Vec2 b2Abs( b2Vec2 a )
|
||||||
|
{
|
||||||
|
b2Vec2 b;
|
||||||
|
b.x = b2AbsFloat( a.x );
|
||||||
|
b.y = b2AbsFloat( a.y );
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Component-wise minimum vector
|
||||||
|
B2_INLINE b2Vec2 b2Min( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
b2Vec2 c;
|
||||||
|
c.x = b2MinFloat( a.x, b.x );
|
||||||
|
c.y = b2MinFloat( a.y, b.y );
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Component-wise maximum vector
|
||||||
|
B2_INLINE b2Vec2 b2Max( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
b2Vec2 c;
|
||||||
|
c.x = b2MaxFloat( a.x, b.x );
|
||||||
|
c.y = b2MaxFloat( a.y, b.y );
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Component-wise clamp vector v into the range [a, b]
|
||||||
|
B2_INLINE b2Vec2 b2Clamp( b2Vec2 v, b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
b2Vec2 c;
|
||||||
|
c.x = b2ClampFloat( v.x, a.x, b.x );
|
||||||
|
c.y = b2ClampFloat( v.y, a.y, b.y );
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the length of this vector (the norm)
|
||||||
|
B2_INLINE float b2Length( b2Vec2 v )
|
||||||
|
{
|
||||||
|
return sqrtf( v.x * v.x + v.y * v.y );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the distance between two points
|
||||||
|
B2_INLINE float b2Distance( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
float dx = b.x - a.x;
|
||||||
|
float dy = b.y - a.y;
|
||||||
|
return sqrtf( dx * dx + dy * dy );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a vector into a unit vector if possible, otherwise returns the zero vector.
|
||||||
|
/// todo MSVC is not inlining this function in several places per warning 4710
|
||||||
|
B2_INLINE b2Vec2 b2Normalize( b2Vec2 v )
|
||||||
|
{
|
||||||
|
float length = sqrtf( v.x * v.x + v.y * v.y );
|
||||||
|
if ( length < FLT_EPSILON )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ 0.0f, 0.0f };
|
||||||
|
}
|
||||||
|
|
||||||
|
float invLength = 1.0f / length;
|
||||||
|
b2Vec2 n = { invLength * v.x, invLength * v.y };
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the provided vector is normalized (norm(a) == 1).
|
||||||
|
B2_INLINE bool b2IsNormalized( b2Vec2 a )
|
||||||
|
{
|
||||||
|
float aa = b2Dot( a, a );
|
||||||
|
return b2AbsFloat( 1.0f - aa ) < 100.0f * FLT_EPSILON;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a vector into a unit vector if possible, otherwise returns the zero vector. Also
|
||||||
|
/// outputs the length.
|
||||||
|
B2_INLINE b2Vec2 b2GetLengthAndNormalize( float* length, b2Vec2 v )
|
||||||
|
{
|
||||||
|
*length = sqrtf( v.x * v.x + v.y * v.y );
|
||||||
|
if ( *length < FLT_EPSILON )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ 0.0f, 0.0f };
|
||||||
|
}
|
||||||
|
|
||||||
|
float invLength = 1.0f / *length;
|
||||||
|
b2Vec2 n = { invLength * v.x, invLength * v.y };
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Normalize rotation
|
||||||
|
B2_INLINE b2Rot b2NormalizeRot( b2Rot q )
|
||||||
|
{
|
||||||
|
float mag = sqrtf( q.s * q.s + q.c * q.c );
|
||||||
|
float invMag = mag > 0.0 ? 1.0f / mag : 0.0f;
|
||||||
|
b2Rot qn = { q.c * invMag, q.s * invMag };
|
||||||
|
return qn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Integrate rotation from angular velocity
|
||||||
|
/// @param q1 initial rotation
|
||||||
|
/// @param deltaAngle the angular displacement in radians
|
||||||
|
B2_INLINE b2Rot b2IntegrateRotation( b2Rot q1, float deltaAngle )
|
||||||
|
{
|
||||||
|
// dc/dt = -omega * sin(t)
|
||||||
|
// ds/dt = omega * cos(t)
|
||||||
|
// c2 = c1 - omega * h * s1
|
||||||
|
// s2 = s1 + omega * h * c1
|
||||||
|
b2Rot q2 = { q1.c - deltaAngle * q1.s, q1.s + deltaAngle * q1.c };
|
||||||
|
float mag = sqrtf( q2.s * q2.s + q2.c * q2.c );
|
||||||
|
float invMag = mag > 0.0 ? 1.0f / mag : 0.0f;
|
||||||
|
b2Rot qn = { q2.c * invMag, q2.s * invMag };
|
||||||
|
return qn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the length squared of this vector
|
||||||
|
B2_INLINE float b2LengthSquared( b2Vec2 v )
|
||||||
|
{
|
||||||
|
return v.x * v.x + v.y * v.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the distance squared between points
|
||||||
|
B2_INLINE float b2DistanceSquared( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
b2Vec2 c = { b.x - a.x, b.y - a.y };
|
||||||
|
return c.x * c.x + c.y * c.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make a rotation using an angle in radians
|
||||||
|
B2_INLINE b2Rot b2MakeRot( float radians )
|
||||||
|
{
|
||||||
|
b2CosSin cs = b2ComputeCosSin( radians );
|
||||||
|
return B2_LITERAL( b2Rot ){ cs.cosine, cs.sine };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the rotation between two unit vectors
|
||||||
|
B2_API b2Rot b2ComputeRotationBetweenUnitVectors( b2Vec2 v1, b2Vec2 v2 );
|
||||||
|
|
||||||
|
/// Is this rotation normalized?
|
||||||
|
B2_INLINE bool b2IsNormalizedRot( b2Rot q )
|
||||||
|
{
|
||||||
|
// larger tolerance due to failure on mingw 32-bit
|
||||||
|
float qq = q.s * q.s + q.c * q.c;
|
||||||
|
return 1.0f - 0.0006f < qq && qq < 1.0f + 0.0006f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Normalized linear interpolation
|
||||||
|
/// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
|
||||||
|
/// https://web.archive.org/web/20170825184056/http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
|
||||||
|
B2_INLINE b2Rot b2NLerp( b2Rot q1, b2Rot q2, float t )
|
||||||
|
{
|
||||||
|
float omt = 1.0f - t;
|
||||||
|
b2Rot q = {
|
||||||
|
omt * q1.c + t * q2.c,
|
||||||
|
omt * q1.s + t * q2.s,
|
||||||
|
};
|
||||||
|
|
||||||
|
float mag = sqrtf( q.s * q.s + q.c * q.c );
|
||||||
|
float invMag = mag > 0.0 ? 1.0f / mag : 0.0f;
|
||||||
|
b2Rot qn = { q.c * invMag, q.s * invMag };
|
||||||
|
return qn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the angular velocity necessary to rotate between two rotations over a give time
|
||||||
|
/// @param q1 initial rotation
|
||||||
|
/// @param q2 final rotation
|
||||||
|
/// @param inv_h inverse time step
|
||||||
|
B2_INLINE float b2ComputeAngularVelocity( b2Rot q1, b2Rot q2, float inv_h )
|
||||||
|
{
|
||||||
|
// ds/dt = omega * cos(t)
|
||||||
|
// dc/dt = -omega * sin(t)
|
||||||
|
// s2 = s1 + omega * h * c1
|
||||||
|
// c2 = c1 - omega * h * s1
|
||||||
|
|
||||||
|
// omega * h * s1 = c1 - c2
|
||||||
|
// omega * h * c1 = s2 - s1
|
||||||
|
// omega * h = (c1 - c2) * s1 + (s2 - s1) * c1;
|
||||||
|
// omega * h = s1 * c1 - c2 * s1 + s2 * c1 - s1 * c1
|
||||||
|
// omega * h = s2 * c1 - c2 * s1 = sin(a2 - a1) ~= a2 - a1 for small delta
|
||||||
|
float omega = inv_h * ( q2.s * q1.c - q2.c * q1.s );
|
||||||
|
return omega;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the angle in radians in the range [-pi, pi]
|
||||||
|
B2_INLINE float b2Rot_GetAngle( b2Rot q )
|
||||||
|
{
|
||||||
|
return b2Atan2( q.s, q.c );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the x-axis
|
||||||
|
B2_INLINE b2Vec2 b2Rot_GetXAxis( b2Rot q )
|
||||||
|
{
|
||||||
|
b2Vec2 v = { q.c, q.s };
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the y-axis
|
||||||
|
B2_INLINE b2Vec2 b2Rot_GetYAxis( b2Rot q )
|
||||||
|
{
|
||||||
|
b2Vec2 v = { -q.s, q.c };
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply two rotations: q * r
|
||||||
|
B2_INLINE b2Rot b2MulRot( b2Rot q, b2Rot r )
|
||||||
|
{
|
||||||
|
// [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc]
|
||||||
|
// [qs qc] [rs rc] [qs*rc+qc*rs -qs*rs+qc*rc]
|
||||||
|
// s(q + r) = qs * rc + qc * rs
|
||||||
|
// c(q + r) = qc * rc - qs * rs
|
||||||
|
b2Rot qr;
|
||||||
|
qr.s = q.s * r.c + q.c * r.s;
|
||||||
|
qr.c = q.c * r.c - q.s * r.s;
|
||||||
|
return qr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transpose multiply two rotations: qT * r
|
||||||
|
B2_INLINE b2Rot b2InvMulRot( b2Rot q, b2Rot r )
|
||||||
|
{
|
||||||
|
// [ qc qs] * [rc -rs] = [qc*rc+qs*rs -qc*rs+qs*rc]
|
||||||
|
// [-qs qc] [rs rc] [-qs*rc+qc*rs qs*rs+qc*rc]
|
||||||
|
// s(q - r) = qc * rs - qs * rc
|
||||||
|
// c(q - r) = qc * rc + qs * rs
|
||||||
|
b2Rot qr;
|
||||||
|
qr.s = q.c * r.s - q.s * r.c;
|
||||||
|
qr.c = q.c * r.c + q.s * r.s;
|
||||||
|
return qr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// relative angle between b and a (rot_b * inv(rot_a))
|
||||||
|
B2_INLINE float b2RelativeAngle( b2Rot b, b2Rot a )
|
||||||
|
{
|
||||||
|
// sin(b - a) = bs * ac - bc * as
|
||||||
|
// cos(b - a) = bc * ac + bs * as
|
||||||
|
float s = b.s * a.c - b.c * a.s;
|
||||||
|
float c = b.c * a.c + b.s * a.s;
|
||||||
|
return b2Atan2( s, c );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert any angle into the range [-pi, pi]
|
||||||
|
B2_INLINE float b2UnwindAngle( float radians )
|
||||||
|
{
|
||||||
|
// Assuming this is deterministic
|
||||||
|
return remainderf( radians, 2.0f * B2_PI );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotate a vector
|
||||||
|
B2_INLINE b2Vec2 b2RotateVector( b2Rot q, b2Vec2 v )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ q.c * v.x - q.s * v.y, q.s * v.x + q.c * v.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inverse rotate a vector
|
||||||
|
B2_INLINE b2Vec2 b2InvRotateVector( b2Rot q, b2Vec2 v )
|
||||||
|
{
|
||||||
|
return B2_LITERAL( b2Vec2 ){ q.c * v.x + q.s * v.y, -q.s * v.x + q.c * v.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform a point (e.g. local space to world space)
|
||||||
|
B2_INLINE b2Vec2 b2TransformPoint( b2Transform t, const b2Vec2 p )
|
||||||
|
{
|
||||||
|
float x = ( t.q.c * p.x - t.q.s * p.y ) + t.p.x;
|
||||||
|
float y = ( t.q.s * p.x + t.q.c * p.y ) + t.p.y;
|
||||||
|
|
||||||
|
return B2_LITERAL( b2Vec2 ){ x, y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inverse transform a point (e.g. world space to local space)
|
||||||
|
B2_INLINE b2Vec2 b2InvTransformPoint( b2Transform t, const b2Vec2 p )
|
||||||
|
{
|
||||||
|
float vx = p.x - t.p.x;
|
||||||
|
float vy = p.y - t.p.y;
|
||||||
|
return B2_LITERAL( b2Vec2 ){ t.q.c * vx + t.q.s * vy, -t.q.s * vx + t.q.c * vy };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply two transforms. If the result is applied to a point p local to frame B,
|
||||||
|
/// the transform would first convert p to a point local to frame A, then into a point
|
||||||
|
/// in the world frame.
|
||||||
|
/// v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p
|
||||||
|
/// = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p
|
||||||
|
B2_INLINE b2Transform b2MulTransforms( b2Transform A, b2Transform B )
|
||||||
|
{
|
||||||
|
b2Transform C;
|
||||||
|
C.q = b2MulRot( A.q, B.q );
|
||||||
|
C.p = b2Add( b2RotateVector( A.q, B.p ), A.p );
|
||||||
|
return C;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a transform that converts a local point in frame B to a local point in frame A.
|
||||||
|
/// v2 = A.q' * (B.q * v1 + B.p - A.p)
|
||||||
|
/// = A.q' * B.q * v1 + A.q' * (B.p - A.p)
|
||||||
|
B2_INLINE b2Transform b2InvMulTransforms( b2Transform A, b2Transform B )
|
||||||
|
{
|
||||||
|
b2Transform C;
|
||||||
|
C.q = b2InvMulRot( A.q, B.q );
|
||||||
|
C.p = b2InvRotateVector( A.q, b2Sub( B.p, A.p ) );
|
||||||
|
return C;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply a 2-by-2 matrix times a 2D vector
|
||||||
|
B2_INLINE b2Vec2 b2MulMV( b2Mat22 A, b2Vec2 v )
|
||||||
|
{
|
||||||
|
b2Vec2 u = {
|
||||||
|
A.cx.x * v.x + A.cy.x * v.y,
|
||||||
|
A.cx.y * v.x + A.cy.y * v.y,
|
||||||
|
};
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the inverse of a 2-by-2 matrix
|
||||||
|
B2_INLINE b2Mat22 b2GetInverse22( b2Mat22 A )
|
||||||
|
{
|
||||||
|
float a = A.cx.x, b = A.cy.x, c = A.cx.y, d = A.cy.y;
|
||||||
|
float det = a * d - b * c;
|
||||||
|
if ( det != 0.0f )
|
||||||
|
{
|
||||||
|
det = 1.0f / det;
|
||||||
|
}
|
||||||
|
|
||||||
|
b2Mat22 B = {
|
||||||
|
{ det * d, -det * c },
|
||||||
|
{ -det * b, det * a },
|
||||||
|
};
|
||||||
|
return B;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Solve A * x = b, where b is a column vector. This is more efficient
|
||||||
|
/// than computing the inverse in one-shot cases.
|
||||||
|
B2_INLINE b2Vec2 b2Solve22( b2Mat22 A, b2Vec2 b )
|
||||||
|
{
|
||||||
|
float a11 = A.cx.x, a12 = A.cy.x, a21 = A.cx.y, a22 = A.cy.y;
|
||||||
|
float det = a11 * a22 - a12 * a21;
|
||||||
|
if ( det != 0.0f )
|
||||||
|
{
|
||||||
|
det = 1.0f / det;
|
||||||
|
}
|
||||||
|
b2Vec2 x = { det * ( a22 * b.x - a12 * b.y ), det * ( a11 * b.y - a21 * b.x ) };
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does a fully contain b
|
||||||
|
B2_INLINE bool b2AABB_Contains( b2AABB a, b2AABB b )
|
||||||
|
{
|
||||||
|
bool s = true;
|
||||||
|
s = s && a.lowerBound.x <= b.lowerBound.x;
|
||||||
|
s = s && a.lowerBound.y <= b.lowerBound.y;
|
||||||
|
s = s && b.upperBound.x <= a.upperBound.x;
|
||||||
|
s = s && b.upperBound.y <= a.upperBound.y;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the center of the AABB.
|
||||||
|
B2_INLINE b2Vec2 b2AABB_Center( b2AABB a )
|
||||||
|
{
|
||||||
|
b2Vec2 b = { 0.5f * ( a.lowerBound.x + a.upperBound.x ), 0.5f * ( a.lowerBound.y + a.upperBound.y ) };
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the extents of the AABB (half-widths).
|
||||||
|
B2_INLINE b2Vec2 b2AABB_Extents( b2AABB a )
|
||||||
|
{
|
||||||
|
b2Vec2 b = { 0.5f * ( a.upperBound.x - a.lowerBound.x ), 0.5f * ( a.upperBound.y - a.lowerBound.y ) };
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Union of two AABBs
|
||||||
|
B2_INLINE b2AABB b2AABB_Union( b2AABB a, b2AABB b )
|
||||||
|
{
|
||||||
|
b2AABB c;
|
||||||
|
c.lowerBound.x = b2MinFloat( a.lowerBound.x, b.lowerBound.x );
|
||||||
|
c.lowerBound.y = b2MinFloat( a.lowerBound.y, b.lowerBound.y );
|
||||||
|
c.upperBound.x = b2MaxFloat( a.upperBound.x, b.upperBound.x );
|
||||||
|
c.upperBound.y = b2MaxFloat( a.upperBound.y, b.upperBound.y );
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do a and b overlap
|
||||||
|
B2_INLINE bool b2AABB_Overlaps( b2AABB a, b2AABB b )
|
||||||
|
{
|
||||||
|
return !( b.lowerBound.x > a.upperBound.x || b.lowerBound.y > a.upperBound.y || a.lowerBound.x > b.upperBound.x ||
|
||||||
|
a.lowerBound.y > b.upperBound.y );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the bounding box of an array of circles
|
||||||
|
B2_INLINE b2AABB b2MakeAABB( const b2Vec2* points, int count, float radius )
|
||||||
|
{
|
||||||
|
B2_ASSERT( count > 0 );
|
||||||
|
b2AABB a = { points[0], points[0] };
|
||||||
|
for ( int i = 1; i < count; ++i )
|
||||||
|
{
|
||||||
|
a.lowerBound = b2Min( a.lowerBound, points[i] );
|
||||||
|
a.upperBound = b2Max( a.upperBound, points[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
b2Vec2 r = { radius, radius };
|
||||||
|
a.lowerBound = b2Sub( a.lowerBound, r );
|
||||||
|
a.upperBound = b2Add( a.upperBound, r );
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signed separation of a point from a plane
|
||||||
|
B2_INLINE float b2PlaneSeparation( b2Plane plane, b2Vec2 point )
|
||||||
|
{
|
||||||
|
return b2Dot( plane.normal, point ) - plane.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One-dimensional mass-spring-damper simulation. Returns the new velocity given the position and time step.
|
||||||
|
/// You can then compute the new position using:
|
||||||
|
/// position += timeStep * newVelocity
|
||||||
|
/// This drives towards a zero position. By using implicit integration we get a stable solution
|
||||||
|
/// that doesn't require transcendental functions.
|
||||||
|
B2_INLINE float b2SpringDamper( float hertz, float dampingRatio, float position, float velocity, float timeStep )
|
||||||
|
{
|
||||||
|
float omega = 2.0f * B2_PI * hertz;
|
||||||
|
float omegaH = omega * timeStep;
|
||||||
|
return ( velocity - omega * omegaH * position ) / ( 1.0f + 2.0f * dampingRatio * omegaH + omegaH * omegaH );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Box2D bases all length units on meters, but you may need different units for your game.
|
||||||
|
/// You can set this value to use different units. This should be done at application startup
|
||||||
|
/// and only modified once. Default value is 1.
|
||||||
|
/// For example, if your game uses pixels for units you can use pixels for all length values
|
||||||
|
/// sent to Box2D. There should be no extra cost. However, Box2D has some internal tolerances
|
||||||
|
/// and thresholds that have been tuned for meters. By calling this function, Box2D is able
|
||||||
|
/// to adjust those tolerances and thresholds to improve accuracy.
|
||||||
|
/// A good rule of thumb is to pass the height of your player character to this function. So
|
||||||
|
/// if your player character is 32 pixels high, then pass 32 to this function. Then you may
|
||||||
|
/// confidently use pixels for all the length values sent to Box2D. All length values returned
|
||||||
|
/// from Box2D will also be pixels because Box2D does not do any scaling internally.
|
||||||
|
/// However, you are now on the hook for coming up with good values for gravity, density, and
|
||||||
|
/// forces.
|
||||||
|
/// @warning This must be modified before any calls to Box2D
|
||||||
|
B2_API void b2SetLengthUnitsPerMeter( float lengthUnits );
|
||||||
|
|
||||||
|
/// Get the current length units per meter.
|
||||||
|
B2_API float b2GetLengthUnitsPerMeter( void );
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup math_cpp C++ Math
|
||||||
|
* @brief Math operator overloads for C++
|
||||||
|
*
|
||||||
|
* See math_functions.h for details.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
/// Unary add one vector to another
|
||||||
|
inline void operator+=( b2Vec2& a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
a.x += b.x;
|
||||||
|
a.y += b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unary subtract one vector from another
|
||||||
|
inline void operator-=( b2Vec2& a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
a.x -= b.x;
|
||||||
|
a.y -= b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unary multiply a vector by a scalar
|
||||||
|
inline void operator*=( b2Vec2& a, float b )
|
||||||
|
{
|
||||||
|
a.x *= b;
|
||||||
|
a.y *= b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unary negate a vector
|
||||||
|
inline b2Vec2 operator-( b2Vec2 a )
|
||||||
|
{
|
||||||
|
return { -a.x, -a.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binary vector addition
|
||||||
|
inline b2Vec2 operator+( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return { a.x + b.x, a.y + b.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binary vector subtraction
|
||||||
|
inline b2Vec2 operator-( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return { a.x - b.x, a.y - b.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binary scalar and vector multiplication
|
||||||
|
inline b2Vec2 operator*( float a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return { a * b.x, a * b.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binary scalar and vector multiplication
|
||||||
|
inline b2Vec2 operator*( b2Vec2 a, float b )
|
||||||
|
{
|
||||||
|
return { a.x * b, a.y * b };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binary vector equality
|
||||||
|
inline bool operator==( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return a.x == b.x && a.y == b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binary vector inequality
|
||||||
|
inline bool operator!=( b2Vec2 a, b2Vec2 b )
|
||||||
|
{
|
||||||
|
return a.x != b.x || a.y != b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
1464
TSE_Core/include/box2d/types.h
Normal file
1464
TSE_Core/include/box2d/types.h
Normal file
File diff suppressed because it is too large
Load Diff
BIN
TSE_Core/include/box2dd.lib
Normal file
BIN
TSE_Core/include/box2dd.lib
Normal file
Binary file not shown.
BIN
TSE_Core/include/box2dd.pdb
Normal file
BIN
TSE_Core/include/box2dd.pdb
Normal file
Binary file not shown.
BIN
TSE_Core/include/libbox2d.a
Normal file
BIN
TSE_Core/include/libbox2d.a
Normal file
Binary file not shown.
10
TSE_Core/src/BehaviourScripts/AudioListener.cpp
Normal file
10
TSE_Core/src/BehaviourScripts/AudioListener.cpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include "AudioListener.hpp"
|
||||||
|
#include "Vector3.hpp"
|
||||||
|
#include "elements/Transformable.hpp"
|
||||||
|
#include "elements/AudioEngine.hpp"
|
||||||
|
|
||||||
|
void TSE::AudioListener::OnUpdate()
|
||||||
|
{
|
||||||
|
Vector3 pos = baseObject->GetGlobalPosition();
|
||||||
|
ma_engine_listener_set_position(AudioEngine::engine, 0, pos.x, pos.y, -pos.z);
|
||||||
|
}
|
||||||
17
TSE_Core/src/BehaviourScripts/AudioListener.hpp
Normal file
17
TSE_Core/src/BehaviourScripts/AudioListener.hpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define AUDIOLISTENER typeid(AudioListener).name()
|
||||||
|
|
||||||
|
#include "elements/BehaviourScript.hpp"
|
||||||
|
|
||||||
|
namespace TSE
|
||||||
|
{
|
||||||
|
class AudioListener : public BehaviourScript
|
||||||
|
{
|
||||||
|
void OnUpdate() override;
|
||||||
|
inline const char* GetName() override
|
||||||
|
{
|
||||||
|
return "Audio Listener";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace TSE
|
||||||
135
TSE_Core/src/BehaviourScripts/AudioSource.cpp
Normal file
135
TSE_Core/src/BehaviourScripts/AudioSource.cpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#include "AudioSource.hpp"
|
||||||
|
#include "Vector3.hpp"
|
||||||
|
#include "elements/Transformable.hpp"
|
||||||
|
|
||||||
|
float TSE::AudioSource::GetMinDistance()
|
||||||
|
{
|
||||||
|
return minDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
float TSE::AudioSource::GetMaxDistance()
|
||||||
|
{
|
||||||
|
return maxDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TSE::AudioSource::GetGlobal()
|
||||||
|
{
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::AudioSource::SetGlobal(bool v)
|
||||||
|
{
|
||||||
|
if(global != v)
|
||||||
|
{
|
||||||
|
auto it = sounds.begin();
|
||||||
|
for (int j = 0; j < sounds.size(); j++)
|
||||||
|
{
|
||||||
|
it->second;
|
||||||
|
ma_sound_set_spatialization_enabled(it->second, !v);
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
global = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::AudioSource::SetMinDistance(float v)
|
||||||
|
{
|
||||||
|
minDistance = v;
|
||||||
|
auto it = sounds.begin();
|
||||||
|
for (int j = 0; j < sounds.size(); j++)
|
||||||
|
{
|
||||||
|
it->second;
|
||||||
|
ma_sound_set_min_distance(it->second, minDistance);
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::AudioSource::SetMaxDistance(float v)
|
||||||
|
{
|
||||||
|
maxDistance = v;
|
||||||
|
auto it = sounds.begin();
|
||||||
|
for (int j = 0; j < sounds.size(); j++)
|
||||||
|
{
|
||||||
|
it->second;
|
||||||
|
ma_sound_set_max_distance(it->second, maxDistance);
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::AudioSource::AddClip(AudioClip *clip)
|
||||||
|
{
|
||||||
|
clips[clip->name] = clip;
|
||||||
|
ma_sound* s = clip->GetAudioSound();
|
||||||
|
ma_sound_set_spatialization_enabled(s, !global);
|
||||||
|
ma_sound_set_attenuation_model(s, ma_attenuation_model_linear);
|
||||||
|
ma_sound_set_rolloff(s, 1.0f);
|
||||||
|
ma_sound_set_min_distance(s, minDistance);
|
||||||
|
ma_sound_set_max_distance(s, maxDistance);
|
||||||
|
ma_sound_set_min_gain(s, 0.0f);
|
||||||
|
ma_sound_set_max_gain(s, 1.0f);
|
||||||
|
sounds[clip->name] = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::AudioSource::RemoveClip(std::string name)
|
||||||
|
{
|
||||||
|
if(currentlyPlaying == name) StopPlaying();
|
||||||
|
clips[name]->DestroyAudioSound(sounds[name]);
|
||||||
|
clips.erase(name);
|
||||||
|
sounds.erase(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::AudioSource::StartClip(std::string name, bool forceRestart)
|
||||||
|
{
|
||||||
|
if(currentlyPlaying != name){
|
||||||
|
StopPlaying();
|
||||||
|
}
|
||||||
|
else if(forceRestart)
|
||||||
|
{
|
||||||
|
ma_sound_seek_to_pcm_frame(sounds[name], 0);
|
||||||
|
}
|
||||||
|
ma_sound_start(sounds[name]);
|
||||||
|
currentlyPlaying = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::AudioSource::StopPlaying()
|
||||||
|
{
|
||||||
|
if(currentlyPlaying == "") return;
|
||||||
|
ma_sound_stop(sounds[currentlyPlaying]);
|
||||||
|
ma_sound_seek_to_pcm_frame(sounds[currentlyPlaying], 0);
|
||||||
|
currentlyPlaying = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::AudioSource::PausePlaying()
|
||||||
|
{
|
||||||
|
if(currentlyPlaying == "") return;
|
||||||
|
ma_sound_stop(sounds[currentlyPlaying]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TSE::AudioClip *TSE::AudioSource::GetClipAt(int i)
|
||||||
|
{
|
||||||
|
auto it = clips.begin();
|
||||||
|
for (int j = 0; j < i; j++)
|
||||||
|
{
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSE::AudioSource::~AudioSource()
|
||||||
|
{
|
||||||
|
int count = clips.size();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
std::string name = GetClipAt(0)->name;
|
||||||
|
RemoveClip(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::AudioSource::OnUpdate()
|
||||||
|
{
|
||||||
|
if(!global)
|
||||||
|
{
|
||||||
|
Vector3 pos = baseObject->GetGlobalPosition();
|
||||||
|
ma_sound_set_position(sounds[currentlyPlaying], pos.x, pos.y, -pos.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
50
TSE_Core/src/BehaviourScripts/AudioSource.hpp
Normal file
50
TSE_Core/src/BehaviourScripts/AudioSource.hpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define AUDIOSOURCE typeid(AudioSource).name()
|
||||||
|
|
||||||
|
#include "Types.hpp"
|
||||||
|
#include "elements/AudioClip.hpp"
|
||||||
|
#include "elements/BehaviourScript.hpp"
|
||||||
|
#include "miniaudio.h"
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace TSE
|
||||||
|
{
|
||||||
|
class AudioSource : public BehaviourScript
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::unordered_map<string, AudioClip*> clips;
|
||||||
|
string currentlyPlaying = "";
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool global = false;
|
||||||
|
float minDistance = 5;
|
||||||
|
float maxDistance = 8;
|
||||||
|
std::unordered_map<std::string, ma_sound*> sounds;
|
||||||
|
|
||||||
|
public:
|
||||||
|
float GetMinDistance();
|
||||||
|
float GetMaxDistance();
|
||||||
|
bool GetGlobal();
|
||||||
|
void SetGlobal(bool v);
|
||||||
|
void SetMinDistance(float v);
|
||||||
|
void SetMaxDistance(float v);
|
||||||
|
|
||||||
|
void AddClip(AudioClip* clip);
|
||||||
|
void RemoveClip(std::string name);
|
||||||
|
|
||||||
|
void StartClip(std::string name, bool forceRestart = true);
|
||||||
|
void StopPlaying();
|
||||||
|
void PausePlaying();
|
||||||
|
|
||||||
|
AudioClip* GetClipAt(int i);
|
||||||
|
|
||||||
|
~AudioSource();
|
||||||
|
|
||||||
|
void OnUpdate() override;
|
||||||
|
inline const char* GetName() override
|
||||||
|
{
|
||||||
|
return "Audio Source";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace TSE
|
||||||
@@ -132,6 +132,8 @@ void TSE::Camera::OnUpdate()
|
|||||||
{
|
{
|
||||||
mainCamera = this;
|
mainCamera = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(rt != nullptr)
|
||||||
IRenderer::camerasToRenderWith.push_back(this);
|
IRenderer::camerasToRenderWith.push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
117
TSE_Core/src/BehaviourScripts/PhysicsObject.cpp
Normal file
117
TSE_Core/src/BehaviourScripts/PhysicsObject.cpp
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#include "PhysicsObject.hpp"
|
||||||
|
#include "MathF.hpp"
|
||||||
|
#include "elements/Transformable.hpp"
|
||||||
|
#include "elements/PhysicsEngine.hpp"
|
||||||
|
|
||||||
|
TSE::PhysicsObject::PhysicsObject(BodyType t, ColliderShape s, float d, float f, Vector3 cs)
|
||||||
|
{
|
||||||
|
type = t;
|
||||||
|
shape = s;
|
||||||
|
density = d;
|
||||||
|
friction = f;
|
||||||
|
collidersize = cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSE::PhysicsObject::~PhysicsObject()
|
||||||
|
{
|
||||||
|
PhysicsEngine::UnRegisterPhysicsObject(this);
|
||||||
|
if(b2Body_IsValid(bodyId))
|
||||||
|
b2DestroyBody(bodyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::PhysicsObject::UpdatePosition()
|
||||||
|
{
|
||||||
|
b2Vec2 b2newPos = b2Body_GetPosition(bodyId);
|
||||||
|
if(lastPos.x != b2newPos.x || lastPos.y != b2newPos.y)
|
||||||
|
{
|
||||||
|
Vector3 newPos(b2newPos.x, b2newPos.y, lastPos.z);
|
||||||
|
lastPos = newPos;
|
||||||
|
Vector3 delta = baseObject->GlobalToLocalPosition(newPos);
|
||||||
|
baseObject->position = baseObject->position + delta;
|
||||||
|
}
|
||||||
|
float newRot = b2Rot_GetAngle(b2Body_GetRotation(bodyId));
|
||||||
|
if(newRot != lastRot)
|
||||||
|
{
|
||||||
|
lastRot = newRot;
|
||||||
|
Vector3 euler = baseObject->GetEuler();
|
||||||
|
euler.z = Rad2Deg(newRot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::PhysicsObject::OnUpdate()
|
||||||
|
{
|
||||||
|
Vector3 globalPos = baseObject->GetGlobalPosition();
|
||||||
|
float rot = Deg2Rad(baseObject->GetEuler().z);
|
||||||
|
if(globalPos != lastPos || rot != lastRot)
|
||||||
|
{
|
||||||
|
lastPos = globalPos;
|
||||||
|
lastRot = rot;
|
||||||
|
b2Vec2 pos;
|
||||||
|
pos.x = globalPos.x;
|
||||||
|
pos.y = globalPos.y;
|
||||||
|
b2Body_SetTransform(bodyId, pos, b2MakeRot(rot));
|
||||||
|
b2Body_SetAwake(bodyId, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::PhysicsObject::Start()
|
||||||
|
{
|
||||||
|
b2WorldId& wid = PhysicsEngine::GetWorldId();
|
||||||
|
|
||||||
|
b2BodyDef def = b2DefaultBodyDef();
|
||||||
|
b2Vec2 pos;
|
||||||
|
Vector3 globalPos = baseObject->GetGlobalPosition();
|
||||||
|
pos.x = globalPos.x;
|
||||||
|
pos.y = globalPos.y;
|
||||||
|
def.position = pos;
|
||||||
|
lastPos = globalPos;
|
||||||
|
float rot = Deg2Rad(baseObject->GetEuler().z);
|
||||||
|
def.rotation = b2MakeRot(rot);
|
||||||
|
lastRot = rot;
|
||||||
|
b2BodyType b2type;
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case BodyType::Dynamic:
|
||||||
|
b2type = b2_dynamicBody;
|
||||||
|
break;
|
||||||
|
case BodyType::Static:
|
||||||
|
b2type = b2_staticBody;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
def.type = b2type;
|
||||||
|
|
||||||
|
bodyId = b2CreateBody(wid, &def);
|
||||||
|
|
||||||
|
b2ShapeDef shapeDef = b2DefaultShapeDef();
|
||||||
|
shapeDef.density = density;
|
||||||
|
shapeDef.material.friction = friction;
|
||||||
|
|
||||||
|
b2Polygon b2shape;
|
||||||
|
b2Circle b2circle;
|
||||||
|
b2Capsule b2capsule;
|
||||||
|
|
||||||
|
switch (shape)
|
||||||
|
{
|
||||||
|
case ColliderShape::Box:
|
||||||
|
b2shape = b2MakeBox(collidersize.x * 0.5f, collidersize.y * 0.5f);
|
||||||
|
b2CreatePolygonShape(bodyId, &shapeDef, &b2shape);
|
||||||
|
break;
|
||||||
|
case ColliderShape::RoundedBox:
|
||||||
|
b2shape = b2MakeRoundedBox(collidersize.x * 0.5f, collidersize.y * 0.5f, collidersize.z);
|
||||||
|
b2CreatePolygonShape(bodyId, &shapeDef, &b2shape);
|
||||||
|
break;
|
||||||
|
case ColliderShape::Circle:
|
||||||
|
b2circle.radius = collidersize.x;
|
||||||
|
b2CreateCircleShape(bodyId, &shapeDef, &b2circle);
|
||||||
|
break;
|
||||||
|
case ColliderShape::Capsule:
|
||||||
|
b2capsule.radius = collidersize.x;
|
||||||
|
b2capsule.center1 = {0, collidersize.y * 0.5f};
|
||||||
|
b2capsule.center1 = {0, collidersize.y * -0.5f};
|
||||||
|
b2CreateCapsuleShape(bodyId, &shapeDef, &b2capsule);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicsEngine::RegisterPhysicsObject(this);
|
||||||
|
}
|
||||||
41
TSE_Core/src/BehaviourScripts/PhysicsObject.hpp
Normal file
41
TSE_Core/src/BehaviourScripts/PhysicsObject.hpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define PHYSICSOBJECT typeid(PhysicsObject).name()
|
||||||
|
|
||||||
|
#include "box2d/box2d.h"
|
||||||
|
#include "enums/PhysicsEnums.hpp"
|
||||||
|
#include "elements/BehaviourScript.hpp"
|
||||||
|
#include "Vector3.hpp"
|
||||||
|
|
||||||
|
namespace TSE
|
||||||
|
{
|
||||||
|
class PhysicsObject: public BehaviourScript
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
BodyType type;
|
||||||
|
ColliderShape shape;
|
||||||
|
float density;
|
||||||
|
float friction;
|
||||||
|
|
||||||
|
//in case of box: x = x, y = y, z = unused
|
||||||
|
//in case of circle: x = radius, y = unused, z = unused
|
||||||
|
//in case of capsule: x = radius, y = height, z = unused
|
||||||
|
//in case of RoundedBox: x = x, y = y, z = radius
|
||||||
|
Vector3 collidersize;
|
||||||
|
|
||||||
|
b2BodyId bodyId;
|
||||||
|
Vector3 lastPos;
|
||||||
|
float lastRot;
|
||||||
|
public:
|
||||||
|
PhysicsObject(BodyType type, ColliderShape shape, float density, float friction, Vector3 colliderSize);
|
||||||
|
~PhysicsObject();
|
||||||
|
|
||||||
|
void UpdatePosition();
|
||||||
|
void OnUpdate() override;
|
||||||
|
void Start() override;
|
||||||
|
inline const char* GetName() override
|
||||||
|
{
|
||||||
|
return "Physics Object";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace TSE
|
||||||
70
TSE_Core/src/elements/AudioClip.cpp
Normal file
70
TSE_Core/src/elements/AudioClip.cpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#include "AudioClip.hpp"
|
||||||
|
#include "Debug.hpp"
|
||||||
|
#include <filesystem>
|
||||||
|
#include "AudioEngine.hpp"
|
||||||
|
|
||||||
|
TSE::AudioClip::AudioClip(string path)
|
||||||
|
{
|
||||||
|
description.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSE::AudioClip::AudioClip(byte *stream, size_t size)
|
||||||
|
{
|
||||||
|
description.audiostream = stream;
|
||||||
|
description.dataSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_sound *TSE::AudioClip::GetAudioSound()
|
||||||
|
{
|
||||||
|
if(description.path != ""){
|
||||||
|
if(std::filesystem::exists(description.path))
|
||||||
|
{
|
||||||
|
ma_result res;
|
||||||
|
ma_sound* sound = (ma_sound*)malloc(sizeof(*sound));
|
||||||
|
|
||||||
|
res = ma_sound_init_from_file(AudioEngine::engine, description.path.c_str(), 0, nullptr, nullptr, sound);
|
||||||
|
if (res != MA_SUCCESS) {
|
||||||
|
TSE_WARNING("ma_sound_init_from_file failed: " + std::to_string(res));
|
||||||
|
delete(sound);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return sound;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TSE_WARNING("Can't create ma_sound because the given path dose not exist: \n" + description.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(description.audiostream != nullptr && description.dataSize > 0)
|
||||||
|
{
|
||||||
|
ma_result res;
|
||||||
|
ma_decoder m_decoder;
|
||||||
|
res = ma_decoder_init_memory(description.audiostream, description.dataSize, nullptr, &m_decoder);
|
||||||
|
if (res != MA_SUCCESS) {
|
||||||
|
TSE_WARNING("ma_decoder_init_memory failed: " + std::to_string(res));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ma_sound* sound = (ma_sound*)malloc(sizeof(*sound));
|
||||||
|
res = ma_sound_init_from_data_source(AudioEngine::engine, &m_decoder, MA_SOUND_FLAG_STREAM, nullptr, sound);
|
||||||
|
ma_decoder_uninit(&m_decoder);
|
||||||
|
if (res != MA_SUCCESS) {
|
||||||
|
TSE_WARNING("ma_sound_init_from_data_source failed: " + std::to_string(res));
|
||||||
|
delete(sound);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return sound;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TSE_WARNING("Can't create ma_sound because description is not set, or unconfigured.");
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::AudioClip::DestroyAudioSound(ma_sound *sound)
|
||||||
|
{
|
||||||
|
if (!sound) return;
|
||||||
|
|
||||||
|
ma_sound_uninit(sound);
|
||||||
|
delete sound;
|
||||||
|
}
|
||||||
28
TSE_Core/src/elements/AudioClip.hpp
Normal file
28
TSE_Core/src/elements/AudioClip.hpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Types.hpp"
|
||||||
|
#include "miniaudio.h"
|
||||||
|
|
||||||
|
namespace TSE
|
||||||
|
{
|
||||||
|
struct AudioClipDescription
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
string path = "";
|
||||||
|
byte* audiostream = nullptr;
|
||||||
|
size_t dataSize = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioClip
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
string name = "";
|
||||||
|
AudioClipDescription description;
|
||||||
|
|
||||||
|
AudioClip(string path);
|
||||||
|
AudioClip(byte* stream, size_t size);
|
||||||
|
|
||||||
|
ma_sound* GetAudioSound();
|
||||||
|
void DestroyAudioSound(ma_sound* sound);
|
||||||
|
};
|
||||||
|
} // namespace TSE
|
||||||
22
TSE_Core/src/elements/AudioEngine.cpp
Normal file
22
TSE_Core/src/elements/AudioEngine.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include "AudioEngine.hpp"
|
||||||
|
#include "Debug.hpp"
|
||||||
|
|
||||||
|
ma_engine* TSE::AudioEngine::engine = nullptr;
|
||||||
|
|
||||||
|
void TSE::AudioEngine::Init()
|
||||||
|
{
|
||||||
|
ma_result res;
|
||||||
|
engine = (ma_engine*)malloc(sizeof(*engine));
|
||||||
|
res = ma_engine_init(nullptr, engine);
|
||||||
|
if(res != MA_SUCCESS)
|
||||||
|
{
|
||||||
|
TSE_WARNING("Couldn't init audio engine. error code: " + std::to_string(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::AudioEngine::Destroy()
|
||||||
|
{
|
||||||
|
ma_engine_uninit(engine);
|
||||||
|
delete(engine);
|
||||||
|
engine = nullptr;
|
||||||
|
}
|
||||||
15
TSE_Core/src/elements/AudioEngine.hpp
Normal file
15
TSE_Core/src/elements/AudioEngine.hpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "miniaudio.h"
|
||||||
|
|
||||||
|
namespace TSE
|
||||||
|
{
|
||||||
|
class AudioEngine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static ma_engine* engine;
|
||||||
|
|
||||||
|
static void Init();
|
||||||
|
static void Destroy();
|
||||||
|
};
|
||||||
|
} // namespace TSE
|
||||||
57
TSE_Core/src/elements/PhysicsEngine.cpp
Normal file
57
TSE_Core/src/elements/PhysicsEngine.cpp
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#include "PhysicsEngine.hpp"
|
||||||
|
#include "utils/Time.hpp"
|
||||||
|
|
||||||
|
void TSE::PhysicsEngine::InitPhysics(Vector2 gravity)
|
||||||
|
{
|
||||||
|
b2WorldDef def = b2DefaultWorldDef();
|
||||||
|
def.enableSleep = true;
|
||||||
|
b2Vec2 grav;
|
||||||
|
grav.x = gravity.x;
|
||||||
|
grav.y = gravity.y;
|
||||||
|
def.gravity = grav;
|
||||||
|
worldId = b2CreateWorld(&def);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::PhysicsEngine::UpdatePhysics()
|
||||||
|
{
|
||||||
|
if(elapsedTime >= timestep)
|
||||||
|
{
|
||||||
|
elapsedTime = 0;
|
||||||
|
b2World_Step(worldId, timestep, iterations);
|
||||||
|
//update physics objects
|
||||||
|
}
|
||||||
|
elapsedTime += Time::deltaTime();
|
||||||
|
|
||||||
|
for(auto obj : registeredObjects)
|
||||||
|
{
|
||||||
|
obj->UpdatePosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::PhysicsEngine::DeletePhysics()
|
||||||
|
{
|
||||||
|
b2DestroyWorld(worldId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::PhysicsEngine::RegisterPhysicsObject(PhysicsObject *obj)
|
||||||
|
{
|
||||||
|
registeredObjects.push_back(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::PhysicsEngine::UnRegisterPhysicsObject(PhysicsObject *obj)
|
||||||
|
{
|
||||||
|
auto it = registeredObjects.begin();
|
||||||
|
for (; it != registeredObjects.end(); it++)
|
||||||
|
{
|
||||||
|
if(*it == obj)
|
||||||
|
{
|
||||||
|
registeredObjects.erase(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b2WorldId &TSE::PhysicsEngine::GetWorldId()
|
||||||
|
{
|
||||||
|
return worldId;
|
||||||
|
}
|
||||||
28
TSE_Core/src/elements/PhysicsEngine.hpp
Normal file
28
TSE_Core/src/elements/PhysicsEngine.hpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BehaviourScripts/PhysicsObject.hpp"
|
||||||
|
#include "box2d/box2d.h"
|
||||||
|
#include <vector>
|
||||||
|
#include "Vector2.hpp"
|
||||||
|
|
||||||
|
namespace TSE
|
||||||
|
{
|
||||||
|
class PhysicsEngine
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
inline static std::vector<PhysicsObject*> registeredObjects = std::vector<PhysicsObject*>();
|
||||||
|
inline static b2WorldId worldId = b2WorldId();
|
||||||
|
inline static constexpr float timestep = 1.0f / 60.0f;
|
||||||
|
inline static const int iterations = 4;
|
||||||
|
inline static float elapsedTime = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void InitPhysics(Vector2 gravity);
|
||||||
|
static void UpdatePhysics();
|
||||||
|
static void DeletePhysics();
|
||||||
|
static void RegisterPhysicsObject(PhysicsObject* obj);
|
||||||
|
static void UnRegisterPhysicsObject(PhysicsObject* obj);
|
||||||
|
|
||||||
|
static b2WorldId& GetWorldId();
|
||||||
|
};
|
||||||
|
} // namespace TSE
|
||||||
@@ -118,12 +118,12 @@ TSE::Vector2 TSE::Texture::size() const
|
|||||||
return Size;
|
return Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
float TSE::Texture::width() const
|
float TSE::Texture::Width() const
|
||||||
{
|
{
|
||||||
return Size.x;
|
return Size.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
float TSE::Texture::height() const
|
float TSE::Texture::Height() const
|
||||||
{
|
{
|
||||||
return Size.y;
|
return Size.y;
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ void TSE::Texture::SetChanels(const byte &ch)
|
|||||||
chanels = ch;
|
chanels = ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint TSE::Texture::GetTextureId() const
|
TSE::uint TSE::Texture::GetTextureId() const
|
||||||
{
|
{
|
||||||
return TextureID;
|
return TextureID;
|
||||||
}
|
}
|
||||||
@@ -181,9 +181,9 @@ void TSE::Texture::SetPixel(const int &x, const int &y, const Color &c)
|
|||||||
|
|
||||||
void TSE::Texture::GetPixel(const int &x, const int &y, Color &c) const
|
void TSE::Texture::GetPixel(const int &x, const int &y, Color &c) const
|
||||||
{
|
{
|
||||||
if(x >= width() || x < 0 ||y >= height() || y < 0)
|
if(x >= Width() || x < 0 ||y >= Height() || y < 0)
|
||||||
{
|
{
|
||||||
TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ")\nTexture size: (" + std::to_string(width()) + ";" + std::to_string(height()) );
|
TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ")\nTexture size: (" + std::to_string(Width()) + ";" + std::to_string(Height()) );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
byte* pixel = getPixelPointer(x,y);
|
byte* pixel = getPixelPointer(x,y);
|
||||||
@@ -201,9 +201,9 @@ void TSE::Texture::GetPixel(const int &x, const int &y, Color &c) const
|
|||||||
|
|
||||||
void TSE::Texture::Fill(const Color &c)
|
void TSE::Texture::Fill(const Color &c)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < width(); x++)
|
for (int x = 0; x < Width(); x++)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < height(); y++)
|
for (int y = 0; y < Height(); y++)
|
||||||
{
|
{
|
||||||
SetPixelNoApply(x,y,c);
|
SetPixelNoApply(x,y,c);
|
||||||
}
|
}
|
||||||
@@ -214,9 +214,9 @@ void TSE::Texture::Fill(const Color &c)
|
|||||||
|
|
||||||
void TSE::Texture::SetPixelNoApply(const int &x, const int &y, const Color &c)
|
void TSE::Texture::SetPixelNoApply(const int &x, const int &y, const Color &c)
|
||||||
{
|
{
|
||||||
if(x >= width() || x < 0 ||y >= height() || y < 0)
|
if(x >= Width() || x < 0 ||y >= Height() || y < 0)
|
||||||
{
|
{
|
||||||
TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ")\nTexture size: (" + std::to_string(width()) + ";" + std::to_string(height()) );
|
TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ")\nTexture size: (" + std::to_string(Width()) + ";" + std::to_string(Height()) );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,9 +244,9 @@ void TSE::Texture::SetPixelNoApply(const int &x, const int &y, const Color &c)
|
|||||||
|
|
||||||
void TSE::Texture::AddPixelNoApply(const int &x, const int &y, const Color &c)
|
void TSE::Texture::AddPixelNoApply(const int &x, const int &y, const Color &c)
|
||||||
{
|
{
|
||||||
if(x >= width() || x < 0 ||y >= height() || y < 0)
|
if(x >= Width() || x < 0 ||y >= Height() || y < 0)
|
||||||
{
|
{
|
||||||
TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ")\nTexture size: (" + std::to_string(width()) + ";" + std::to_string(height()) );
|
TSE_WARNING("trying to access pixel outside of texture.\n pixel: (" + std::to_string(x) + ";" + std::to_string(y) + ")\nTexture size: (" + std::to_string(Width()) + ";" + std::to_string(Height()) );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +307,7 @@ TSE::byte *TSE::Texture::getPixelPointer(const int &x, const int &y) const
|
|||||||
int alphaoffset = y * 2;
|
int alphaoffset = y * 2;
|
||||||
if(bpp() > 24)
|
if(bpp() > 24)
|
||||||
alphaoffset = 0;
|
alphaoffset = 0;
|
||||||
int offset = ((y * width() + x) * (bpp() / 8) + alphaoffset);
|
int offset = ((y * Width() + x) * (bpp() / 8) + alphaoffset);
|
||||||
return imagePtr + offset;
|
return imagePtr + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ namespace TSE
|
|||||||
|
|
||||||
uint bpp() const;
|
uint bpp() const;
|
||||||
Vector2 size() const override;
|
Vector2 size() const override;
|
||||||
float width() const override;
|
float Width() const override;
|
||||||
float height() const override;
|
float Height() const override;
|
||||||
byte Chanels() const;
|
byte Chanels() const;
|
||||||
byte* GetImagePtr() const;
|
byte* GetImagePtr() const;
|
||||||
void SetPixel(const Vector2& pos, const Color& c);
|
void SetPixel(const Vector2& pos, const Color& c);
|
||||||
|
|||||||
16
TSE_Core/src/enums/PhysicsEnums.hpp
Normal file
16
TSE_Core/src/enums/PhysicsEnums.hpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace TSE
|
||||||
|
{
|
||||||
|
enum BodyType {
|
||||||
|
Static,
|
||||||
|
Dynamic,
|
||||||
|
};
|
||||||
|
enum ColliderShape {
|
||||||
|
Box,
|
||||||
|
RoundedBox,
|
||||||
|
Circle,
|
||||||
|
Capsule,
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace TSE
|
||||||
2
TSE_Core/src/extern/miniaudio.c
vendored
Normal file
2
TSE_Core/src/extern/miniaudio.c
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define MINIAUDIO_IMPLEMENTATION
|
||||||
|
#include "miniaudio.h"
|
||||||
23
TSE_Core/src/interfaces/IRenderTexture.hpp
Normal file
23
TSE_Core/src/interfaces/IRenderTexture.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IResizeNotifiable.hpp"
|
||||||
|
#include "IRenderTarget.hpp"
|
||||||
|
#include "ITexture.hpp"
|
||||||
|
|
||||||
|
namespace TSE
|
||||||
|
{
|
||||||
|
class IRenderTextureCreator;
|
||||||
|
|
||||||
|
class IRenderTexture : public IRenderTarget, public ITexture, public IResizeNotifiable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline static IRenderTextureCreator* factory = nullptr;
|
||||||
|
virtual void SetSize(Vector2 v) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IRenderTextureCreator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual IRenderTexture* CreateTextureHeap(Vector2 v) = 0;
|
||||||
|
};
|
||||||
|
} // namespace TSE
|
||||||
@@ -9,8 +9,8 @@ namespace TSE
|
|||||||
public:
|
public:
|
||||||
virtual ~ITexture() = default;
|
virtual ~ITexture() = default;
|
||||||
virtual Vector2 size() const = 0;
|
virtual Vector2 size() const = 0;
|
||||||
virtual float width() const = 0;
|
virtual float Width() const = 0;
|
||||||
virtual float height() const = 0;
|
virtual float Height() const = 0;
|
||||||
virtual uint GetTextureId() const = 0;
|
virtual uint GetTextureId() const = 0;
|
||||||
};
|
};
|
||||||
} // namespace TSE
|
} // namespace TSE
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "utils/Time.hpp"
|
#include "utils/Time.hpp"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "IInputManager.hpp"
|
#include "IInputManager.hpp"
|
||||||
|
#include "elements/AudioEngine.hpp"
|
||||||
|
|
||||||
TSE::IWindow* TSE::IWindow::lastWindow = nullptr;
|
TSE::IWindow* TSE::IWindow::lastWindow = nullptr;
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ bool TSE::IWindow::BaseInit() const
|
|||||||
LuaStateHandler::InitLuaState();
|
LuaStateHandler::InitLuaState();
|
||||||
Debug::Init();
|
Debug::Init();
|
||||||
Debug::Log("TSE:" + TSE_VERSION_STRING);
|
Debug::Log("TSE:" + TSE_VERSION_STRING);
|
||||||
|
AudioEngine::Init();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,6 +24,7 @@ void TSE::IWindow::BaseUpdate() const
|
|||||||
|
|
||||||
TSE::IWindow::~IWindow()
|
TSE::IWindow::~IWindow()
|
||||||
{
|
{
|
||||||
|
AudioEngine::Destroy();
|
||||||
IInputManager::instance()->Delete();
|
IInputManager::instance()->Delete();
|
||||||
Time::Destroy();
|
Time::Destroy();
|
||||||
Debug::Close();
|
Debug::Close();
|
||||||
|
|||||||
59
TSE_Editor/CMakeLists.txt
Normal file
59
TSE_Editor/CMakeLists.txt
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#cmake version
|
||||||
|
cmake_minimum_required(VERSION 3.31)
|
||||||
|
|
||||||
|
#project name
|
||||||
|
project(TSE_Editor)
|
||||||
|
|
||||||
|
#cpp settings
|
||||||
|
find_program(CLANG_C NAMES clang)
|
||||||
|
find_program(CLANG_CXX NAMES clang++)
|
||||||
|
|
||||||
|
if(CLANG_C AND CLANG_CXX)
|
||||||
|
message(STATUS "foung Clang, using as Compiler")
|
||||||
|
set(CMAKE_C_COMPILER ${CLANG_C} CACHE STRING "C Compiler" FORCE)
|
||||||
|
set(CMAKE_CXX_COMPILER ${CLANG_CXX} CACHE STRING "C++ Compiler" FORCE)
|
||||||
|
else()
|
||||||
|
message(STATUS "Clang not found, using Standard-Compiler")
|
||||||
|
endif()
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
|
|
||||||
|
#project output settings
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/lib")
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/lib/Debug")
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/lib/Release")
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/lib")
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/lib/Debug")
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/lib/Release")
|
||||||
|
|
||||||
|
#source files
|
||||||
|
file(GLOB CPP_SOURCE_TSE
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/*.cpp"
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/*/*.cpp"
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/*/*/*.cpp"
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/*/*/*/*.cpp"
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/*.c"
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/*/*.c"
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/*/*/*.c"
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/*/*/*/*.c"
|
||||||
|
)
|
||||||
|
|
||||||
|
#includes
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Base/src)
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Base/include)
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Math/src)
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Core/src)
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Core/include)
|
||||||
|
|
||||||
|
#project def
|
||||||
|
if(Lib)
|
||||||
|
add_library(TSE_Editor SHARED ${CPP_SOURCE_TSE})
|
||||||
|
else()
|
||||||
|
add_library(TSE_Editor STATIC ${CPP_SOURCE_TSE})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#flags
|
||||||
|
target_compile_options(TSE_Editor PRIVATE -march=native)
|
||||||
|
|
||||||
53
TSE_Editor/src/BehaviourScriptRegistry.hpp
Normal file
53
TSE_Editor/src/BehaviourScriptRegistry.hpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "Types.hpp"
|
||||||
|
#include <functional>
|
||||||
|
#include "elements/BehaviourScript.hpp"
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
class BehaviourScriptRegistry
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using BehaviourScriptCtor = BehaviourScript* (*)();
|
||||||
|
|
||||||
|
inline static std::unordered_map<string, BehaviourScriptCtor> registeredScripts = {};
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline static void RegisterBehaviourScript(string name, BehaviourScriptCtor ctor )
|
||||||
|
{
|
||||||
|
registeredScripts[name] = ctor;
|
||||||
|
}
|
||||||
|
inline static BehaviourScript* CreateBehaviourScript(string name)
|
||||||
|
{
|
||||||
|
auto it = registeredScripts.find(name);
|
||||||
|
if (it != registeredScripts.end())
|
||||||
|
return it->second();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
inline static void UnregisterBehaviourScript(string name)
|
||||||
|
{
|
||||||
|
registeredScripts.erase(name);
|
||||||
|
}
|
||||||
|
inline static int GetScriptCount()
|
||||||
|
{
|
||||||
|
return registeredScripts.size();
|
||||||
|
}
|
||||||
|
inline static string GetScriptNameAt(int i)
|
||||||
|
{
|
||||||
|
if (i < 0 || i >= static_cast<int>(registeredScripts.size()))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
auto it = registeredScripts.begin();
|
||||||
|
std::advance(it, i);
|
||||||
|
return it->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const auto& GetRegistry()
|
||||||
|
{
|
||||||
|
return registeredScripts;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace TSE::EDITOR
|
||||||
|
|
||||||
44
TSE_Editor/src/EditorSubsystem.cpp
Normal file
44
TSE_Editor/src/EditorSubsystem.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#include "EditorSubsystem.hpp"
|
||||||
|
|
||||||
|
#include "BehaviourScriptRegistry.hpp"
|
||||||
|
#include "BehaviourScripts/AudioListener.hpp"
|
||||||
|
#include "BehaviourScripts/AudioSource.hpp"
|
||||||
|
|
||||||
|
TSE::EDITOR::EditorSubsystem::EditorSubsystem() : sv(nullptr), editorLayer("")
|
||||||
|
{
|
||||||
|
rt = IRenderTexture::factory->CreateTextureHeap({100,100});
|
||||||
|
sv = SceneView(rt);
|
||||||
|
|
||||||
|
controller.AddGuiElement("Scene", &sv);
|
||||||
|
controller.AddGuiElement("Consol", &cv);
|
||||||
|
controller.AddGuiElement("Hirearchie", &hv);
|
||||||
|
controller.AddGuiElement("Properties", &pv);
|
||||||
|
controller.AddGuiElement("Debug", &dv);
|
||||||
|
controller.AddGuiElement("Camera", &camv);
|
||||||
|
|
||||||
|
BehaviourScriptRegistry::RegisterBehaviourScript("Image", []() -> BehaviourScript* {return new Image();});
|
||||||
|
BehaviourScriptRegistry::RegisterBehaviourScript("Image Animation", []() -> BehaviourScript* {return new ImageAnimation();});
|
||||||
|
BehaviourScriptRegistry::RegisterBehaviourScript("Mesh Container", []() -> BehaviourScript* {return new MeshContainer();});
|
||||||
|
BehaviourScriptRegistry::RegisterBehaviourScript("Rect Base", []() -> BehaviourScript* {return new RectBase();});
|
||||||
|
BehaviourScriptRegistry::RegisterBehaviourScript("Renderable", []() -> BehaviourScript* {return new Renderable();});
|
||||||
|
BehaviourScriptRegistry::RegisterBehaviourScript("Particle System", []() -> BehaviourScript* {return new ParticleSystem();});
|
||||||
|
BehaviourScriptRegistry::RegisterBehaviourScript("Camera", []() -> BehaviourScript* {return new Camera();});
|
||||||
|
BehaviourScriptRegistry::RegisterBehaviourScript("Audio Listener", []() -> BehaviourScript* {return new AudioListener();});
|
||||||
|
BehaviourScriptRegistry::RegisterBehaviourScript("Audio Source", []() -> BehaviourScript* {return new AudioSource();});
|
||||||
|
|
||||||
|
#pragma region camerastuff
|
||||||
|
|
||||||
|
Transformable* editorCamera = new Transformable(".EditorCamera");
|
||||||
|
|
||||||
|
Camera* editorCam = new Camera();
|
||||||
|
editorCam->SetRenderTarget(rt);
|
||||||
|
editorCamera->AddBehaviourScript(editorCam);
|
||||||
|
|
||||||
|
// basicCameraControls controls = basicCameraControls();
|
||||||
|
// editorCamera->AddBehaviourScript(&controls);
|
||||||
|
|
||||||
|
editorLayer = Layer(".editor");
|
||||||
|
editorLayer.AddTransformable(editorCamera);
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
}
|
||||||
29
TSE_Editor/src/EditorSubsystem.hpp
Normal file
29
TSE_Editor/src/EditorSubsystem.hpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "UI/ViewportController.hpp"
|
||||||
|
#include "UI/windows/CameraView.hpp"
|
||||||
|
#include "UI/windows/ConsolView.hpp"
|
||||||
|
#include "UI/windows/DebugView.hpp"
|
||||||
|
#include "UI/windows/HirearchieView.hpp"
|
||||||
|
#include "UI/windows/PropertiesView.hpp"
|
||||||
|
#include "UI/windows/SceneView.hpp"
|
||||||
|
#include "interfaces/IRenderTexture.hpp"
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
class EditorSubsystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ViewportController controller;
|
||||||
|
ConsolView cv;
|
||||||
|
DebugView dv;
|
||||||
|
HirearchieView hv = HirearchieView(nullptr);
|
||||||
|
PropertiesView pv;
|
||||||
|
CameraView camv;
|
||||||
|
TSE::IRenderTexture* rt;
|
||||||
|
SceneView sv;
|
||||||
|
Layer editorLayer;
|
||||||
|
|
||||||
|
EditorSubsystem();
|
||||||
|
};
|
||||||
|
} // namespace TSE::EDITOR
|
||||||
1090
TSE_Editor/src/UI/ElementDrawer.cpp
Normal file
1090
TSE_Editor/src/UI/ElementDrawer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
94
TSE_Editor/src/UI/ElementDrawer.hpp
Normal file
94
TSE_Editor/src/UI/ElementDrawer.hpp
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
#include "Types.hpp"
|
||||||
|
#include "elements/Transformable.hpp"
|
||||||
|
#include "elements/Scene.hpp"
|
||||||
|
#include "elements/Layer.hpp"
|
||||||
|
#include "Debug.hpp"
|
||||||
|
#include "BehaviourScripts/Renderable.hpp"
|
||||||
|
#include "BehaviourScripts/MeshContainer.hpp"
|
||||||
|
#include "BehaviourScripts/ImageAnimation.hpp"
|
||||||
|
#include "BehaviourScripts/RectBase.hpp"
|
||||||
|
#include "BehaviourScripts/ParticleSystem.hpp"
|
||||||
|
#include "BehaviourScripts/AudioListener.hpp"
|
||||||
|
#include "BehaviourScripts/AudioSource.hpp"
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
enum class InspectableType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Transformable,
|
||||||
|
Scene,
|
||||||
|
Layer
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Inspectable
|
||||||
|
{
|
||||||
|
InspectableType type = InspectableType::None;
|
||||||
|
void* ptr = nullptr;
|
||||||
|
|
||||||
|
Inspectable() = default;
|
||||||
|
Inspectable(InspectableType t, void* p) : type(t), ptr(p) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ElementDrawer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
inline static bool addDropdownOpen = false;
|
||||||
|
inline static char searchBuffer[128] = "";
|
||||||
|
inline static ImGuiTextFilter filter;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void Draw(const Inspectable& element,const bool& debug) {
|
||||||
|
switch (element.type) {
|
||||||
|
case InspectableType::Transformable:
|
||||||
|
Draw(static_cast<Transformable*>(element.ptr), debug);
|
||||||
|
break;
|
||||||
|
case InspectableType::Scene:
|
||||||
|
Draw(static_cast<Scene*>(element.ptr), debug);
|
||||||
|
break;
|
||||||
|
case InspectableType::Layer:
|
||||||
|
Draw(static_cast<Layer*>(element.ptr), debug);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
TSE_WARNING("Draw not implemented for this type.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static void DrawAddDropdown(const std::vector<std::string>& options, std::string& selectedOption);
|
||||||
|
static void Draw(Transformable* element,const bool& debug);
|
||||||
|
static void Draw(Scene* element,const bool& debug);
|
||||||
|
static void Draw(Layer* element,const bool& debug);
|
||||||
|
static void Draw(BehaviourScript* element,const bool& debug, const int& id = 0);
|
||||||
|
static void Draw(Renderable* element, const bool& debug);
|
||||||
|
static void Draw(MeshContainer* element, const bool& debug);
|
||||||
|
static void Draw(Image* element, const bool& debug);
|
||||||
|
static void Draw(ImageAnimation* element, const bool& debug);
|
||||||
|
static void Draw(RectBase* element, const bool& debug);
|
||||||
|
static void Draw(Material* element, const bool& debug);
|
||||||
|
static void Draw(Texture* element, const bool& debug, const std::string& label = "", const bool small = false);
|
||||||
|
static void Draw(Sprite* element, const bool& debug, const std::string& label = "", const bool small = false);
|
||||||
|
static void Draw(Mesh* element, const bool& debug, const std::string& label = "", const bool small = false);
|
||||||
|
static void Draw(ImageAnimationSet* element, const bool& debug, const std::string& label = "", const bool small = false);
|
||||||
|
static void Draw(AudioSource* element, const bool& debug);
|
||||||
|
static void Draw(AudioClip* element, const bool& debug, const std::string& label = "", const bool small = false);
|
||||||
|
static void Draw(Camera* element, const bool& debug);
|
||||||
|
static void Draw(ParticleSystem* element, const bool& debug);
|
||||||
|
|
||||||
|
static void DrawAudioClipCompact(AudioClip* element, const bool& debug, const std::string& label);
|
||||||
|
static void DrawAudioClipNormal(AudioClip* element, const bool& debug, const std::string& label);
|
||||||
|
static void DrawImageAnimationSetCompact(ImageAnimationSet* element, const bool& debug, const std::string& label);
|
||||||
|
static void DrawImageAnimationSetNormal(ImageAnimationSet* element, const bool& debug, const std::string& label);
|
||||||
|
static void DrawMeshCompact(Mesh* element, const bool& debug, const std::string& label);
|
||||||
|
static void DrawMeshNormal(Mesh* element, const bool& debug, const std::string& label);
|
||||||
|
static void DrawSpriteCompact(Sprite* element, const bool& debug, const std::string& label);
|
||||||
|
static void DrawSpriteNormal(Sprite* element, const bool& debug, const std::string& label);
|
||||||
|
static void DrawTextureCompact(Texture* element, const bool& debug, const std::string& label);
|
||||||
|
static void DrawTextureNormal(Texture* element, const bool& debug, const std::string& label);
|
||||||
|
};
|
||||||
|
} // namespace TSE::EDITOR
|
||||||
213
TSE_Editor/src/UI/ViewportController.cpp
Normal file
213
TSE_Editor/src/UI/ViewportController.cpp
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
#include "ViewportController.hpp"
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "tinyfiledialogs.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
TSE::EDITOR::ViewportController::ViewportController()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable | ImGuiConfigFlags_DockingEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wantOpenCreate = false;
|
||||||
|
|
||||||
|
void TSE::EDITOR::ViewportController::ShowFileMenu()
|
||||||
|
{
|
||||||
|
// if(ImGui::MenuItem("New Project"))
|
||||||
|
// {
|
||||||
|
// wantOpenCreate = true;
|
||||||
|
// gCreateState.confirmed = false;
|
||||||
|
// gCreateState.nameBuf[0] = '\0';
|
||||||
|
// gCreateState.pathBuf[0] = '\0';
|
||||||
|
// }
|
||||||
|
// if(ImGui::MenuItem("Load Project"))
|
||||||
|
// {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// if(ImGui::MenuItem("Save Project"))
|
||||||
|
// {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// ImGui::Separator();
|
||||||
|
// if(ImGui::MenuItem("Build"))
|
||||||
|
// {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// ImGui::Separator();
|
||||||
|
if(ImGui::MenuItem("Exit"))
|
||||||
|
{
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<std::string> PickFolderBlocking(const char* startPath)
|
||||||
|
{
|
||||||
|
const char* chosen = tinyfd_selectFolderDialog("Ordner wählen", startPath);
|
||||||
|
if (chosen && chosen[0]) return std::string(chosen);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TSE::EDITOR::ViewportController::NewProjectPopup()
|
||||||
|
{
|
||||||
|
if (wantOpenCreate) {
|
||||||
|
ImGui::OpenPopup("Neues Element##CreateItemModal");
|
||||||
|
wantOpenCreate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// WICHTIG: Diese SetNext*-Aufrufe OHNE IF davor!
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(520, 0), ImGuiCond_Appearing);
|
||||||
|
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
|
||||||
|
ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||||
|
ImGui::SetNextWindowSizeConstraints(ImVec2(480, 0), ImVec2(620, FLT_MAX));
|
||||||
|
|
||||||
|
// --- POPUP ---
|
||||||
|
if (ImGui::BeginPopupModal("Neues Element##CreateItemModal", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
|
||||||
|
{
|
||||||
|
ImGui::TextUnformatted("Name");
|
||||||
|
ImGui::SetNextItemWidth(-1);
|
||||||
|
ImGui::InputText("##name", gCreateState.nameBuf, IM_ARRAYSIZE(gCreateState.nameBuf));
|
||||||
|
|
||||||
|
ImGui::Dummy(ImVec2(0, 8));
|
||||||
|
|
||||||
|
ImGui::TextUnformatted("Ordner");
|
||||||
|
ImGui::PushItemWidth(-40);
|
||||||
|
ImGui::InputText("##path", gCreateState.pathBuf, IM_ARRAYSIZE(gCreateState.pathBuf));
|
||||||
|
ImGui::PopItemWidth();
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (!gCreateState.pickingFolder) {
|
||||||
|
if (ImGui::Button("...", ImVec2(28, 0))) {
|
||||||
|
const char* startPath = (gCreateState.pathBuf[0] ? gCreateState.pathBuf : nullptr);
|
||||||
|
gCreateState.pickingFolder = true;
|
||||||
|
// Erzwinge neuen Thread: std::launch::async
|
||||||
|
gCreateState.pickFuture = std::async(std::launch::async, [startPath]{
|
||||||
|
return PickFolderBlocking(startPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Während der Auswahl: Button disabled + kleines Status-Label
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::Button("...", ImVec2(28, 0));
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextUnformatted("Öffne Ordner-Dialog...");
|
||||||
|
|
||||||
|
// Poll das Future: sobald fertig, Ergebnis übernehmen
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
if (gCreateState.pickFuture.valid() &&
|
||||||
|
gCreateState.pickFuture.wait_for(0ms) == std::future_status::ready)
|
||||||
|
{
|
||||||
|
if (auto res = gCreateState.pickFuture.get(); res && !res->empty()) {
|
||||||
|
std::snprintf(gCreateState.pathBuf, IM_ARRAYSIZE(gCreateState.pathBuf), "%s", res->c_str());
|
||||||
|
}
|
||||||
|
gCreateState.pickingFolder = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Dummy(ImVec2(0, 10));
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Dummy(ImVec2(0, 2));
|
||||||
|
|
||||||
|
// Buttons rechts
|
||||||
|
float btnW = 80.0f;
|
||||||
|
float totalW = btnW*2 + ImGui::GetStyle().ItemSpacing.x;
|
||||||
|
float avail = ImGui::GetContentRegionAvail().x;
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + avail - totalW);
|
||||||
|
|
||||||
|
if (ImGui::Button("OK", ImVec2(btnW, 0))) {
|
||||||
|
bool valid = gCreateState.nameBuf[0] && gCreateState.pathBuf[0];
|
||||||
|
if (valid) {
|
||||||
|
gCreateState.confirmed = true;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Cancel", ImVec2(btnW, 0))) {
|
||||||
|
gCreateState.confirmed = false;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(gCreateState.confirmed)
|
||||||
|
{
|
||||||
|
gCreateState.confirmed = false;
|
||||||
|
//ProjectManager::NewProject(gCreateState.pathBuf, gCreateState.nameBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::ViewportController::ShowViewsMenu()
|
||||||
|
{
|
||||||
|
for(auto ui : uiElements)
|
||||||
|
{
|
||||||
|
if(ImGui::MenuItem(ui.first.c_str()))
|
||||||
|
{
|
||||||
|
ui.second->SetEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::ViewportController::MenuBar()
|
||||||
|
{
|
||||||
|
ImGui::BeginMainMenuBar();
|
||||||
|
|
||||||
|
if(ImGui::BeginMenu("File"))
|
||||||
|
{
|
||||||
|
ShowFileMenu();
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
if(ImGui::BeginMenu("Views"))
|
||||||
|
{
|
||||||
|
ShowViewsMenu();
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ImGui::EndMainMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::ViewportController::Docking()
|
||||||
|
{
|
||||||
|
ImGuiWindowFlags wndFlags = ImGuiWindowFlags_NoDocking;
|
||||||
|
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||||
|
// ImGui::SetNextWindowPos(viewport->WorkPos);
|
||||||
|
// ImGui::SetNextWindowSize(viewport->WorkSize);
|
||||||
|
// ImGui::SetNextWindowViewport(viewport->ID);
|
||||||
|
// ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||||
|
// ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||||
|
// wndFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
|
||||||
|
// wndFlags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
|
||||||
|
// ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {0,0});
|
||||||
|
// ImGui::Begin("DockSpace", &dockSpaceOpen, wndFlags);
|
||||||
|
// ImGui::PopStyleVar(3);
|
||||||
|
uint dockspaceID = ImGui::GetID("MyDockSpace");
|
||||||
|
ImGui::DockSpaceOverViewport(dockspaceID, viewport);
|
||||||
|
// ImGui::DockSpace(dockspaceID, {0,0});
|
||||||
|
// ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::ViewportController::Update()
|
||||||
|
{
|
||||||
|
MenuBar();
|
||||||
|
NewProjectPopup();
|
||||||
|
Docking();
|
||||||
|
for(auto ui : uiElements)
|
||||||
|
{
|
||||||
|
ui.second->Render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::ViewportController::AddGuiElement(const std::string &name, IGUIElement *element)
|
||||||
|
{
|
||||||
|
uiElements[name] = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSE::EDITOR::IGUIElement *TSE::EDITOR::ViewportController::GetGuiElement(const std::string &name)
|
||||||
|
{
|
||||||
|
return uiElements.at(name);
|
||||||
|
}
|
||||||
41
TSE_Editor/src/UI/ViewportController.hpp
Normal file
41
TSE_Editor/src/UI/ViewportController.hpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "UI/base/IGUIElement.hpp"
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "Types.hpp"
|
||||||
|
#include <future>
|
||||||
|
#include <atomic>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
struct CreateItemState
|
||||||
|
{
|
||||||
|
bool confirmed = false;
|
||||||
|
char nameBuf[256] = "";
|
||||||
|
char pathBuf[1024] = "";
|
||||||
|
|
||||||
|
std::atomic<bool> pickingFolder{false};
|
||||||
|
std::future<std::optional<string>> pickFuture;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ViewportController
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool dockSpaceOpen = true;
|
||||||
|
std::unordered_map<string, IGUIElement*> uiElements;
|
||||||
|
CreateItemState gCreateState;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ViewportController();
|
||||||
|
|
||||||
|
void ShowFileMenu();
|
||||||
|
void NewProjectPopup();
|
||||||
|
void ShowViewsMenu();
|
||||||
|
void MenuBar();
|
||||||
|
void Docking();
|
||||||
|
void Update();
|
||||||
|
void AddGuiElement(const std::string& name, IGUIElement* element);
|
||||||
|
IGUIElement* GetGuiElement(const std::string& name);
|
||||||
|
};
|
||||||
|
} // namespace TSE::Editor
|
||||||
35
TSE_Editor/src/UI/base/GuiWindow.cpp
Normal file
35
TSE_Editor/src/UI/base/GuiWindow.cpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#include "GuiWindow.h"
|
||||||
|
|
||||||
|
TSE::EDITOR::GuiWindow::GuiWindow(const string &title, const ImGuiWindowFlags &flags)
|
||||||
|
{
|
||||||
|
this->title = title;
|
||||||
|
this->flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSE::EDITOR::GuiWindow::GuiWindow(const string &title, const Vector2 pos, const Vector2 size, const ImGuiWindowFlags &flags)
|
||||||
|
{
|
||||||
|
this->title = title;
|
||||||
|
this->pos = pos;
|
||||||
|
this->size = size;
|
||||||
|
this->flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::GuiWindow::Render()
|
||||||
|
{
|
||||||
|
if(enabled)
|
||||||
|
{
|
||||||
|
Begin();
|
||||||
|
Define();
|
||||||
|
End();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::GuiWindow::Begin()
|
||||||
|
{
|
||||||
|
ImGui::Begin(title.c_str(), &enabled, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::GuiWindow::End()
|
||||||
|
{
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
27
TSE_Editor/src/UI/base/GuiWindow.h
Normal file
27
TSE_Editor/src/UI/base/GuiWindow.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IGUIElement.hpp"
|
||||||
|
#include "Types.hpp"
|
||||||
|
#include "Vector2.hpp"
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
class GuiWindow : public IGUIElement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Vector2 pos = Vector2::zero;
|
||||||
|
Vector2 size = Vector2(100,100);
|
||||||
|
ImGuiWindowFlags flags = ImGuiWindowFlags_None;
|
||||||
|
string title = "Title";
|
||||||
|
|
||||||
|
GuiWindow(const string& title, const ImGuiWindowFlags& flags = ImGuiWindowFlags_None);
|
||||||
|
GuiWindow(const string& title, const Vector2 pos, const Vector2 size, const ImGuiWindowFlags& flags = ImGuiWindowFlags_None);
|
||||||
|
void Render() override;
|
||||||
|
virtual void Define() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Begin();
|
||||||
|
void End();
|
||||||
|
};
|
||||||
|
} // namespace TSE::EDITOR
|
||||||
16
TSE_Editor/src/UI/base/IGUIElement.hpp
Normal file
16
TSE_Editor/src/UI/base/IGUIElement.hpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
class IGUIElement
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
bool enabled = true;
|
||||||
|
public:
|
||||||
|
virtual void Render() = 0;
|
||||||
|
inline void SetEnabled(bool state)
|
||||||
|
{
|
||||||
|
enabled = state;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} // namespace TSE::EDITOR
|
||||||
30
TSE_Editor/src/UI/windows/CameraView.cpp
Normal file
30
TSE_Editor/src/UI/windows/CameraView.cpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#include "CameraView.hpp"
|
||||||
|
|
||||||
|
TSE::EDITOR::CameraView::CameraView() : GuiWindow("Camera", ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar)
|
||||||
|
{
|
||||||
|
fb = IRenderTexture::factory->CreateTextureHeap({100,100});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::CameraView::Define()
|
||||||
|
{
|
||||||
|
if(currentCamera != Camera::mainCamera)
|
||||||
|
{
|
||||||
|
if(currentCamera != nullptr)
|
||||||
|
currentCamera->SetRenderTarget(nullptr);
|
||||||
|
currentCamera = Camera::mainCamera;
|
||||||
|
if(currentCamera != nullptr)
|
||||||
|
currentCamera->SetRenderTarget(fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiWindowFlags flags2 = ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar;
|
||||||
|
if(ImGui::BeginChild("##SceneChild", {0,0}, ImGuiChildFlags_None, flags2))
|
||||||
|
{
|
||||||
|
ImGui::Image(fb->GetTextureId(), {fb->Width(), fb->Height()},{0,1}, {1,0});
|
||||||
|
auto vec2 = ImGui::GetWindowSize();
|
||||||
|
if(fb->Width() != vec2.x || fb->Height() != vec2.y)
|
||||||
|
{
|
||||||
|
fb->SetSize({vec2.x, vec2.y});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
18
TSE_Editor/src/UI/windows/CameraView.hpp
Normal file
18
TSE_Editor/src/UI/windows/CameraView.hpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "UI/base/GuiWindow.h"
|
||||||
|
#include "interfaces/IRenderTexture.hpp"
|
||||||
|
#include "BehaviourScripts/Camera.hpp"
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
class CameraView : public GuiWindow
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Camera* currentCamera = nullptr;
|
||||||
|
TSE::IRenderTexture* fb;
|
||||||
|
public:
|
||||||
|
CameraView();
|
||||||
|
void Define() override;
|
||||||
|
};
|
||||||
|
} // namespace TSE::EDITOR
|
||||||
117
TSE_Editor/src/UI/windows/ConsolView.cpp
Normal file
117
TSE_Editor/src/UI/windows/ConsolView.cpp
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#include "ConsolView.hpp"
|
||||||
|
|
||||||
|
TSE::EDITOR::ConsolView::ConsolView() : GuiWindow("Console", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoCollapse)
|
||||||
|
{
|
||||||
|
Debug::AddCallback(CreateLogEntry);
|
||||||
|
textbuffer[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
TSE::EDITOR::ConsolView::~ConsolView()
|
||||||
|
{
|
||||||
|
delete[] textbuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::ConsolView::MenuBar()
|
||||||
|
{
|
||||||
|
if(ImGui::BeginMenuBar())
|
||||||
|
{
|
||||||
|
if(ImGui::MenuItem("Clear"))
|
||||||
|
{
|
||||||
|
entries.clear();
|
||||||
|
}
|
||||||
|
bool style = logs;
|
||||||
|
if(style) ImGui::PushStyleColor(ImGuiCol_Text, {0,1,0,1});
|
||||||
|
if(ImGui::MenuItem("Logs")) logs = !logs;
|
||||||
|
if(style) ImGui::PopStyleColor();
|
||||||
|
style = warnings;
|
||||||
|
if(style) ImGui::PushStyleColor(ImGuiCol_Text, {0,1,0,1});
|
||||||
|
if(ImGui::MenuItem("Warning")) warnings = !warnings;
|
||||||
|
if(style) ImGui::PopStyleColor();
|
||||||
|
style = errors;
|
||||||
|
if(style) ImGui::PushStyleColor(ImGuiCol_Text, {0,1,0,1});
|
||||||
|
if(ImGui::MenuItem("Errors")) errors = !errors;
|
||||||
|
if(style) ImGui::PopStyleColor();
|
||||||
|
style = autoScroll;
|
||||||
|
if(style) ImGui::PushStyleColor(ImGuiCol_Text, {0,1,0,1});
|
||||||
|
if(ImGui::MenuItem("Auto Scroll")) autoScroll = !autoScroll;
|
||||||
|
if(style) ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
ImGui::EndMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::ConsolView::LogList()
|
||||||
|
{
|
||||||
|
float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
|
||||||
|
if(ImGui::BeginChild("ScrollingRegion", {0, -footer_height_to_reserve}, ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_HorizontalScrollbar))
|
||||||
|
{
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {4, 1});
|
||||||
|
for(auto entry : entries)
|
||||||
|
{
|
||||||
|
switch (entry.type)
|
||||||
|
{
|
||||||
|
case LogEntryType::Warning:
|
||||||
|
if (warnings) continue;
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, {1,1,0,1});
|
||||||
|
ImGui::TextUnformatted(entry.CreateTimedMSG().c_str());
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
break;
|
||||||
|
case LogEntryType::Error:
|
||||||
|
if (errors) continue;
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, {1,0,0,1});
|
||||||
|
ImGui::TextUnformatted(entry.CreateTimedMSG().c_str());
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
break;
|
||||||
|
case LogEntryType::Log:
|
||||||
|
if (logs) continue;
|
||||||
|
ImGui::TextUnformatted(entry.CreateTimedMSG().c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
|
||||||
|
ImGui::SetScrollHereY(1.0f);
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
ImGui::Separator();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Callback(ImGuiInputTextCallbackData* data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::ConsolView::Input()
|
||||||
|
{
|
||||||
|
ImGuiInputTextFlags _flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
|
||||||
|
|
||||||
|
if(ImGui::InputText("<-- Command", textbuffer, 512, _flags, Callback))
|
||||||
|
{
|
||||||
|
HandleInput(std::string(textbuffer));
|
||||||
|
textbuffer[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::ConsolView::Define()
|
||||||
|
{
|
||||||
|
MenuBar();
|
||||||
|
LogList();
|
||||||
|
Input();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::ConsolView::HandleInput(const string &input)
|
||||||
|
{
|
||||||
|
LuaStateHandler::RunLuaString(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::ConsolView::CreateLogEntry(const string &msg, const LogEntryType &t)
|
||||||
|
{
|
||||||
|
if(entries.size() > 0 && entries[entries.size() - 1].msg == msg)
|
||||||
|
{
|
||||||
|
entries[entries.size() - 1].count += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LogEntry entry(msg, t);
|
||||||
|
entries.push_back(entry);
|
||||||
|
}
|
||||||
65
TSE_Editor/src/UI/windows/ConsolView.hpp
Normal file
65
TSE_Editor/src/UI/windows/ConsolView.hpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "UI/base/GuiWindow.h"
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include "Types.hpp"
|
||||||
|
#include <chrono>
|
||||||
|
#include "Debug.hpp"
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
struct LogEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LogEntryType type;
|
||||||
|
string msg;
|
||||||
|
std::chrono::hh_mm_ss<std::chrono::milliseconds> time;
|
||||||
|
int count = 1;
|
||||||
|
|
||||||
|
inline LogEntry(const string& message, const LogEntryType& t)
|
||||||
|
{
|
||||||
|
msg = message;
|
||||||
|
type = t;
|
||||||
|
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
|
||||||
|
auto today = std::chrono::floor<std::chrono::days>(now);
|
||||||
|
auto time_since_midnight = now - today;
|
||||||
|
|
||||||
|
time = std::chrono::hh_mm_ss<std::chrono::milliseconds>(std::chrono::floor<std::chrono::milliseconds>(time_since_midnight));
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
inline string CreateTimedMSG()
|
||||||
|
{
|
||||||
|
string res = std::to_string(time.hours().count()) + ":" + std::to_string(time.minutes().count()) + ":" + std::to_string(time.seconds().count());
|
||||||
|
res += " -> " + msg;
|
||||||
|
if(count > 1)
|
||||||
|
{
|
||||||
|
res += " (" + std::to_string(count) + ")";
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConsolView : public GuiWindow
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static inline std::vector<LogEntry> entries = {};
|
||||||
|
|
||||||
|
bool logs = false, warnings = false, errors = false, autoScroll = true;
|
||||||
|
char* textbuffer = new char[512];
|
||||||
|
public:
|
||||||
|
static std::vector<string> arguments;
|
||||||
|
|
||||||
|
ConsolView();
|
||||||
|
~ConsolView();
|
||||||
|
void MenuBar();
|
||||||
|
void LogList();
|
||||||
|
void Input();
|
||||||
|
void Define() override;
|
||||||
|
void HandleInput(const string& input);
|
||||||
|
static void CreateLogEntry(const string& msg, const LogEntryType& t);
|
||||||
|
};
|
||||||
|
} // namespace TSE::EDITOR
|
||||||
24
TSE_Editor/src/UI/windows/DebugView.cpp
Normal file
24
TSE_Editor/src/UI/windows/DebugView.cpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include "DebugView.hpp"
|
||||||
|
#include "utils/Time.hpp"
|
||||||
|
#include "elements/Transformable.hpp"
|
||||||
|
|
||||||
|
TSE::EDITOR::DebugView::DebugView() : GuiWindow("Debug", ImGuiWindowFlags_NoCollapse) { }
|
||||||
|
|
||||||
|
void TSE::EDITOR::DebugView::Define()
|
||||||
|
{
|
||||||
|
lasttime += Time::deltaTime();
|
||||||
|
if(lasttime >= 1)
|
||||||
|
{
|
||||||
|
lasttime = 0;
|
||||||
|
debugFps = std::to_string(frameCount) + " FPS";
|
||||||
|
|
||||||
|
frameCount = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frameCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text(debugFps.c_str());
|
||||||
|
ImGui::Text(("Object count: " + std::to_string(Transformable::GetTansformableCount())).c_str());
|
||||||
|
}
|
||||||
23
TSE_Editor/src/UI/windows/DebugView.hpp
Normal file
23
TSE_Editor/src/UI/windows/DebugView.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "UI/base/GuiWindow.h"
|
||||||
|
#include "Types.hpp"
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
class DebugView : public GuiWindow
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
string debugFps = "0 FPS";
|
||||||
|
float lasttime = 0;
|
||||||
|
int frameCount = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DebugView();
|
||||||
|
|
||||||
|
~DebugView() = default;
|
||||||
|
|
||||||
|
void Define() override;
|
||||||
|
};
|
||||||
|
} // namespace TSE::EDITOR
|
||||||
392
TSE_Editor/src/UI/windows/HirearchieView.cpp
Normal file
392
TSE_Editor/src/UI/windows/HirearchieView.cpp
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
#include "HirearchieView.hpp"
|
||||||
|
#include "PropertiesView.hpp"
|
||||||
|
|
||||||
|
TSE::EDITOR::HirearchieView::HirearchieView(Scene *s) : GuiWindow("Hirearchie", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoCollapse)
|
||||||
|
{
|
||||||
|
SetScene(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::SetScene(Scene *s)
|
||||||
|
{
|
||||||
|
currentScene = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::Define()
|
||||||
|
{
|
||||||
|
if(currentScene == nullptr) return;
|
||||||
|
MenuBar();
|
||||||
|
|
||||||
|
bool collapseOpen = ImGui::CollapsingHeader(currentScene->GetName().c_str(), ImGuiTreeNodeFlags_DefaultOpen);
|
||||||
|
if(ImGui::BeginPopupContextItem())
|
||||||
|
{
|
||||||
|
if(ImGui::MenuItem("Rename"))
|
||||||
|
{
|
||||||
|
strcpy(inputBuffer, currentScene->GetName().c_str());
|
||||||
|
activatePopUpNamingLayer = true;
|
||||||
|
inputPurpose = RenamingScene;
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
AddMenu(nullptr, nullptr);
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
if(collapseOpen)
|
||||||
|
{
|
||||||
|
int layerCount = currentScene->GetLayerCount();
|
||||||
|
for (int i = 0; i < layerCount; i++)
|
||||||
|
{
|
||||||
|
auto layer = currentScene->GetLayerAt(i);
|
||||||
|
//if(layer->GetName() != ".editor")
|
||||||
|
DisplayLayer(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(activatePopUpNamingLayer)
|
||||||
|
{
|
||||||
|
activatePopUpNamingLayer = false;
|
||||||
|
openPopUpNamingLayer = true;
|
||||||
|
ImGui::OpenPopup("Enter Text");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openPopUpNamingLayer) {
|
||||||
|
if (ImGui::BeginPopupModal("Enter Text", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
ImGui::Text("Please enter a Name:");
|
||||||
|
|
||||||
|
ImGui::InputText("##input", inputBuffer, IM_ARRAYSIZE(inputBuffer));
|
||||||
|
|
||||||
|
bool inputValid = strlen(inputBuffer) > 0;
|
||||||
|
|
||||||
|
if (!inputValid) {
|
||||||
|
ImGui::TextColored(ImVec4(1,0,0,1), "Not Valid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("OK", ImVec2(120, 0))) {
|
||||||
|
if (inputValid) {
|
||||||
|
inputConfirmed = true;
|
||||||
|
openPopUpNamingLayer = false;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Cancel", ImVec2(120, 0))) {
|
||||||
|
openPopUpNamingLayer = false;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inputConfirmed) {
|
||||||
|
switch (inputPurpose)
|
||||||
|
{
|
||||||
|
case LayerCreation:
|
||||||
|
CreateLayer();
|
||||||
|
break;
|
||||||
|
case RenamingLayer:
|
||||||
|
currentScene->RenameLayer(tmpHolder2->GetName(), inputBuffer);
|
||||||
|
tmpHolder2 = nullptr;
|
||||||
|
break;
|
||||||
|
case RenamingScene:
|
||||||
|
currentScene->SetName(inputBuffer);
|
||||||
|
break;
|
||||||
|
case RenamingTransformable:
|
||||||
|
tmpHolder1->name = inputBuffer;
|
||||||
|
tmpHolder1 = nullptr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
inputConfirmed = false;
|
||||||
|
inputPurpose = Nothing;
|
||||||
|
strcpy(inputBuffer, ""); // Reset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::MenuBar()
|
||||||
|
{
|
||||||
|
if(ImGui::BeginMenuBar())
|
||||||
|
{
|
||||||
|
if(ImGui::BeginMenu("Add"))
|
||||||
|
{
|
||||||
|
if(ImGui::MenuItem("Empty"))
|
||||||
|
{
|
||||||
|
CreateEmpty(currentScene->GetLayerAt(0));
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
if(ImGui::MenuItem("Layer"))
|
||||||
|
{
|
||||||
|
activatePopUpNamingLayer = true;
|
||||||
|
inputPurpose = LayerCreation;
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::DisplayLayer(Layer *l)
|
||||||
|
{
|
||||||
|
ImGui::Indent(20.0f);
|
||||||
|
bool collapseOpen = ImGui::CollapsingHeader(l->GetName().c_str(), ImGuiTreeNodeFlags_DefaultOpen);
|
||||||
|
if(ImGui::BeginPopupContextItem())
|
||||||
|
{
|
||||||
|
bool disabled = currentScene->GetLayerCount() <= 1;
|
||||||
|
if (disabled)
|
||||||
|
{
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
||||||
|
}
|
||||||
|
if(ImGui::MenuItem("Move Up") && !disabled)
|
||||||
|
{
|
||||||
|
MoveUpLayer(l);
|
||||||
|
}
|
||||||
|
if(ImGui::MenuItem("Move Down") && !disabled)
|
||||||
|
{
|
||||||
|
MoveDownLayer(l);
|
||||||
|
}
|
||||||
|
if (disabled)
|
||||||
|
{
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
}
|
||||||
|
if(ImGui::MenuItem("Rename"))
|
||||||
|
{
|
||||||
|
strcpy(inputBuffer, l->GetName().c_str());
|
||||||
|
activatePopUpNamingLayer = true;
|
||||||
|
inputPurpose = RenamingLayer;
|
||||||
|
tmpHolder2 = l;
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
AddMenu(nullptr, l);
|
||||||
|
ImGui::Separator();
|
||||||
|
if(ImGui::MenuItem("Delete"))
|
||||||
|
{
|
||||||
|
currentScene->RemoveLayer(l->GetName());
|
||||||
|
//property display remove if present
|
||||||
|
|
||||||
|
//exit early:
|
||||||
|
ImGui::EndPopup();
|
||||||
|
ImGui::Unindent(20.0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
if(ImGui::BeginDragDropTarget())
|
||||||
|
{
|
||||||
|
auto payload = ImGui::AcceptDragDropPayload("OBJ_TRANS_PTR");
|
||||||
|
if(payload != nullptr)
|
||||||
|
{
|
||||||
|
auto t2 = *(Transformable**)payload->Data;
|
||||||
|
HandleDropping(l, t2);
|
||||||
|
}
|
||||||
|
ImGui::EndDragDropTarget();
|
||||||
|
}
|
||||||
|
if(collapseOpen)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < l->GetAllObjects().size(); i++)
|
||||||
|
{
|
||||||
|
DisplayObj(l->GetAllObjects()[i], l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::Unindent(20.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::DisplayObj(Transformable *t, Layer *l)
|
||||||
|
{
|
||||||
|
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanFullWidth;
|
||||||
|
if(t->GetChildren().size() == 0)
|
||||||
|
{
|
||||||
|
flags |= ImGuiTreeNodeFlags_Leaf;
|
||||||
|
}
|
||||||
|
if(selected == t->id)
|
||||||
|
{
|
||||||
|
flags |= ImGuiTreeNodeFlags_Selected;
|
||||||
|
}
|
||||||
|
bool open = ImGui::TreeNodeEx((t->GetName() + "##" + to_string(t->id)).c_str(), flags);
|
||||||
|
if(ImGui::BeginPopupContextItem())
|
||||||
|
{
|
||||||
|
bool disabled = false;
|
||||||
|
if(t->GetParent() != nullptr)
|
||||||
|
disabled = t->GetParent()->GetChildren().size() < 1;
|
||||||
|
else
|
||||||
|
disabled = l->GetAllObjects().size() <= 1;
|
||||||
|
if (disabled)
|
||||||
|
{
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
||||||
|
}
|
||||||
|
if(ImGui::MenuItem("Move Up") && !disabled)
|
||||||
|
{
|
||||||
|
t->MoveUp(l);
|
||||||
|
}
|
||||||
|
if(ImGui::MenuItem("Move Down") && !disabled)
|
||||||
|
{
|
||||||
|
t->MoveDown(l);
|
||||||
|
}
|
||||||
|
if (disabled)
|
||||||
|
{
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
}
|
||||||
|
if(ImGui::MenuItem("Rename"))
|
||||||
|
{
|
||||||
|
strcpy(inputBuffer, t->name.c_str());
|
||||||
|
activatePopUpNamingLayer = true;
|
||||||
|
inputPurpose = RenamingTransformable;
|
||||||
|
tmpHolder1 = t;
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
AddMenu(t, nullptr);
|
||||||
|
ImGui::Separator();
|
||||||
|
if(ImGui::MenuItem("Delete"))
|
||||||
|
{
|
||||||
|
t->SetParent(nullptr);
|
||||||
|
int layerCount = currentScene->GetLayerCount();
|
||||||
|
for (int i = 0; i < layerCount; i++)
|
||||||
|
{
|
||||||
|
auto layer = currentScene->GetLayerAt(i);
|
||||||
|
if(layer->HasTransformable(t))
|
||||||
|
{
|
||||||
|
layer->RemoveTransformable(t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Transformable::HardDelete(t->id);
|
||||||
|
PropertiesView::ForceClearInspectorElement();
|
||||||
|
|
||||||
|
//exit early:
|
||||||
|
ImGui::EndPopup();
|
||||||
|
if(open)
|
||||||
|
{
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
if(ImGui::BeginDragDropSource())
|
||||||
|
{
|
||||||
|
auto ptr = &t;
|
||||||
|
ImGui::SetDragDropPayload("OBJ_TRANS_PTR", ptr, sizeof(ptr));
|
||||||
|
std::string name = t->GetName();
|
||||||
|
ImGui::Text((std::string("Move \"") + t->GetName() + "\"").c_str());
|
||||||
|
ImGui::EndDragDropSource();
|
||||||
|
}
|
||||||
|
if(ImGui::BeginDragDropTarget())
|
||||||
|
{
|
||||||
|
auto payload = ImGui::AcceptDragDropPayload("OBJ_TRANS_PTR");
|
||||||
|
if(payload != nullptr)
|
||||||
|
{
|
||||||
|
auto t2 = *(Transformable**)payload->Data;
|
||||||
|
HandleDropping(t, t2);
|
||||||
|
}
|
||||||
|
ImGui::EndDragDropTarget();
|
||||||
|
}
|
||||||
|
if(ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
|
||||||
|
{
|
||||||
|
selected = t->id;
|
||||||
|
PropertiesView::SetInspectorElement(InspectableType::Transformable, t);
|
||||||
|
}
|
||||||
|
if(open)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < t->GetChildren().size(); i++)
|
||||||
|
{
|
||||||
|
DisplayObj(t->GetChildren()[i], nullptr);
|
||||||
|
}
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::HandleDropping(Layer *target, Transformable *payload)
|
||||||
|
{
|
||||||
|
payload->SetParent(nullptr);
|
||||||
|
int layerCount = currentScene->GetLayerCount();
|
||||||
|
for (int i = 0; i < layerCount; i++)
|
||||||
|
{
|
||||||
|
auto layer = currentScene->GetLayerAt(i);
|
||||||
|
if(layer == target && layer->HasTransformable(payload)) return;
|
||||||
|
if(layer->HasTransformable(payload))
|
||||||
|
{
|
||||||
|
layer->RemoveTransformable(payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target->AddTransformable(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::HandleDropping(Transformable *target, Transformable *payload)
|
||||||
|
{
|
||||||
|
if(target->id == payload->id) return;
|
||||||
|
if(payload->IsMyChild(target)) return;
|
||||||
|
int layerCount = currentScene->GetLayerCount();
|
||||||
|
for (int i = 0; i < layerCount; i++)
|
||||||
|
{
|
||||||
|
auto layer = currentScene->GetLayerAt(i);
|
||||||
|
if(layer->HasTransformable(payload))
|
||||||
|
{
|
||||||
|
layer->RemoveTransformable(payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payload->SetParent(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::AddMenu(Transformable *t, Layer *l)
|
||||||
|
{
|
||||||
|
if(t== nullptr && l == nullptr) //Scene
|
||||||
|
{
|
||||||
|
if(ImGui::MenuItem("Add Layer"))
|
||||||
|
{
|
||||||
|
activatePopUpNamingLayer = true;
|
||||||
|
inputPurpose = LayerCreation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //Layer and Transformable
|
||||||
|
{
|
||||||
|
if(ImGui::MenuItem("Add Empty"))
|
||||||
|
{
|
||||||
|
if(t == nullptr)
|
||||||
|
CreateEmpty(l);
|
||||||
|
else
|
||||||
|
CreateEmpty(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::MoveUpLayer(Layer *t)
|
||||||
|
{
|
||||||
|
if(currentScene->GetLayerAt(0) == t) return;
|
||||||
|
currentScene->MoveLayerUp(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::MoveDownLayer(Layer *t)
|
||||||
|
{
|
||||||
|
int lc = currentScene->GetLayerCount();
|
||||||
|
if(currentScene->GetLayerAt(lc - 1) == t) return;
|
||||||
|
currentScene->MoveLayerDown(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::CreateEmpty(Transformable *parent)
|
||||||
|
{
|
||||||
|
Transformable* t = new Transformable("Unnamed Object");
|
||||||
|
if(parent != nullptr)
|
||||||
|
{
|
||||||
|
t->SetParent(parent);
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// // TODO: need to chose a layer to put it in. I would suggest the first one?
|
||||||
|
// currentScene->GetLayerAt(0)->AddTransformable(t);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::CreateEmpty(Layer *parent)
|
||||||
|
{
|
||||||
|
Transformable* t = new Transformable("Unnamed Object");
|
||||||
|
if(parent != nullptr)
|
||||||
|
{
|
||||||
|
parent->AddTransformable(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::HirearchieView::CreateLayer()
|
||||||
|
{
|
||||||
|
Layer* l = new Layer(inputBuffer);
|
||||||
|
currentScene->AddLayer(l);
|
||||||
|
}
|
||||||
47
TSE_Editor/src/UI/windows/HirearchieView.hpp
Normal file
47
TSE_Editor/src/UI/windows/HirearchieView.hpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "UI/base/GuiWindow.h"
|
||||||
|
#include "uuid.h"
|
||||||
|
#include "elements/Scene.hpp"
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
class HirearchieView : public GuiWindow
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
enum NamingPopUpPorPuse
|
||||||
|
{
|
||||||
|
Nothing = 0,
|
||||||
|
LayerCreation = 1,
|
||||||
|
RenamingLayer = 2,
|
||||||
|
RenamingTransformable = 3,
|
||||||
|
RenamingScene = 4
|
||||||
|
};
|
||||||
|
Scene* currentScene;
|
||||||
|
uuids::uuid selected = uuids::uuid();
|
||||||
|
|
||||||
|
bool openPopUpNamingLayer = false;
|
||||||
|
bool activatePopUpNamingLayer = false;
|
||||||
|
char inputBuffer[128] = "";
|
||||||
|
bool inputConfirmed = false;
|
||||||
|
NamingPopUpPorPuse inputPurpose = Nothing;
|
||||||
|
Transformable* tmpHolder1 = nullptr;
|
||||||
|
Layer* tmpHolder2 = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HirearchieView(Scene* s);
|
||||||
|
void SetScene(Scene* s);
|
||||||
|
void Define() override;
|
||||||
|
void MenuBar();
|
||||||
|
void DisplayLayer(Layer* l);
|
||||||
|
void DisplayObj(Transformable* t, Layer* l);
|
||||||
|
void HandleDropping(Layer* target, Transformable* payload);
|
||||||
|
void HandleDropping(Transformable* target, Transformable* payload);
|
||||||
|
void AddMenu(Transformable* t, Layer* l);
|
||||||
|
void MoveUpLayer(Layer* t);
|
||||||
|
void MoveDownLayer(Layer* t);
|
||||||
|
void CreateEmpty(Transformable* parent);
|
||||||
|
void CreateEmpty(Layer* parent);
|
||||||
|
void CreateLayer();
|
||||||
|
};
|
||||||
|
} // namespace TSE::EDITOR
|
||||||
48
TSE_Editor/src/UI/windows/PropertiesView.cpp
Normal file
48
TSE_Editor/src/UI/windows/PropertiesView.cpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include "PropertiesView.hpp"
|
||||||
|
|
||||||
|
TSE::EDITOR::PropertiesView::PropertiesView() : GuiWindow("Properies", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoCollapse) { }
|
||||||
|
|
||||||
|
void TSE::EDITOR::PropertiesView::Define()
|
||||||
|
{
|
||||||
|
MenuBar();
|
||||||
|
if(currentlyInspecting.type == InspectableType::None) return;
|
||||||
|
|
||||||
|
ElementDrawer::Draw(currentlyInspecting, debugMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::PropertiesView::MenuBar()
|
||||||
|
{
|
||||||
|
if(ImGui::BeginMenuBar())
|
||||||
|
{
|
||||||
|
if(ImGui::BeginMenu("..."))
|
||||||
|
{
|
||||||
|
if(ImGui::MenuItem("Debug"))
|
||||||
|
{
|
||||||
|
debugMode = !debugMode;
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
bool style = locked;
|
||||||
|
if(style) ImGui::PushStyleColor(ImGuiCol_Text, {0,1,0,1});
|
||||||
|
if(ImGui::MenuItem("Lock")) locked = !locked;
|
||||||
|
if(style) ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
ImGui::EndMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::PropertiesView::SetInspectorElement(InspectableType type, void *element)
|
||||||
|
{
|
||||||
|
if(!locked)
|
||||||
|
currentlyInspecting = Inspectable(type, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::PropertiesView::ClearInspectorElement()
|
||||||
|
{
|
||||||
|
if(!locked)
|
||||||
|
currentlyInspecting = Inspectable(InspectableType::None, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::PropertiesView::ForceClearInspectorElement()
|
||||||
|
{
|
||||||
|
currentlyInspecting = Inspectable(InspectableType::None, nullptr);
|
||||||
|
}
|
||||||
24
TSE_Editor/src/UI/windows/PropertiesView.hpp
Normal file
24
TSE_Editor/src/UI/windows/PropertiesView.hpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "UI/base/GuiWindow.h"
|
||||||
|
#include "UI/ElementDrawer.hpp"
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
class PropertiesView : public GuiWindow
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
inline static Inspectable currentlyInspecting = {};
|
||||||
|
bool debugMode = false;
|
||||||
|
inline static bool locked = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PropertiesView();
|
||||||
|
|
||||||
|
void Define() override;
|
||||||
|
void MenuBar();
|
||||||
|
static void SetInspectorElement(InspectableType type, void* element);
|
||||||
|
static void ClearInspectorElement();
|
||||||
|
static void ForceClearInspectorElement();
|
||||||
|
};
|
||||||
|
} // namespace TSE::EDITOR
|
||||||
21
TSE_Editor/src/UI/windows/SceneView.cpp
Normal file
21
TSE_Editor/src/UI/windows/SceneView.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include "SceneView.hpp"
|
||||||
|
|
||||||
|
TSE::EDITOR::SceneView::SceneView(TSE::IRenderTexture *frameBuffer) : GuiWindow("Scene", ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar)
|
||||||
|
{
|
||||||
|
fb = frameBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSE::EDITOR::SceneView::Define()
|
||||||
|
{
|
||||||
|
ImGuiWindowFlags flags2 = ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar;
|
||||||
|
if(ImGui::BeginChild("##SceneChild", {0,0}, ImGuiChildFlags_None, flags2))
|
||||||
|
{
|
||||||
|
ImGui::Image(fb->GetTextureId(), {fb->Width(), fb->Height()},{0,1}, {1,0});
|
||||||
|
auto vec2 = ImGui::GetWindowSize();
|
||||||
|
if(fb->Width() != vec2.x || fb->Height() != vec2.y)
|
||||||
|
{
|
||||||
|
fb->SetSize({vec2.x, vec2.y});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
16
TSE_Editor/src/UI/windows/SceneView.hpp
Normal file
16
TSE_Editor/src/UI/windows/SceneView.hpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "UI/base/GuiWindow.h"
|
||||||
|
#include "interfaces/IRenderTexture.hpp"
|
||||||
|
|
||||||
|
namespace TSE::EDITOR
|
||||||
|
{
|
||||||
|
class SceneView : public GuiWindow
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
TSE::IRenderTexture* fb;
|
||||||
|
public:
|
||||||
|
SceneView(TSE::IRenderTexture* frameBuffer);
|
||||||
|
void Define() override;
|
||||||
|
};
|
||||||
|
} // namespace TSE::EDITOR
|
||||||
@@ -10,6 +10,9 @@
|
|||||||
#include "PathHelper.hpp"
|
#include "PathHelper.hpp"
|
||||||
#include "elements/Texture.hpp"
|
#include "elements/Texture.hpp"
|
||||||
#include "TextureHelperOpenGL.hpp"
|
#include "TextureHelperOpenGL.hpp"
|
||||||
|
#include "interfaces/IRenderer.hpp"
|
||||||
|
#include "BehaviourScripts/Camera.hpp"
|
||||||
|
#include "RenderTextureCreatorOpenGL.hpp"
|
||||||
|
|
||||||
TSE::GLFW::OpenGLRenderingBackend::OpenGLRenderingBackend(Color _backgroundColor, bool _vsync)
|
TSE::GLFW::OpenGLRenderingBackend::OpenGLRenderingBackend(Color _backgroundColor, bool _vsync)
|
||||||
: OpenGLRenderingBackend(_backgroundColor, _vsync, 0, false){ }
|
: OpenGLRenderingBackend(_backgroundColor, _vsync, 0, false){ }
|
||||||
@@ -34,6 +37,7 @@ TSE::GLFW::OpenGLRenderingBackend::~OpenGLRenderingBackend()
|
|||||||
|
|
||||||
void TSE::GLFW::OpenGLRenderingBackend::InitPreWindow()
|
void TSE::GLFW::OpenGLRenderingBackend::InitPreWindow()
|
||||||
{
|
{
|
||||||
|
IRenderTexture::factory = new RenderTextureCreatorOpenGL();
|
||||||
Texture::helper = new TextureHelperOpenGL();
|
Texture::helper = new TextureHelperOpenGL();
|
||||||
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, TSE_OPENGL_VERSION_MAJOR);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, TSE_OPENGL_VERSION_MAJOR);
|
||||||
@@ -140,7 +144,13 @@ void TSE::GLFW::OpenGLRenderingBackend::onUpdate() const
|
|||||||
|
|
||||||
void TSE::GLFW::OpenGLRenderingBackend::onClear() const
|
void TSE::GLFW::OpenGLRenderingBackend::onClear() const
|
||||||
{
|
{
|
||||||
//cameras
|
for (int i = 0; i < IRenderer::camerasToRenderWith.size(); i++)
|
||||||
|
{
|
||||||
|
IRenderer::camerasToRenderWith[i]->Bind();
|
||||||
|
IRenderer::camerasToRenderWith[i]->UpdateRenderTarget();
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
IRenderer::camerasToRenderWith[i]->Unbind();
|
||||||
|
}
|
||||||
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
if(useseImGui)
|
if(useseImGui)
|
||||||
@@ -153,7 +163,12 @@ void TSE::GLFW::OpenGLRenderingBackend::onClear() const
|
|||||||
|
|
||||||
void TSE::GLFW::OpenGLRenderingBackend::onClearDepthBuffer() const
|
void TSE::GLFW::OpenGLRenderingBackend::onClearDepthBuffer() const
|
||||||
{
|
{
|
||||||
//cameras
|
for (int i = 0; i < IRenderer::camerasToRenderWith.size(); i++)
|
||||||
|
{
|
||||||
|
IRenderer::camerasToRenderWith[i]->Bind();
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
|
IRenderer::camerasToRenderWith[i]->Unbind();
|
||||||
|
}
|
||||||
|
|
||||||
glClear(GL_DEPTH_BUFFER_BIT);
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,17 +15,17 @@ void TSE::GLFW::RenderTexture::SetSize(Vector2 v)
|
|||||||
buffer.Resize(v);
|
buffer.Resize(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
float TSE::GLFW::RenderTexture::width() const
|
float TSE::GLFW::RenderTexture::Width() const
|
||||||
{
|
{
|
||||||
return buffer.GetSize().x;
|
return buffer.GetSize().x;
|
||||||
}
|
}
|
||||||
|
|
||||||
float TSE::GLFW::RenderTexture::height() const
|
float TSE::GLFW::RenderTexture::Height() const
|
||||||
{
|
{
|
||||||
return buffer.GetSize().y;
|
return buffer.GetSize().y;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint TSE::GLFW::RenderTexture::GetTextureId() const
|
TSE::uint TSE::GLFW::RenderTexture::GetTextureId() const
|
||||||
{
|
{
|
||||||
return buffer.GetTextureId();
|
return buffer.GetTextureId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
#include "interfaces/IRenderTarget.hpp"
|
#include "interfaces/IRenderTarget.hpp"
|
||||||
#include "interfaces/ITexture.hpp"
|
#include "interfaces/ITexture.hpp"
|
||||||
#include "interfaces/IResizeNotifiable.hpp"
|
#include "interfaces/IResizeNotifiable.hpp"
|
||||||
|
#include "interfaces/IRenderTexture.hpp"
|
||||||
|
|
||||||
namespace TSE::GLFW
|
namespace TSE::GLFW
|
||||||
{
|
{
|
||||||
class RenderTexture : public IRenderTarget, public ITexture, public IResizeNotifiable
|
class RenderTexture : public IRenderTexture
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FrameBuffer buffer;
|
FrameBuffer buffer;
|
||||||
@@ -15,9 +16,9 @@ namespace TSE::GLFW
|
|||||||
RenderTexture(Vector2 v);
|
RenderTexture(Vector2 v);
|
||||||
|
|
||||||
Vector2 size() const override;
|
Vector2 size() const override;
|
||||||
void SetSize(Vector2 v);
|
void SetSize(Vector2 v) override;
|
||||||
float width() const override;
|
float Width() const override;
|
||||||
float height() const override;
|
float Height() const override;
|
||||||
uint GetTextureId() const override;
|
uint GetTextureId() const override;
|
||||||
|
|
||||||
void Update() override;
|
void Update() override;
|
||||||
|
|||||||
16
TSE_GlfwOpenGlImpl/src/RenderTextureCreatorOpenGL.hpp
Normal file
16
TSE_GlfwOpenGlImpl/src/RenderTextureCreatorOpenGL.hpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "interfaces/IRenderTexture.hpp"
|
||||||
|
#include "RenderTexture.hpp"
|
||||||
|
|
||||||
|
namespace TSE::GLFW
|
||||||
|
{
|
||||||
|
class RenderTextureCreatorOpenGL : public IRenderTextureCreator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline IRenderTexture* CreateTextureHeap(Vector2 v) override
|
||||||
|
{
|
||||||
|
return new RenderTexture(v);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} // namespace name
|
||||||
@@ -18,19 +18,19 @@ void TSE::GLFW::TextureHelperOpenGL::Apply(Texture *tex)
|
|||||||
if(tex->Chanels() == 1)
|
if(tex->Chanels() == 1)
|
||||||
{
|
{
|
||||||
if (tex->bpp() == 8)
|
if (tex->bpp() == 8)
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->width(), tex->height(), 0, GL_RED, GL_UNSIGNED_BYTE, tex->GetImagePtr());
|
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->Width(), tex->Height(), 0, GL_RED, GL_UNSIGNED_BYTE, tex->GetImagePtr());
|
||||||
}
|
}
|
||||||
if(tex->Chanels() == 3)
|
if(tex->Chanels() == 3)
|
||||||
{
|
{
|
||||||
if(tex->bpp() == 24)
|
if(tex->bpp() == 24)
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->width(), tex->height(), 0, GL_BGR, GL_UNSIGNED_BYTE, tex->GetImagePtr());
|
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->Width(), tex->Height(), 0, GL_BGR, GL_UNSIGNED_BYTE, tex->GetImagePtr());
|
||||||
}
|
}
|
||||||
else if(tex->Chanels() == 4)
|
else if(tex->Chanels() == 4)
|
||||||
{
|
{
|
||||||
if(tex->bpp() == 32)
|
if(tex->bpp() == 32)
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->width(), tex->height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, tex->GetImagePtr());
|
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->Width(), tex->Height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, tex->GetImagePtr());
|
||||||
if (tex->bpp() == 8) //need to decode it with bitwise operations in shader
|
if (tex->bpp() == 8) //need to decode it with bitwise operations in shader
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->width(), tex->height(), 0, GL_RED, GL_UNSIGNED_BYTE, tex->GetImagePtr());
|
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, tex->Width(), tex->Height(), 0, GL_RED, GL_UNSIGNED_BYTE, tex->GetImagePtr());
|
||||||
}
|
}
|
||||||
|
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
TSE::GLFW::FrameBuffer::FrameBuffer(const Vector2 &size)
|
TSE::GLFW::FrameBuffer::FrameBuffer(const Vector2 &size)
|
||||||
{
|
{
|
||||||
this->size = size;
|
width = size.x;
|
||||||
|
height = size.y;
|
||||||
CreateFBTexture();
|
CreateFBTexture();
|
||||||
for (auto const& i : objectsToResize)
|
for (auto const& i : objectsToResize)
|
||||||
{
|
{
|
||||||
@@ -31,7 +32,8 @@ TSE::GLFW::FrameBuffer::~FrameBuffer()
|
|||||||
|
|
||||||
void TSE::GLFW::FrameBuffer::Resize(Vector2 size)
|
void TSE::GLFW::FrameBuffer::Resize(Vector2 size)
|
||||||
{
|
{
|
||||||
this->size = size;
|
width = size.x;
|
||||||
|
height = size.y;
|
||||||
shouldResize = true;
|
shouldResize = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ void TSE::GLFW::FrameBuffer::Update()
|
|||||||
CreateFBTexture();
|
CreateFBTexture();
|
||||||
for (auto const& i : objectsToResize)
|
for (auto const& i : objectsToResize)
|
||||||
{
|
{
|
||||||
i->OnResize(size.x, size.y, this);
|
i->OnResize(width, height, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +55,7 @@ TSE::uint TSE::GLFW::FrameBuffer::GetTextureId() const
|
|||||||
|
|
||||||
TSE::Vector2 TSE::GLFW::FrameBuffer::GetSize() const
|
TSE::Vector2 TSE::GLFW::FrameBuffer::GetSize() const
|
||||||
{
|
{
|
||||||
return size;
|
return {width, height};
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSE::GLFW::FrameBuffer::Initialize()
|
void TSE::GLFW::FrameBuffer::Initialize()
|
||||||
@@ -74,7 +76,7 @@ void TSE::GLFW::FrameBuffer::Initialize()
|
|||||||
void TSE::GLFW::FrameBuffer::LoadFBTexture()
|
void TSE::GLFW::FrameBuffer::LoadFBTexture()
|
||||||
{
|
{
|
||||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
@@ -82,12 +84,12 @@ void TSE::GLFW::FrameBuffer::LoadFBTexture()
|
|||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
glBindRenderbuffer(GL_RENDERBUFFER, depthRboID);
|
glBindRenderbuffer(GL_RENDERBUFFER, depthRboID);
|
||||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size.x, size.y);
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSE::GLFW::FrameBuffer::CreateFBTexture()
|
void TSE::GLFW::FrameBuffer::CreateFBTexture()
|
||||||
{
|
{
|
||||||
glViewport(0,0, size.x, size.y);
|
glViewport(0,0, width, height);
|
||||||
//resize
|
//resize
|
||||||
if(textureID == 0)
|
if(textureID == 0)
|
||||||
glGenTextures(1, &textureID);
|
glGenTextures(1, &textureID);
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ namespace TSE::GLFW
|
|||||||
private:
|
private:
|
||||||
uint textureID = 0;
|
uint textureID = 0;
|
||||||
uint depthRboID = 0;
|
uint depthRboID = 0;
|
||||||
Vector2 size;
|
|
||||||
bool shouldResize = false;
|
bool shouldResize = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
#define SHADER_PACKAGE_SIZE (sizeof(float) * (3 + 4 + 2 + 1))
|
#define SHADER_PACKAGE_SIZE (sizeof(float) * (3 + 4 + 2 + 1))
|
||||||
|
|
||||||
TSE::GLFW::BasicTextureShader* TSE::GLFW::BasicTextureShader::instance = nullptr;
|
TSE::GLFW::BasicTextureShader* TSE::GLFW::BasicTextureShader::instance = nullptr;
|
||||||
std::map<uint, float> TSE::GLFW::BasicTextureShader::textureSlots;
|
std::map<TSE::uint, float> TSE::GLFW::BasicTextureShader::textureSlots;
|
||||||
|
|
||||||
TSE::GLFW::BasicTextureShader *TSE::GLFW::BasicTextureShader::Instance()
|
TSE::GLFW::BasicTextureShader *TSE::GLFW::BasicTextureShader::Instance()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user