mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2024-12-11 04:51:07 +00:00
887 lines
45 KiB
C++
887 lines
45 KiB
C++
#pragma once
|
|
|
|
// Define IM3D_CONFIG "myfilename.h" from your build system if you do not want to modify im3d_config.h directly.
|
|
#ifdef IM3D_CONFIG
|
|
#include IM3D_CONFIG
|
|
#else
|
|
#include "im3d_config.h"
|
|
#endif
|
|
|
|
#define IM3D_VERSION "1.16"
|
|
|
|
#ifndef IM3D_API
|
|
#define IM3D_API
|
|
#endif
|
|
|
|
#ifndef IM3D_ASSERT
|
|
#include <cassert>
|
|
#define IM3D_ASSERT(e) assert(e)
|
|
#endif
|
|
|
|
#ifndef IM3D_VERTEX_ALIGNMENT
|
|
#define IM3D_VERTEX_ALIGNMENT 4
|
|
#endif
|
|
|
|
#include <cstdarg> // va_list
|
|
|
|
namespace Im3d {
|
|
|
|
typedef unsigned int U32;
|
|
struct Vec2;
|
|
struct Vec3;
|
|
struct Vec4;
|
|
struct Mat3;
|
|
struct Mat4;
|
|
struct Color;
|
|
struct VertexData;
|
|
struct AppData;
|
|
struct DrawList;
|
|
struct TextDrawList;
|
|
struct Context;
|
|
|
|
typedef U32 Id;
|
|
constexpr Id Id_Invalid = 0;
|
|
|
|
// Get AppData struct from the current context, fill before calling NewFrame().
|
|
IM3D_API AppData& GetAppData();
|
|
|
|
// Call at the start of each frame, after filling the AppData struct.
|
|
IM3D_API void NewFrame();
|
|
// Call after all Im3d calls have been made for the current frame, before accessing draw data.
|
|
IM3D_API void EndFrame();
|
|
|
|
// Access draw data. Draw lists are valid after calling EndFrame() and before calling NewFrame().
|
|
IM3D_API const DrawList* GetDrawLists();
|
|
IM3D_API U32 GetDrawListCount();
|
|
|
|
// Access to text draw data. Draw lists are valid after calling EndFrame() and before calling NewFrame().
|
|
IM3D_API const TextDrawList* GetTextDrawLists();
|
|
IM3D_API U32 GetTextDrawListCount();
|
|
|
|
// DEPRECATED (use EndFrame() + GetDrawLists()).
|
|
// Call after all Im3d calls have been made for the current frame.
|
|
IM3D_API void Draw();
|
|
|
|
|
|
// Begin/end primitive. End() must be called before starting each new primitive type.
|
|
IM3D_API void BeginPoints();
|
|
IM3D_API void BeginLines();
|
|
IM3D_API void BeginLineLoop();
|
|
IM3D_API void BeginLineStrip();
|
|
IM3D_API void BeginTriangles();
|
|
IM3D_API void BeginTriangleStrip();
|
|
IM3D_API void End();
|
|
|
|
// Add a vertex to the current primitive (call between Begin*() and End()).
|
|
IM3D_API void Vertex(const Vec3& _position);
|
|
IM3D_API void Vertex(const Vec3& _position, Color _color);
|
|
IM3D_API void Vertex(const Vec3& _position, float _size);
|
|
IM3D_API void Vertex(const Vec3& _position, float _size, Color _color);
|
|
IM3D_API void Vertex(float _x, float _y, float _z);
|
|
IM3D_API void Vertex(float _x, float _y, float _z, Color _color);
|
|
IM3D_API void Vertex(float _x, float _y, float _z, float _size);
|
|
IM3D_API void Vertex(float _x, float _y, float _z, float _size, Color _color);
|
|
|
|
// Color draw state (per vertex).
|
|
IM3D_API void PushColor(); // push the stack top
|
|
IM3D_API void PushColor(Color _color);
|
|
IM3D_API void PopColor();
|
|
IM3D_API void SetColor(Color _color);
|
|
IM3D_API void SetColor(float _r, float _g, float _b, float _a = 1.0f);
|
|
IM3D_API Color GetColor();
|
|
|
|
// Alpha draw state, multiplies the alpha set by the color draw state (per vertex).
|
|
IM3D_API void PushAlpha(); // push the stack top
|
|
IM3D_API void PushAlpha(float _alpha);
|
|
IM3D_API void PopAlpha();
|
|
IM3D_API void SetAlpha(float _alpha);
|
|
IM3D_API float GetAlpha();
|
|
|
|
// Size draw state, for points/lines this is the radius/width in pixels (per vertex).
|
|
IM3D_API void PushSize(); // push the stack top
|
|
IM3D_API void PushSize(float _size);
|
|
IM3D_API void PopSize();
|
|
IM3D_API void SetSize(float _size);
|
|
IM3D_API float GetSize();
|
|
|
|
// Sorting draw state, enable depth sorting between primitives (per primitive).
|
|
IM3D_API void PushEnableSorting(); // push the stack top
|
|
IM3D_API void PushEnableSorting(bool _enable);
|
|
IM3D_API void PopEnableSorting();
|
|
IM3D_API void EnableSorting(bool _enable);
|
|
|
|
// Push/pop all draw states (color, alpha, size, sorting).
|
|
IM3D_API void PushDrawState();
|
|
IM3D_API void PopDrawState();
|
|
|
|
// Transform state (per vertex).
|
|
IM3D_API void PushMatrix(); // push stack top
|
|
IM3D_API void PushMatrix(const Mat4& _mat4);
|
|
IM3D_API void PopMatrix();
|
|
IM3D_API void SetMatrix(const Mat4& _mat4);
|
|
IM3D_API void SetIdentity();
|
|
IM3D_API void MulMatrix(const Mat4& _mat4);
|
|
IM3D_API void Translate(float _x, float _y, float _z);
|
|
IM3D_API void Translate(const Vec3& _vec3);
|
|
IM3D_API void Rotate(const Vec3& _axis, float _angle);
|
|
IM3D_API void Rotate(const Mat3& _rotation);
|
|
IM3D_API void Scale(float _x, float _y, float _z);
|
|
|
|
// High order shapes. Where _detail = -1, an automatic level of detail is chosen based on the distance to the view origin (as specified via the AppData struct).
|
|
IM3D_API void DrawXyzAxes();
|
|
IM3D_API void DrawPoint(const Vec3& _position, float _size, Color _color);
|
|
IM3D_API void DrawLine(const Vec3& _a, const Vec3& _b, float _size, Color _color);
|
|
IM3D_API void DrawQuad(const Vec3& _a, const Vec3& _b, const Vec3& _c, const Vec3& _d);
|
|
IM3D_API void DrawQuad(const Vec3& _origin, const Vec3& _normal, const Vec2& _size);
|
|
IM3D_API void DrawQuadFilled(const Vec3& _a, const Vec3& _b, const Vec3& _c, const Vec3& _d);
|
|
IM3D_API void DrawQuadFilled(const Vec3& _origin, const Vec3& _normal, const Vec2& _size);
|
|
IM3D_API void DrawCircle(const Vec3& _origin, const Vec3& _normal, float _radius, int _detail = -1);
|
|
IM3D_API void DrawCircleFilled(const Vec3& _origin, const Vec3& _normal, float _radius, int _detail = -1);
|
|
IM3D_API void DrawSphere(const Vec3& _origin, float _radius, int _detail = -1);
|
|
IM3D_API void DrawSphereFilled(const Vec3& _origin, float _radius, int _detail = -1);
|
|
IM3D_API void DrawAlignedBox(const Vec3& _min, const Vec3& _max);
|
|
IM3D_API void DrawAlignedBoxFilled(const Vec3& _min, const Vec3& _max);
|
|
IM3D_API void DrawCylinder(const Vec3& _start, const Vec3& _end, float _radius, int _detail = -1);
|
|
IM3D_API void DrawCapsule(const Vec3& _start, const Vec3& _end, float _radius, int _detail = -1);
|
|
IM3D_API void DrawPrism(const Vec3& _start, const Vec3& _end, float _radius, int _sides);
|
|
IM3D_API void DrawArrow(const Vec3& _start, const Vec3& _end, float _headLength = -1.0f, float _headThickness = -1.0f);
|
|
IM3D_API void DrawCone(const Vec3& _origin, const Vec3& _normal,float height, float _radius, int _detail);
|
|
IM3D_API void DrawConeFilled(const Vec3& _origin, const Vec3& _normal,float height, float _radius, int _detail);
|
|
|
|
// Add text. See TextFlags_ enum for _textFlags. _size is a hint to the application-side text rendering.
|
|
IM3D_API void Text(const Vec3& _position, U32 _textFlags, const char* _text, ...); // use the current draw state for size/color
|
|
IM3D_API void Text(const Vec3& _position, float _size, Color _color, U32 _textFlags, const char* _text, ...);
|
|
|
|
// IDs are used to uniquely identify gizmos and layers. Gizmo should have a unique ID during a frame.
|
|
// Note that ids are a hash of the whole ID stack, see PushId(), PopId().
|
|
IM3D_API Id MakeId(const char* _str);
|
|
IM3D_API Id MakeId(const void* _ptr);
|
|
IM3D_API Id MakeId(int _i);
|
|
|
|
// PushId(), PopId() affect the result of subsequent calls to MakeId(), use when creating gizmos in a loop.
|
|
IM3D_API void PushId(); // push stack top
|
|
IM3D_API void PushId(Id _id);
|
|
IM3D_API void PushId(const char* _str);
|
|
IM3D_API void PushId(const void* _ptr);
|
|
IM3D_API void PushId(int _i);
|
|
IM3D_API void PopId();
|
|
IM3D_API Id GetId();
|
|
|
|
// Layer id state, subsequent primitives are added to a separate draw list associated with the id (per primitive).
|
|
IM3D_API void PushLayerId(Id _layer);
|
|
IM3D_API void PushLayerId(const char* _str); // calls PushLayerId(MakeId(_str))
|
|
IM3D_API void PopLayerId();
|
|
IM3D_API Id GetLayerId();
|
|
|
|
// Manipulate translation/rotation/scale via a gizmo. Return true if the gizmo is 'active' (if it modified the output parameter).
|
|
// If _local is true, the Gizmo* functions expect that the local matrix is on the matrix stack; in general the application should
|
|
// push the local matrix before calling any of the following.
|
|
IM3D_API bool GizmoTranslation(const char* _id, float _translation_[3], bool _local = false);
|
|
IM3D_API bool GizmoRotation(const char* _id, float _rotation_[3*3], bool _local = false);
|
|
IM3D_API bool GizmoScale(const char* _id, float _scale_[3]); // local scale only
|
|
// Unified gizmo, selects local/global, translation/rotation/scale based on the context-global gizmo modes. Return true if the gizmo is active.
|
|
IM3D_API bool Gizmo(const char* _id, float _translation_[3], float _rotation_[3*3], float _scale_[3]); // any of _translation_/_rotation_/_scale_ may be null.
|
|
IM3D_API bool Gizmo(const char* _id, float _transform_[4*4]);
|
|
|
|
// Gizmo* overloads which take an ID directly. In some cases the app may want to call MakeId() separately, usually to change the gizmo appearance if hot/active.
|
|
IM3D_API bool GizmoTranslation(Id _id, float _translation_[3], bool _local = false);
|
|
IM3D_API bool GizmoRotation(Id _id, float _rotation_[3*3], bool _local = false);
|
|
IM3D_API bool GizmoScale(Id _id, float _scale_[3]);
|
|
IM3D_API bool Gizmo(Id _id, float _transform_[4*4]);
|
|
IM3D_API bool Gizmo(Id _id, float _translation_[3], float _rotation_[3*3], float _scale_[3]);
|
|
|
|
// Active gizmo ID. This will match the _id parameter passed to a Gizmo* function. Return Id_Invalid if no gizmo is in use.
|
|
IM3D_API Id GetActiveId();
|
|
// ID of the current current 'hot' gizmo (nearest intersecting gizmo along the cursor ray).
|
|
IM3D_API Id GetHotId();
|
|
|
|
// Visibility tests. The application must set a culling frustum via AppData.
|
|
IM3D_API bool IsVisible(const Vec3& _origin, float _radius); // sphere
|
|
IM3D_API bool IsVisible(const Vec3& _min, const Vec3& _max); // axis-aligned bounding box
|
|
|
|
// Get/set the current context. All Im3d calls affect the currently bound context.
|
|
IM3D_API Context& GetContext();
|
|
IM3D_API void SetContext(Context& _ctx);
|
|
|
|
// Merge vertex data from _src into _dst_. Layers are preserved. Call before EndFrame().
|
|
IM3D_API void MergeContexts(Context& _dst_, const Context& _src);
|
|
|
|
|
|
struct IM3D_API Vec2
|
|
{
|
|
float x, y;
|
|
|
|
Vec2() {}
|
|
Vec2(float _xy): x(_xy), y(_xy) {}
|
|
Vec2(float _x, float _y): x(_x), y(_y) {}
|
|
|
|
operator float*() { return &x; }
|
|
operator const float*() const { return &x; }
|
|
|
|
#ifdef IM3D_VEC2_APP
|
|
IM3D_VEC2_APP
|
|
#endif
|
|
};
|
|
|
|
struct IM3D_API Vec3
|
|
{
|
|
float x, y, z;
|
|
|
|
Vec3() {}
|
|
Vec3(float _xyz): x(_xyz), y(_xyz), z(_xyz) {}
|
|
Vec3(float _x, float _y, float _z): x(_x), y(_y), z(_z) {}
|
|
Vec3(const Vec2& _xy, float _z): x(_xy.x), y(_xy.y), z(_z) {}
|
|
Vec3(const Vec4& _v); // discards w
|
|
|
|
operator float*() { return &x; }
|
|
operator const float*() const { return &x; }
|
|
|
|
#ifdef IM3D_VEC3_APP
|
|
IM3D_VEC3_APP
|
|
#endif
|
|
};
|
|
|
|
struct IM3D_API Vec4
|
|
{
|
|
float x, y, z, w;
|
|
|
|
Vec4() {}
|
|
Vec4(float _xyzw): x(_xyzw), y(_xyzw), z(_xyzw), w(_xyzw) {}
|
|
Vec4(float _x, float _y, float _z, float _w): x(_x), y(_y), z(_z), w(_w) {}
|
|
Vec4(const Vec3& _xyz, float _w): x(_xyz.x), y(_xyz.y), z(_xyz.z), w(_w) {}
|
|
Vec4(Color _rgba);
|
|
|
|
operator float*() { return &x; }
|
|
operator const float*() const { return &x; }
|
|
|
|
#ifdef IM3D_VEC4_APP
|
|
IM3D_VEC4_APP
|
|
#endif
|
|
};
|
|
|
|
struct IM3D_API Mat3
|
|
{
|
|
float m[3*3]; // column-major unless IM3D_MATRIX_ROW_MAJOR defined
|
|
|
|
Mat3() {}
|
|
Mat3(float _diagonal);
|
|
Mat3(
|
|
float m00, float m01, float m02,
|
|
float m10, float m11, float m12,
|
|
float m20, float m21, float m22
|
|
);
|
|
Mat3(const Vec3& _colX, const Vec3& _colY, const Vec3& _colZ);
|
|
Mat3(const Mat4& _mat4); // extract upper 3x3
|
|
|
|
operator float*() { return m; }
|
|
operator const float*() const { return m; }
|
|
|
|
Vec3 getCol(int _i) const;
|
|
Vec3 getRow(int _i) const;
|
|
void setCol(int _i, const Vec3& _v);
|
|
void setRow(int _i, const Vec3& _v);
|
|
|
|
Vec3 getScale() const;
|
|
void setScale(const Vec3& _scale);
|
|
|
|
float operator()(int _row, int _col) const
|
|
{
|
|
#ifdef IM3D_MATRIX_ROW_MAJOR
|
|
int i = _row * 3 + _col;
|
|
#else
|
|
int i = _col * 3 + _row;
|
|
#endif
|
|
return m[i];
|
|
}
|
|
float& operator()(int _row, int _col)
|
|
{
|
|
#ifdef IM3D_MATRIX_ROW_MAJOR
|
|
int i = _row * 3 + _col;
|
|
#else
|
|
int i = _col * 3 + _row;
|
|
#endif
|
|
return m[i];
|
|
}
|
|
|
|
#ifdef IM3D_MAT3_APP
|
|
IM3D_MAT3_APP
|
|
#endif
|
|
};
|
|
|
|
struct IM3D_API Mat4
|
|
{
|
|
float m[4*4]; // column-major unless IM3D_MATRIX_ROW_MAJOR defined
|
|
|
|
Mat4() {}
|
|
Mat4(float _diagonal);
|
|
Mat4(
|
|
float m00, float m01, float m02, float m03,
|
|
float m10, float m11, float m12, float m13,
|
|
float m20, float m21, float m22, float m23,
|
|
float m30 = 0.0f, float m31 = 0.0f, float m32 = 0.0f, float m33 = 1.0f
|
|
);
|
|
Mat4(const Mat3& _mat3);
|
|
Mat4(const Vec3& _translation, const Mat3& _rotation, const Vec3& _scale);
|
|
|
|
operator float*() { return m; }
|
|
operator const float*() const { return m; }
|
|
|
|
Vec4 getCol(int _i) const;
|
|
Vec4 getRow(int _i) const;
|
|
void setCol(int _i, const Vec4& _v);
|
|
void setRow(int _i, const Vec4& _v);
|
|
|
|
Vec3 getTranslation() const;
|
|
void setTranslation(const Vec3& _translation);
|
|
Mat3 getRotation() const;
|
|
void setRotation(const Mat3& _rotation);
|
|
Vec3 getScale() const;
|
|
void setScale(const Vec3& _scale);
|
|
|
|
float operator()(int _row, int _col) const
|
|
{
|
|
#ifdef IM3D_MATRIX_ROW_MAJOR
|
|
int i = _row * 4 + _col;
|
|
#else
|
|
int i = _col * 4 + _row;
|
|
#endif
|
|
return m[i];
|
|
}
|
|
float& operator()(int _row, int _col)
|
|
{
|
|
#ifdef IM3D_MATRIX_ROW_MAJOR
|
|
int i = _row * 4 + _col;
|
|
#else
|
|
int i = _col * 4 + _row;
|
|
#endif
|
|
return m[i];
|
|
}
|
|
|
|
#ifdef IM3D_MAT4_APP
|
|
IM3D_MAT4_APP
|
|
#endif
|
|
};
|
|
|
|
struct IM3D_API Color
|
|
{
|
|
U32 v; // rgba8 (MSB = r)
|
|
|
|
constexpr Color(): v(0) {}
|
|
constexpr Color(U32 _rgba): v(_rgba) {}
|
|
Color(const Vec4& _rgba);
|
|
Color(const Vec3& _rgb, float _alpha);
|
|
Color(float _r, float _g, float _b, float _a = 1.0f);
|
|
|
|
operator U32() const { return v; }
|
|
|
|
void set(int _i, float _val)
|
|
{
|
|
_i *= 8;
|
|
U32 mask = 0xff << _i;
|
|
v = (v & ~mask) | (U32(_val * 255.0f) << _i);
|
|
}
|
|
void setR(float _val) { set(3, _val); }
|
|
void setG(float _val) { set(2, _val); }
|
|
void setB(float _val) { set(1, _val); }
|
|
void setA(float _val) { set(0, _val); }
|
|
|
|
float get(int _i) const
|
|
{
|
|
_i *= 8;
|
|
U32 mask = 0xff << _i;
|
|
return float((v & mask) >> _i) / 255.0f;
|
|
}
|
|
float getR() const { return get(3); }
|
|
float getG() const { return get(2); }
|
|
float getB() const { return get(1); }
|
|
float getA() const { return get(0); }
|
|
|
|
U32 getABGR() const
|
|
{
|
|
return 0
|
|
| ((v & (0xff << 24)) >> 24) // r
|
|
| ((v & (0xff << 16)) >> 8) // g
|
|
| ((v & (0xff << 8)) << 8) // b
|
|
| ((v & (0xff )) << 24) // a
|
|
;
|
|
}
|
|
};
|
|
|
|
constexpr Color Color_Black = Color(0x000000ff);
|
|
constexpr Color Color_White = Color(0xffffffff);
|
|
constexpr Color Color_Gray = Color(0x808080ff);
|
|
constexpr Color Color_Red = Color(0xff0000ff);
|
|
constexpr Color Color_Green = Color(0x00ff00ff);
|
|
constexpr Color Color_Blue = Color(0x0000ffff);
|
|
constexpr Color Color_Magenta = Color(0xff00ffff);
|
|
constexpr Color Color_Yellow = Color(0xffff00ff);
|
|
constexpr Color Color_Cyan = Color(0x00ffffff);
|
|
|
|
constexpr Color Color_Pink = Color(0xffc0cbff);
|
|
constexpr Color Color_Orange = Color(0xffa500ff);
|
|
constexpr Color Color_Gold = Color(0xffd700ff);
|
|
constexpr Color Color_Brown = Color(0x8b4513ff);
|
|
constexpr Color Color_Purple = Color(0x800080ff);
|
|
constexpr Color Color_Teal = Color(0x008080ff);
|
|
constexpr Color Color_Navy = Color(0x000080ff);
|
|
|
|
struct alignas(IM3D_VERTEX_ALIGNMENT) VertexData
|
|
{
|
|
Vec4 m_positionSize; // xyz = position, w = size
|
|
Color m_color; // rgba8 (MSB = r)
|
|
|
|
VertexData() {}
|
|
VertexData(const Vec3& _position, float _size, Color _color): m_positionSize(_position, _size), m_color(_color) {}
|
|
};
|
|
|
|
enum DrawPrimitiveType
|
|
{
|
|
// order here determines the order in which unsorted primitives are drawn
|
|
DrawPrimitive_Triangles,
|
|
DrawPrimitive_Lines,
|
|
DrawPrimitive_Points,
|
|
|
|
DrawPrimitive_Count
|
|
};
|
|
|
|
struct DrawList
|
|
{
|
|
Id m_layerId;
|
|
DrawPrimitiveType m_primType;
|
|
const VertexData* m_vertexData;
|
|
U32 m_vertexCount;
|
|
};
|
|
typedef void (DrawPrimitivesCallback)(const DrawList& _drawList);
|
|
|
|
enum TextFlags
|
|
{
|
|
TextFlags_AlignLeft = (1 << 0),
|
|
TextFlags_AlignRight = (1 << 1),
|
|
TextFlags_AlignTop = (1 << 3),
|
|
TextFlags_AlignBottom = (1 << 4),
|
|
|
|
TextFlags_Default = 0 // align center
|
|
};
|
|
|
|
struct alignas(IM3D_VERTEX_ALIGNMENT) TextData
|
|
{
|
|
Vec4 m_positionSize; // xyz = position, w = size
|
|
Color m_color; // rgba8 (MSB = r)
|
|
U32 m_flags; // TextFlags
|
|
U32 m_textLength; // # chars in the text, excluding null terminator
|
|
U32 m_textBufferOffset; // start of the text in the draw list's text buffer
|
|
};
|
|
|
|
struct TextDrawList
|
|
{
|
|
Id m_layerId;
|
|
const TextData* m_textData;
|
|
U32 m_textDataCount;
|
|
const char* m_textBuffer;
|
|
};
|
|
|
|
enum Key
|
|
{
|
|
Mouse_Left,
|
|
Key_L,
|
|
Key_R,
|
|
Key_S,
|
|
Key_T,
|
|
|
|
Key_Count,
|
|
|
|
// the following map keys -> 'action' states which may be more intuitive
|
|
Action_Select = Mouse_Left,
|
|
Action_GizmoLocal = Key_L,
|
|
Action_GizmoRotation = Key_R,
|
|
Action_GizmoScale = Key_S,
|
|
Action_GizmoTranslation = Key_T,
|
|
|
|
Action_Count
|
|
};
|
|
|
|
enum FrustumPlane
|
|
{
|
|
FrustumPlane_Near,
|
|
FrustumPlane_Far,
|
|
FrustumPlane_Top,
|
|
FrustumPlane_Right,
|
|
FrustumPlane_Bottom,
|
|
FrustumPlane_Left,
|
|
|
|
FrustumPlane_Count
|
|
};
|
|
|
|
struct AppData
|
|
{
|
|
bool m_keyDown[Key_Count] = { false }; // Key states.
|
|
Vec4 m_cullFrustum[FrustumPlane_Count] = { Vec4(0.0f) }; // Frustum planes for culling (if culling enabled).
|
|
Vec3 m_cursorRayOrigin = Vec3(0.0f); // World space cursor ray origin.
|
|
Vec3 m_cursorRayDirection = Vec3(0.0f); // World space cursor ray direction.
|
|
Vec3 m_worldUp = Vec3(0.0f, 1.0f, 0.0f); // World space 'up' vector.
|
|
Vec3 m_viewOrigin = Vec3(0.0f); // World space render origin (camera position).
|
|
Vec3 m_viewDirection = Vec3(0.0f); // World space view direction.
|
|
Vec2 m_viewportSize = Vec2(0.0f); // Viewport size (pixels).
|
|
float m_projScaleY = 1.0f; // Scale factor used to convert from pixel size -> world scale; use tan(fov) for perspective projections, far plane height for ortho.
|
|
bool m_projOrtho = false; // If the projection matrix is orthographic.
|
|
float m_deltaTime = 0.0f; // Time since previous frame (seconds).
|
|
float m_snapTranslation = 0.0f; // Snap value for translation gizmos (world units). 0 = disabled.
|
|
float m_snapRotation = 0.0f; // Snap value for rotation gizmos (radians). 0 = disabled.
|
|
float m_snapScale = 0.0f; // Snap value for scale gizmos. 0 = disabled.
|
|
bool m_flipGizmoWhenBehind = true; // Flip gizmo axes when viewed from behind.
|
|
void* m_appData = nullptr; // App-specific data.
|
|
|
|
DrawPrimitivesCallback* drawCallback = nullptr; // e.g. void Im3d_Draw(const DrawList& _drawList)
|
|
|
|
// Extract cull frustum planes from the view-projection matrix.
|
|
// Set _ndcZNegativeOneToOne = true if the proj matrix maps z from [-1,1] (OpenGL style).
|
|
void setCullFrustum(const Mat4& _viewProj, bool _ndcZNegativeOneToOne);
|
|
};
|
|
|
|
// Minimal vector.
|
|
template <typename T>
|
|
struct Vector
|
|
{
|
|
Vector() {}
|
|
~Vector();
|
|
|
|
T& operator[](U32 _i) { IM3D_ASSERT(_i < m_size); return m_data[_i]; }
|
|
const T& operator[](U32 _i) const { IM3D_ASSERT(_i < m_size); return m_data[_i]; }
|
|
T* data() { return m_data; }
|
|
const T* data() const { return m_data; }
|
|
|
|
T& push_back() { if (m_size == m_capacity) { reserve(m_capacity + m_capacity / 2); } return m_data[m_size++]; }
|
|
void push_back(const T& _v) { T tmp = _v; if (m_size == m_capacity) { reserve(m_capacity + m_capacity / 2); } m_data[m_size++] = tmp; }
|
|
void pop_back() { IM3D_ASSERT(m_size > 0); --m_size; }
|
|
void append(const T* _v, U32 _count);
|
|
void append(const Vector<T>& _other) { append(_other.data(), _other.size()); }
|
|
|
|
T* begin() { return m_data; }
|
|
const T* begin() const { return m_data; }
|
|
T* end() { return m_data + m_size; }
|
|
const T* end() const { return m_data + m_size; }
|
|
T& front() { IM3D_ASSERT(m_size > 0); return m_data[0]; }
|
|
const T& front() const { IM3D_ASSERT(m_size > 0); return m_data[0]; }
|
|
T& back() { IM3D_ASSERT(m_size > 0); return m_data[m_size - 1]; }
|
|
const T& back() const { IM3D_ASSERT(m_size > 0); return m_data[m_size - 1]; }
|
|
|
|
U32 size() const { return m_size; }
|
|
U32 capacity() const { return m_capacity; }
|
|
bool empty() const { return m_size == 0; }
|
|
|
|
void clear() { m_size = 0; }
|
|
void reserve(U32 _capacity);
|
|
void resize(U32 _size, const T& _val);
|
|
void resize(U32 _size);
|
|
|
|
static void swap(Vector<T>& _a_, Vector<T>& _b_);
|
|
|
|
private:
|
|
|
|
T* m_data = nullptr;
|
|
U32 m_size = 0;
|
|
U32 m_capacity = 0;
|
|
};
|
|
|
|
|
|
enum PrimitiveMode
|
|
{
|
|
PrimitiveMode_None,
|
|
PrimitiveMode_Points,
|
|
PrimitiveMode_Lines,
|
|
PrimitiveMode_LineStrip,
|
|
PrimitiveMode_LineLoop,
|
|
PrimitiveMode_Triangles,
|
|
PrimitiveMode_TriangleStrip
|
|
};
|
|
enum GizmoMode
|
|
{
|
|
GizmoMode_Translation,
|
|
GizmoMode_Rotation,
|
|
GizmoMode_Scale
|
|
};
|
|
|
|
// Context stores all relevant state - main interface affects the context currently bound via SetCurrentContext().
|
|
struct IM3D_API Context
|
|
{
|
|
Context();
|
|
~Context();
|
|
|
|
void begin(PrimitiveMode _mode);
|
|
void end();
|
|
|
|
void vertex(const Vec3& _position, float _size, Color _color);
|
|
void vertex(const Vec3& _position ) { vertex(_position, getSize(), getColor()); }
|
|
|
|
void text(const Vec3& _position, float _size, Color _color, TextFlags _flags, const char* _textStart, const char* _textEnd);
|
|
void text(const Vec3& _position, float _size, Color _color, TextFlags _flags, const char* _text, va_list _args);
|
|
|
|
void reset();
|
|
void merge(const Context& _src);
|
|
void endFrame();
|
|
void draw(); // DEPRECATED (see Im3d::Draw)
|
|
|
|
const DrawList* getDrawLists() const { return m_drawLists.data(); }
|
|
U32 getDrawListCount() const { return m_drawLists.size(); }
|
|
|
|
const TextDrawList* getTextDrawLists() const { return m_textDrawLists.data(); }
|
|
U32 getTextDrawListCount() const { return m_textDrawLists.size(); }
|
|
|
|
|
|
void setColor(Color _color) { m_colorStack.back() = _color; }
|
|
Color getColor() const { return m_colorStack.back(); }
|
|
void pushColor(Color _color) { m_colorStack.push_back(_color); }
|
|
void popColor() { IM3D_ASSERT(m_colorStack.size() > 1); m_colorStack.pop_back(); }
|
|
|
|
void setAlpha(float _alpha) { m_alphaStack.back() = _alpha; }
|
|
float getAlpha() const { return m_alphaStack.back(); }
|
|
void pushAlpha(float _alpha) { m_alphaStack.push_back(_alpha); }
|
|
void popAlpha() { IM3D_ASSERT(m_alphaStack.size() > 1); m_alphaStack.pop_back(); }
|
|
|
|
void setSize(float _size) { m_sizeStack.back() = _size; }
|
|
float getSize() const { return m_sizeStack.back(); }
|
|
void pushSize(float _size) { m_sizeStack.push_back(_size); }
|
|
void popSize() { IM3D_ASSERT(m_sizeStack.size() > 1); m_sizeStack.pop_back(); }
|
|
|
|
void setEnableSorting(bool _enable);
|
|
bool getEnableSorting() const { return m_enableSortingStack.back(); }
|
|
void pushEnableSorting(bool _enable);
|
|
void popEnableSorting();
|
|
|
|
Id getLayerId() const { return m_layerIdStack.back(); }
|
|
void pushLayerId(Id _layer);
|
|
void popLayerId();
|
|
|
|
void setMatrix(const Mat4& _mat4) { m_matrixStack.back() = _mat4; }
|
|
const Mat4& getMatrix() const { return m_matrixStack.back(); }
|
|
void pushMatrix(const Mat4& _mat4) { m_matrixStack.push_back(_mat4); }
|
|
void popMatrix() { IM3D_ASSERT(m_matrixStack.size() > 1); m_matrixStack.pop_back(); }
|
|
|
|
void setId(Id _id) { m_idStack.back() = _id; }
|
|
Id getId() const { return m_idStack.back(); }
|
|
void pushId(Id _id) { m_idStack.push_back(_id); }
|
|
void popId() { IM3D_ASSERT(m_idStack.size() > 1); m_idStack.pop_back(); }
|
|
|
|
AppData& getAppData() { return m_appData; }
|
|
|
|
// Low-level interface for internal and app-defined gizmos. May be subject to breaking changes.
|
|
|
|
bool gizmoAxisTranslation_Behavior(Id _id, const Vec3& _origin, const Vec3& _axis, float _snap, float _worldHeight, float _worldSize, Vec3* _out_);
|
|
void gizmoAxisTranslation_Draw(Id _id, const Vec3& _origin, const Vec3& _axis, float _worldHeight, float _worldSize, Color _color);
|
|
|
|
bool gizmoPlaneTranslation_Behavior(Id _id, const Vec3& _origin, const Vec3& _normal, float _snap, float _worldSize, Vec3* _out_);
|
|
void gizmoPlaneTranslation_Draw(Id _id, const Vec3& _origin, const Vec3& _normal, float _worldSize, Color _color);
|
|
|
|
bool gizmoAxislAngle_Behavior(Id _id, const Vec3& _origin, const Vec3& _axis, float _snap, float _worldRadius, float _worldSize, float* _out_);
|
|
void gizmoAxislAngle_Draw(Id _id, const Vec3& _origin, const Vec3& _axis, float _worldRadius, float _angle, Color _color, float _minAlpha);
|
|
|
|
bool gizmoAxisScale_Behavior(Id _id, const Vec3& _origin, const Vec3& _axis, float _snap, float _worldHeight, float _worldSize, float *_out_);
|
|
void gizmoAxisScale_Draw(Id _id, const Vec3& _origin, const Vec3& _axis, float _worldHeight, float _worldSize, Color _color);
|
|
|
|
// Convert pixels -> world space size based on distance between _position and view origin.
|
|
float pixelsToWorldSize(const Vec3& _position, float _pixels);
|
|
// Convert world space size -> pixels based on distance between _position and view origin.
|
|
float worldSizeToPixels(const Vec3& _position, float _pixels);
|
|
// Blend between _min and _max based on distance betwen _position and view origin.
|
|
int estimateLevelOfDetail(const Vec3& _position, float _worldSize, int _min = 4, int _max = 256);
|
|
|
|
// Make _id hot if _depth < m_hotDepth && _intersects.
|
|
bool makeHot(Id _id, float _depth, bool _intersects);
|
|
// Make _id active.
|
|
void makeActive(Id _id);
|
|
// Reset the acive/hot ids and the hot depth.
|
|
void resetId();
|
|
|
|
// Interpret key state.
|
|
bool isKeyDown(Key _key) const { return m_keyDownCurr[_key]; }
|
|
bool wasKeyPressed(Key _key) const { return m_keyDownCurr[_key] && !m_keyDownPrev[_key]; }
|
|
|
|
// Visibiity tests for culling.
|
|
bool isVisible(const VertexData* _vdata, DrawPrimitiveType _prim); // per-vertex
|
|
bool isVisible(const Vec3& _origin, float _radius); // sphere
|
|
bool isVisible(const Vec3& _min, const Vec3& _max); // axis-aligned box
|
|
|
|
// Gizmo state.
|
|
|
|
bool m_gizmoLocal; // Global mode selection for gizmos.
|
|
GizmoMode m_gizmoMode; // "
|
|
Id m_activeId; // Currently active gizmo. If set, this is the same as m_hotId.
|
|
Id m_hotId; // ID of the current 'hot' gizmo (nearest intersecting gizmo along the cursor ray). NB this is the id of the *sub* gizmo, not the app-specified ID.
|
|
float m_hotDepth; // Depth of the current hot gizmo along the cursor ray, for handling occlusion.
|
|
Id m_appId; // Current ID *without* the hashing the ID stack (= _id arg to Gizmo* functions).
|
|
Id m_appActiveId; // Copied from m_appId for the current active gizmo.
|
|
Id m_appHotId; // Copied from m_appId for the current 'hot' gizmo.
|
|
Vec3 m_gizmoStateVec3; // Stored state for the active gizmo.
|
|
Mat3 m_gizmoStateMat3; // "
|
|
float m_gizmoStateFloat; // "
|
|
float m_gizmoHeightPixels; // Height/radius of gizmos.
|
|
float m_gizmoSizePixels; // Thickness of gizmo lines.
|
|
|
|
|
|
// Stats, debugging.
|
|
|
|
// Return the total number of primitives (sorted + unsorted) of the given _type in all layers.
|
|
U32 getPrimitiveCount(DrawPrimitiveType _type) const;
|
|
|
|
// Return the total number of text primitives in all layers.
|
|
U32 getTextCount() const;
|
|
|
|
// Return the number of layers.
|
|
U32 getLayerCount() const { return m_layerIdMap.size(); }
|
|
|
|
private:
|
|
|
|
// State stacks.
|
|
Vector<Color> m_colorStack;
|
|
Vector<float> m_alphaStack;
|
|
Vector<float> m_sizeStack;
|
|
Vector<bool> m_enableSortingStack;
|
|
Vector<Mat4> m_matrixStack;
|
|
Vector<Id> m_idStack;
|
|
Vector<Id> m_layerIdStack;
|
|
|
|
// Vertex data: one list per layer, per primitive type, *2 for sorted/unsorted.
|
|
typedef Vector<VertexData> VertexList;
|
|
Vector<VertexList*> m_vertexData[2]; // Each layer is DrawPrimitive_Count consecutive lists.
|
|
int m_vertexDataIndex; // 0, or 1 if sorting enabled.
|
|
Vector<Id> m_layerIdMap; // Map Id -> vertex data index.
|
|
int m_layerIndex; // Index of the currently active layer in m_layerIdMap.
|
|
Vector<DrawList> m_drawLists; // All draw lists for the current frame, available after calling endFrame() before calling reset().
|
|
bool m_sortCalled; // Avoid calling sort() during every call to draw().
|
|
bool m_endFrameCalled; // For assert, if vertices are pushed after endFrame() was called.
|
|
|
|
// Text data: one list per layer.
|
|
typedef Vector<TextData> TextList;
|
|
Vector<TextList*> m_textData;
|
|
Vector<char> m_textBuffer;
|
|
Vector<TextDrawList> m_textDrawLists;
|
|
|
|
// Primitive state.
|
|
PrimitiveMode m_primMode;
|
|
DrawPrimitiveType m_primType;
|
|
U32 m_firstVertThisPrim; // Index of the first vertex pushed during this primitive.
|
|
U32 m_vertCountThisPrim; // # calls to vertex() since the last call to begin().
|
|
Vec3 m_minVertThisPrim;
|
|
Vec3 m_maxVertThisPrim;
|
|
|
|
// App data.
|
|
AppData m_appData;
|
|
bool m_keyDownCurr[Key_Count]; // Key state captured during reset().
|
|
bool m_keyDownPrev[Key_Count]; // Key state from previous frame.
|
|
Vec4 m_cullFrustum[FrustumPlane_Count]; // Optimized frustum planes from m_appData.m_cullFrustum.
|
|
int m_cullFrustumCount; // # valid frustum planes in m_cullFrustum.
|
|
|
|
// Sort primitive data.
|
|
void sort();
|
|
|
|
// Return -1 if _id not found.
|
|
int findLayerIndex(Id _id) const;
|
|
|
|
// Access the current vertex/text data based on m_layerIndex.
|
|
VertexList* getCurrentVertexList();
|
|
TextList* getCurrentTextList();
|
|
};
|
|
|
|
namespace internal {
|
|
|
|
#if IM3D_THREAD_LOCAL_CONTEXT_PTR
|
|
#define IM3D_THREAD_LOCAL thread_local
|
|
#else
|
|
#define IM3D_THREAD_LOCAL
|
|
#endif
|
|
|
|
extern IM3D_THREAD_LOCAL Context* g_CurrentContext;
|
|
|
|
}
|
|
|
|
inline AppData& GetAppData() { return GetContext().getAppData(); }
|
|
inline void NewFrame() { GetContext().reset(); }
|
|
inline void EndFrame() { GetContext().endFrame(); }
|
|
inline void Draw() { GetContext().draw(); }
|
|
|
|
inline const DrawList* GetDrawLists() { return GetContext().getDrawLists(); }
|
|
inline U32 GetDrawListCount() { return GetContext().getDrawListCount(); }
|
|
|
|
inline const TextDrawList* GetTextDrawLists() { return GetContext().getTextDrawLists(); }
|
|
inline U32 GetTextDrawListCount() { return GetContext().getTextDrawListCount(); }
|
|
|
|
inline void BeginPoints() { GetContext().begin(PrimitiveMode_Points); }
|
|
inline void BeginLines() { GetContext().begin(PrimitiveMode_Lines); }
|
|
inline void BeginLineLoop() { GetContext().begin(PrimitiveMode_LineLoop); }
|
|
inline void BeginLineStrip() { GetContext().begin(PrimitiveMode_LineStrip); }
|
|
inline void BeginTriangles() { GetContext().begin(PrimitiveMode_Triangles); }
|
|
inline void BeginTriangleStrip() { GetContext().begin(PrimitiveMode_TriangleStrip); }
|
|
inline void End() { GetContext().end(); }
|
|
|
|
inline void Vertex(const Vec3& _position) { GetContext().vertex(_position, GetContext().getSize(), GetContext().getColor()); }
|
|
inline void Vertex(const Vec3& _position, Color _color) { GetContext().vertex(_position, GetContext().getSize(), _color); }
|
|
inline void Vertex(const Vec3& _position, float _size) { GetContext().vertex(_position, _size, GetContext().getColor()); }
|
|
inline void Vertex(const Vec3& _position, float _size, Color _color) { GetContext().vertex(_position, _size, _color); }
|
|
inline void Vertex(float _x, float _y, float _z) { Vertex(Vec3(_x, _y, _z)); }
|
|
inline void Vertex(float _x, float _y, float _z, Color _color) { Vertex(Vec3(_x, _y, _z), _color); }
|
|
inline void Vertex(float _x, float _y, float _z, float _size) { Vertex(Vec3(_x, _y, _z), _size); }
|
|
inline void Vertex(float _x, float _y, float _z, float _size, Color _color) { Vertex(Vec3(_x, _y, _z), _size, _color); }
|
|
|
|
inline void PushDrawState() { Context& ctx = GetContext(); ctx.pushColor(ctx.getColor()); ctx.pushAlpha(ctx.getAlpha()); ctx.pushSize(ctx.getSize()); ctx.pushEnableSorting(ctx.getEnableSorting()); }
|
|
inline void PopDrawState() { Context& ctx = GetContext(); ctx.popColor(); ctx.popAlpha(); ctx.popSize(); ctx.popEnableSorting(); }
|
|
|
|
inline void PushColor() { GetContext().pushColor(GetContext().getColor()); }
|
|
inline void PushColor(Color _color) { GetContext().pushColor(_color); }
|
|
inline void PopColor() { GetContext().popColor(); }
|
|
inline void SetColor(Color _color) { GetContext().setColor(_color); }
|
|
inline void SetColor(float _r, float _g, float _b, float _a) { GetContext().setColor(Color(_r, _g, _b, _a)); }
|
|
inline Color GetColor() { return GetContext().getColor(); }
|
|
|
|
inline void PushAlpha() { GetContext().pushAlpha(GetContext().getAlpha()); }
|
|
inline void PushAlpha(float _alpha) { GetContext().pushAlpha(_alpha); }
|
|
inline void PopAlpha() { GetContext().popAlpha(); }
|
|
inline void SetAlpha(float _alpha) { GetContext().setAlpha(_alpha); }
|
|
inline float GetAlpha() { return GetContext().getAlpha(); }
|
|
|
|
inline void PushSize() { GetContext().pushSize(GetContext().getAlpha()); }
|
|
inline void PushSize(float _size) { GetContext().pushSize(_size); }
|
|
inline void PopSize() { GetContext().popSize(); }
|
|
inline void SetSize(float _size) { GetContext().setSize(_size); }
|
|
inline float GetSize() { return GetContext().getSize(); }
|
|
|
|
inline void PushEnableSorting() { GetContext().pushEnableSorting(GetContext().getEnableSorting()); }
|
|
inline void PushEnableSorting(bool _enable) { GetContext().pushEnableSorting(_enable); }
|
|
inline void PopEnableSorting() { GetContext().popEnableSorting(); }
|
|
inline void EnableSorting(bool _enable) { GetContext().setEnableSorting(_enable); }
|
|
|
|
inline void PushMatrix() { GetContext().pushMatrix(GetContext().getMatrix()); }
|
|
inline void PushMatrix(const Mat4& _mat4) { GetContext().pushMatrix(_mat4); }
|
|
inline void PopMatrix() { GetContext().popMatrix(); }
|
|
inline void SetMatrix(const Mat4& _mat4) { GetContext().setMatrix(_mat4); }
|
|
inline void SetIdentity() { GetContext().setMatrix(Mat4(1.0f)); }
|
|
|
|
inline void PushId() { GetContext().pushId(GetContext().getId()); }
|
|
inline void PushId(Id _id) { GetContext().pushId(_id); }
|
|
inline void PushId(const char* _str) { GetContext().pushId(MakeId(_str)); }
|
|
inline void PushId(const void* _ptr) { GetContext().pushId(MakeId(_ptr)); }
|
|
inline void PushId(int _i) { GetContext().pushId(MakeId(_i)); }
|
|
inline void PopId() { GetContext().popId(); }
|
|
inline Id GetId() { return GetContext().getId(); }
|
|
|
|
inline void PushLayerId() { GetContext().pushLayerId(GetContext().getLayerId()); }
|
|
inline void PushLayerId(Id _layer) { GetContext().pushLayerId(_layer); }
|
|
inline void PushLayerId(const char* _str) { PushLayerId(MakeId(_str)); }
|
|
inline void PopLayerId() { GetContext().popLayerId(); }
|
|
inline Id GetLayerId() { return GetContext().getLayerId(); }
|
|
|
|
inline bool GizmoTranslation(const char* _id, float _translation_[3], bool _local) { return GizmoTranslation(MakeId(_id), _translation_, _local); }
|
|
inline bool GizmoRotation(const char* _id, float _rotation_[3*3], bool _local) { return GizmoRotation(MakeId(_id), _rotation_, _local);}
|
|
inline bool GizmoScale(const char* _id, float _scale_[3]) { return GizmoScale(MakeId(_id), _scale_); }
|
|
inline bool Gizmo(const char* _id, float _translation_[3], float _rotation_[3*3], float _scale_[3]) { return Gizmo(MakeId(_id), _translation_, _rotation_, _scale_); }
|
|
inline bool Gizmo(const char* _id, float _transform_[4*4]) { return Gizmo(MakeId(_id), _transform_); }
|
|
inline Id GetActiveId() { return GetContext().m_appActiveId;}
|
|
inline Id GetHotId() { return GetContext().m_appHotId; }
|
|
|
|
inline bool IsVisible(const Vec3& _origin, float _radius) { return GetContext().isVisible(_origin, _radius); }
|
|
inline bool IsVisible(const Vec3& _min, const Vec3& _max) { return GetContext().isVisible(_min, _max);}
|
|
|
|
inline Context& GetContext() { return *internal::g_CurrentContext; }
|
|
inline void SetContext(Context& _ctx) { internal::g_CurrentContext = &_ctx; }
|
|
inline void MergeContexts(Context& _dst_, const Context& _src) { _dst_.merge(_src); }
|
|
|
|
} // namespac Im3d
|