This C++ example demonstrates an interactive 3D mesh using the the GLU tesselator and per-pixel-lighting.
This C++ example demonstrates an interactive 3D mesh using the the GLU tesselator and per-pixel-lighting.
#include <cstdio>
#include <cmath>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "../demos/graph/testshapes/Glyph03FreeMonoRegular_M.hpp"
#include "../demos/GLLauncher01.hpp"
#include <cstdio>
#include <cmath>
#include <memory>
#include <vector>
#include "../demos/GLLauncher01.hpp"
#if 0
static void printOutlineShape(
const std::string &tag,
const OutlineShape& o,
size_t idx=0) {
size_t o_idx=0;
printf(
" - Outline [%2zu][%2zu]:\n", idx, o_idx);
for(
const Vertex& v : ol.vertices()){
printf(
" - V[%2zu][%2zu]: %s\n", idx, o_idx, v.coord().toString());
}
++o_idx;
}
}
static void printOutlineShapes(const std::string &tag, const std::vector<OutlineShape>& oshapes) {
printf(
"%s: %zu OutlineShapes\n", tag, oshapes.size());
size_t os_idx=0;
printOutlineShape(tag, o, os_idx);
++os_idx;
}
}
#endif
public:
};
private:
constexpr static PMVData mat_req = PMVData::inv_proj | PMVData::inv_mv | PMVData::inv_tps_mv;
ShaderState& m_st;
GLUniformSyncPMVMat4f m_pmvMat;
GLUniformVec3f m_light0Pos;
GLUniformVec4f m_staticColor;
bool m_initialized;
public:
public:
m_pmvMat("gcu_PMVMatrix", mat_req),
m_light0Pos("gcu_Light0Pos", lightPos),
m_staticColor(
"gcu_StaticColor",
Vec4f(0, 0, 0, 1)),
m_initialized(false)
{
m_st.manage(m_pmvMat);
m_st.manage(m_light0Pos);
m_st.manage(m_staticColor);
}
constexpr bool initialized() const noexcept {
return m_initialized; }
bool init(
GL& gl,
const jau::fraction_timespec& when) {
ShaderCodeSRef rsVp = ShaderCode::create(gl, GL_VERTEX_SHADER,
"demos/glsl",
"demos/glsl/bin", "SingleLight0");
ShaderCodeSRef rsFp = ShaderCode::create(gl, GL_FRAGMENT_SHADER,
"demos/glsl",
"demos/glsl/bin", "SingleLight0");
if( !rsVp || !rsFp ) {
return false;
}
{
std::string custom = "#define MAX_TEXTURE_UNITS 0\n";
size_t vsPos = rsVp->defaultShaderCustomization(gl, true, true);
size_t fsPos = rsFp->defaultShaderCustomization(gl, true, true);
rsVp->insertShaderSource(0, vsPos, custom);
rsFp->insertShaderSource(0, fsPos, custom);
}
if( !sp0->add(gl, rsVp, true) || !sp0->add(gl, rsFp, true) ) {
sp0->destroy(gl);
return false;
}
m_st.attachShaderProgram(gl, sp0, true);
pmv.getP().loadIdentity();
pmv.getMv().loadIdentity();
m_st.send(gl, m_pmvMat);
m_st.send(gl, m_light0Pos);
m_st.send(gl, m_staticColor);
m_initialized = sp0->inUse();
if( !m_initialized ) {
m_st.destroy(gl);
}
return m_initialized;
}
m_st.destroyShaderProgram(gl);
}
m_st.useProgram(gl, on);
}
m_st.send(gl, m_staticColor);
}
m_st.send(gl, m_pmvMat);
}
m_st.send(gl, m_pmvMat);
m_st.send(gl, m_staticColor);
}
const PMVMat4f&
pmv() const noexcept {
return m_pmvMat.pmv(); }
const Vec4f&
color() const noexcept {
return m_staticColor.vec4f(); }
void setColor(
const Vec4f& c)
noexcept { m_staticColor.vec4f()=c; }
ShaderState&
st() noexcept {
return m_st; }
const ShaderState&
st() const noexcept {
return m_st; }
const GLUniformVec3f&
lightPosition() const noexcept {
return m_light0Pos; }
};
public:
typedef jau::darray<uint32_t, glmemsize_t>
u32buffer_t;
private:
GraphRenderer& m_renderer;
GLUtilTesselator::SegmentList m_segments;
public:
: m_renderer(renderer),
m_array(
GLFloatArrayDataServer::createGLSLInterleaved(m_renderer.arrayCompsPerElement(), false, 256, GL_STATIC_DRAW)) {
m_array->addGLSLSubArray("gca_Vertex", 3, GL_ARRAY_BUFFER);
if( m_renderer.usesNormal() ) {
m_array->addGLSLSubArray("gca_Normal", 3, GL_ARRAY_BUFFER);
}
m_renderer.st().manage(m_array);
}
constexpr bool initialized() const noexcept {
return m_renderer.initialized(); }
m_renderer.st().destroyAllData(gl);
}
void seal(
GL& gl,
bool seal_) {
return;
}
m_array->seal(gl, seal_);
m_array->enableBuffer(gl, false);
}
public:
m_array->clear(gl);
m_segments.clear();
}
return;
}
if( Graph::DEBUG_MODE ) {
jau_PLAIN_PRINT(
true,
"add.0 segments:\n%s", GLUtilTesselator::Segment::toString(
"- ", m_segments) );
}
{
GLUtilTesselator glutess( GLUtilTesselator::FLAG_NORMAL, *m_array);
GLUtilTesselator::SegmentList segs = glutess.tesselate(shape);
m_segments.insert(m_segments.cend(), segs.cbegin(), segs.cend());
}
if( Graph::DEBUG_MODE ) {
jau_PLAIN_PRINT(
true,
"add.x segments:\n%s", GLUtilTesselator::Segment::toString(
"- ", m_segments) );
}
}
static bool once = true;
if (once) {
once = false;
}
return;
}
m_renderer.useProgram(gl, true);
m_array->enableBuffer(gl, true);
::glEnable(GL_BLEND);
::glBlendEquation(GL_FUNC_ADD);
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for(const GLUtilTesselator::Segment& s : m_segments ) {
::glDrawArrays(s.type, s.first, s.count);
}
m_array->enableBuffer(gl, false);
}
};
private:
float m_zOffset = 0.0f;
public:
constexpr const
Vec3f&
position() const noexcept {
return m_position; }
constexpr const float&
zOffset() const noexcept {
return m_zOffset; }
constexpr float&
zOffset() noexcept {
return m_zOffset; }
constexpr const Quat4f&
rotation() const noexcept {
return m_rotation; }
constexpr const Vec3f&
scale() const noexcept {
return m_scale; }
constexpr Vec3f&
scale() noexcept {
return m_scale; }
bool hasPos = !m_position.is_zero();
bool hasRotate = !m_rotation.isIdentity();
bool hasRotPivot = false;
{
bool sameScaleRotatePivot = hasScale && hasRotate && ( !hasRotPivot || m_rotPivot == ctr );
if( sameScaleRotatePivot ) {
iMatIdent = false;
iMat.setToTranslation(m_position);
iMat.translate(
Vec3f(ctr).mul(m_scale));
iMat.rotate(m_rotation);
iMat.scale(m_scale);
iMat.translate(-ctr);
} else if( hasRotate || hasScale ) {
iMatIdent = false;
iMat.setToTranslation(m_position);
if( hasRotate ) {
if( hasRotPivot ) {
iMat.translate(
Vec3f(m_rotPivot).mul(m_scale));
iMat.rotate(m_rotation);
iMat.translate(
Vec3f(-m_rotPivot).mul(m_scale));
} else {
iMat.translate(
Vec3f(ctr).mul(m_scale));
iMat.rotate(m_rotation);
iMat.translate(
Vec3f(-ctr).mul(m_scale));
}
}
if( hasScale ) {
iMat.translate(
Vec3f(ctr).mul(m_scale));
iMat.scale(m_scale);
iMat.translate(
Vec3f(-ctr).mul(m_scale));
}
} else if( hasPos ) {
iMatIdent = false;
iMat.setToTranslation(m_position);
} else {
iMatIdent = true;
iMat.loadIdentity();
}
}
}
};
typedef std::shared_ptr<Shape>
ShapeRef;
typedef std::shared_ptr<Ship>
ShipRef;
private:
std::vector<OutlineShape> m_oshapes;
AABBox3f m_bounds;
SpatialState m_sstate;
SpatialState m_sstateOrigin;
GraphRenderer& m_renderer;
GraphRegion m_region;
protected:
bool iMatIdent = true;
bool iMatDirty = false;
struct Private{ explicit Private() = default; };
iMatDirty = true;
m_sstate = m_sstateOrigin;
}
public:
Shape(Private, GraphRenderer &renderer)
: m_renderer(renderer), m_region(m_renderer)
{ }
virtual ~Shape() noexcept = default;
return std::make_shared<Shape>(Private(), renderer);
}
m_region.destroy(gl);
}
m_bounds.reset();
m_region.clear(gl);
iMatDirty = false;
m_sstate = SpatialState();
}
const SpatialState&
spatialState() const noexcept {
return m_sstate; }
const AABBox3f&
bounds() const noexcept {
return m_bounds; }
constexpr const Vec3f&
position() const noexcept {
return m_sstate.position(); }
constexpr Vec3f&
position() noexcept { iMatDirty=
true;
return m_sstate.position(); }
constexpr void set_position(
Vec3f new_pos)
noexcept { m_sstate.position() = new_pos; }
constexpr const float&
zOffset() const noexcept {
return m_sstate.zOffset(); }
constexpr float&
zOffset() noexcept { iMatDirty=
true;
return m_sstate.zOffset(); }
constexpr const Quat4f&
rotation() const noexcept {
return m_sstate.rotation(); }
constexpr Quat4f&
rotation() noexcept { iMatDirty=
true;
return m_sstate.rotation(); }
constexpr const Vec3f&
rotationPivot() const noexcept {
return m_sstate.rotationPivot(); }
constexpr Vec3f&
rotationPivot() noexcept { iMatDirty=
true;
return m_sstate.rotationPivot(); }
constexpr Vec3f&
scale() noexcept { iMatDirty=
true;
return m_sstate.
scale(); }
constexpr const std::vector<OutlineShape>&
outlineShapes() const noexcept {
return m_oshapes; }
constexpr std::vector<OutlineShape>&
outlineShapes() noexcept {
return m_oshapes; }
const Vec4f&
color() const noexcept {
return m_color; }
m_bounds.reset();
m_region.clear(gl);
for(OutlineShape& o : m_oshapes){
m_region.addOutlineShape(o);
m_bounds.resize(o.bounds());
}
m_region.seal(gl, true);
m_sstateOrigin = m_sstate;
}
applyMatToMv(pmv);
m_renderer.setColor(m_color);
m_renderer.updateAll(gl);
m_region.draw(gl);
}
virtual void tick(
float ) { }
private:
void applyMatToMv(
PMVMat4f& pmvMat)
noexcept {
if( iMatDirty ) {
m_sstate.updateMat(m_bounds.center(), iMat, iMatIdent);
iMatDirty = false;
}
if( !iMatIdent ) {
pmvMat.mulMv(iMat);
}
}
};
private:
float m_velo;
public:
constexpr float veloScalar() const noexcept {
return m_velo; }
void setVelo(
float v)
noexcept { m_velo = v; }
void addVelo(
float v)
noexcept { m_velo += v; }
};
private:
public:
const Vec3f&
velo() const noexcept {
return m_velo; }
}
}
};
private:
float m_zAxisDirRotationOffset = 0;
VeloEasy m_velo;
public:
Ship(Shape::Private, GraphRenderer &renderer)
{ }
return std::make_shared<Ship>(Shape::Private(), renderer);
}
}
m_velo = VeloEasy();
}
m_velo = VeloEasy();
}
}
void setVelo(
float v)
noexcept { m_velo.setVelo(v); }
void addVelo(
float v)
noexcept { m_velo.addVelo(v); }
void tick(
float dt)
override {
iMatDirty = true;
}
}
};
private:
constexpr static float zNear= 1.0f;
constexpr static float zFar =100.0f;
ShaderState m_st;
bool m_initialized;
bool m_animating = true;
jau::fraction_timespec m_tlast;
GraphRenderer m_renderer;
std::vector<ShapeRef> m_shapes;
bool m_once = true;
GLUniformSyncPMVMat4f m_pmvMat;
GLUniformVec3f m_light0Pos;
GLUniformVec4f m_staticColor;
public:
m_initialized(false),
m_renderer(m_st),
m_ship1(Ship::createShared(m_renderer)),
m_light0Pos("gcu_Light0Pos", m_renderer.lightPosition().vec3f()),
m_staticColor(
"gcu_StaticColor",
Vec4f(0.05f, 0.05f, 0.5f, 1))
{
m_st.manage(m_pmvMat);
m_st.manage(m_light0Pos);
m_st.manage(m_staticColor);
}
std::vector<ShapeRef>&
shapes() noexcept {
return m_shapes; }
bool animating() const noexcept {
return m_animating; }
bool&
animating() noexcept {
return m_animating; }
bool init(
const WindowSRef& win,
const jau::fraction_timespec& when)
override {
m_tlast = when;
if( !m_renderer.init(gl, when) ) {
win->dispose(when);
return false;
}
m_ship1->clear(gl);
m_ship1->setColor(
Vec4f(0.05f, 0.05f, 0.5f, 1.0f));
m_ship1->update(gl);
m_shapes.push_back(m_ship1);
if ( false ) {
m_shapes.push_back(frontShape);
std::vector<OutlineShape>& oshapes = frontShape->
outlineShapes();
for(OutlineShape& o : oshapes){
OutlineShape back = o.flipFace();
oshapes.push_back(back);
}
frontShape->
scale().
x *= 2.0f;
frontShape->
scale().
y *= 2.0f;
}
::glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
::glEnable(GL_DEPTH_TEST);
::glDisable(GL_CULL_FACE);
m_initialized = true;
if( !m_initialized ) {
m_st.destroy(gl);
win->dispose(when);
}
return m_initialized;
}
s->destroy(gl);
}
m_ship1 = nullptr;
m_renderer.destroy(gl);
m_initialized = false;
}
const float aspect = 1.0f;
const float fovy_deg=45.0f;
const float aspect2 = ( (float) m_viewport.width() / (float) m_viewport.height() ) / aspect;
m_st.useProgram(gl, true);
}
static bool once = true;
if (once) {
jau_fprintf_td2(when.
to_ms(), stdout,
"RL::display: %s, initialized %s, shapes %zu, %s\n",
toString(), m_initialized, m_shapes.size(), win->toString());
once = false;
}
if( !m_initialized ) {
return;
}
::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_st.useProgram(gl, true);
const float dt = float( (when - m_tlast).to_double() );
if( m_animating ) {
s->tick(dt);
}
s->draw(gl);
}
if( m_once ) {
m_once = false;
}
m_tlast = when;
}
std::string
toStringImpl() const noexcept
override {
return "GraphShapes01"; }
};
private:
class MyKeyListener : public KeyListener {
private:
public:
MyKeyListener(
Game& p) : m_parent(p) {}
void keyPressed(KeyEvent& e, [[maybe_unused]] const KeyboardTracker& kt) override {
if( e.
keySym() == VKeyCode::VK_ESCAPE ) {
if( win ) {
}
}
else if( e.
keySym() == VKeyCode::VK_R) {
m_parent.ship1()->reset();
}
else if( e.
keySym() == VKeyCode::VK_PAUSE || e.
keySym() == VKeyCode::VK_P ) {
m_parent.animating() = !m_parent.animating();
}
else if( e.
keySym() == VKeyCode::VK_W ) {
}
else if( e.
keySym() == VKeyCode::VK_UP ) {
}
else if( e.
keySym() == VKeyCode::VK_DOWN ) {
}
else if( e.
keySym() == VKeyCode::VK_RIGHT) {
}
else if( e.
keySym() == VKeyCode::VK_LEFT ) {
}
else if( e.
keySym() == VKeyCode::VK_A ) {
m_parent.ship1()->addVelo(0.1f);
}
else if( e.
keySym() == VKeyCode::VK_S ) {
m_parent.ship1()->addVelo(-0.1f);
}
}
void keyReleased([[maybe_unused]] KeyEvent& e, [[maybe_unused]] const KeyboardTracker& kt) override {
}
};
typedef std::shared_ptr<MyKeyListener> MyKeyListenerRef;
MyKeyListenerRef m_kl;
public:
m_kl(
std::make_shared<MyKeyListener>(*this)) { }
bool init(
const WindowSRef& win,
const jau::fraction_timespec& when)
override {
return false;
}
win->addKeyListener(m_kl);
return true;
}
win->removeKeyListener(m_kl);
}
};
int main(
int argc,
char *argv[])
{
return launch(
"PrimitivesCobraMK3.cpp",
std::make_shared<Example>(), argc, argv);
}
int launch(std::string_view sfile, GLLaunchProps props, const RenderListenerSRef &demo, int argc, char *argv[])
int main(int argc, char *argv[])
std::shared_ptr< Shape > ShapeRef
std::shared_ptr< Ship > ShipRef
std::shared_ptr< Shape > ShapeRef
void dispose(const WindowSRef &win, const jau::fraction_timespec &when) override
Notifies the listener to perform the release of all renderer resources per context,...
Recti & viewport() noexcept
bool init(const WindowSRef &win, const jau::fraction_timespec &when) override
Called by the drawable immediately after the render context is initialized.
void dispose(const WindowSRef &win, const jau::fraction_timespec &when) override
Notifies the listener to perform the release of all renderer resources per context,...
ShipRef & ship1() noexcept
std::vector< ShapeRef > & shapes() noexcept
bool animating() const noexcept
void display(const WindowSRef &win, const jau::fraction_timespec &when) override
Called by the drawable to initiate rendering by the client.
std::string toStringImpl() const noexcept override
static void addShapeToRegion(gamp::graph::OutlineShape &shape)
constexpr bool initialized() const noexcept
void addOutlineShape(OutlineShape &shape)
jau::darray< uint32_t, glmemsize_t > u32buffer_t
GraphRegion(GraphRenderer &renderer, ShaderState &st)
void seal(GL &gl, bool seal_)
ShaderState & st() noexcept
GLUniformVec3f & lightPosition() noexcept
constexpr GLsizei arrayCompsPerElement() const noexcept
const Vec4f & color() const noexcept
constexpr bool initialized() const noexcept
constexpr bool usesNormal() const noexcept
PMVMat4f & pmv() noexcept
GraphRenderer(ShaderState &st)
void useProgram(GL &gl, bool on)
void setColor(const Vec4f &c) noexcept
constexpr const float & zOffset() const noexcept
void setColor(const Vec4f &c) noexcept
void tick(float dt)
Game ..
void resetSpatial() noexcept
static ShapeRef createShared(ShaderState &st, GraphRenderer &renderer)
constexpr const std::vector< OutlineShape > & outlineShapes() const noexcept
constexpr const Quat4f & rotation() const noexcept
constexpr void set_position(Vec3f new_pos) noexcept
const AABBox3f & bounds() const noexcept
Shape(Private, ShaderState &st, GraphRenderer &renderer)
constexpr const Vec3f & rotationPivot() const noexcept
virtual void clear(GL &gl)
constexpr const Vec3f & position() const noexcept
const Vec4f & color() const noexcept
constexpr const Vec3f & scale() const noexcept
const SpatialState & spatialState() const noexcept
virtual ~Shape() noexcept=default
Vec3f getDir() const noexcept
void setVelo(float v) noexcept
Vec3f velo() const noexcept
Ship(Shape::Private, GraphRenderer &renderer)
void destroy(GL &gl) override
static ShipRef createShared(GraphRenderer &renderer)
void tick(float dt) override
void addVelo(float v) noexcept
void setOriginZRotation(float v) noexcept
Set z-axis rotation offset of movement orientation.
void clear(GL &gl) override
A Generic shape objects which is defined by a list of Outlines.
const OutlineList & outlines() const noexcept
std::string toString() const noexcept
Define a single continuous stroke by control vertices.
Specifies a set of OpenGL capabilities.
static GLContext & downcast(RenderContext *rc)
Downcast dereferenced given RenderContext* to GLContext&, throws exception if signature doesn't match...
Specifies the OpenGL profile.
static constexpr std::string_view GLES2
The embedded OpenGL profile ES 2.x, with x >= 0.
constexpr RenderListener(Private) noexcept
Private ctor for shared_ptr<RenderListener> instance method w/o public ctor.
std::string toString() const noexcept
std::string toString() const noexcept
constexpr VKeyCode keySym() const noexcept
Returns the virtual key symbol reflecting the current keyboard layout.
constexpr const WindowWeakPtr & source() const noexcept
constexpr const jau::fraction_timespec & when() const noexcept
constexpr size_type size() const noexcept
Like std::vector::size().
constexpr Matrix4 & loadIdentity() noexcept
Set this matrix to identity.
constexpr value_type length() const noexcept
Return the length of a vector, a.k.a the norm or magnitude
constexpr Vector3F & scale(const value_type s) noexcept
this = this * s, component wise.
static constexpr const value_type one
constexpr Mat4 & getP() noexcept
Returns the projection matrix (P).
constexpr PMVMatrix4 & translateMv(float x, float y, float z) noexcept
Translate the modelview matrix.
constexpr_cxx20 PMVMatrix4 & popMv() noexcept
Pop the modelview matrix from its stack.
PMVMatrix4 & perspectiveP(const float fovy_rad, const float aspect, const float zNear, const float zFar)
Multiply the projection matrix with the perspective/frustum matrix.
constexpr Mat4 & getMv() noexcept
Returns the modelview matrix (Mv).
constexpr_cxx20 PMVMatrix4 & pushMv() noexcept
Push the modelview matrix to its stack, while preserving its values.
#define jau_PLAIN_PRINT(printPrefix, fmt,...)
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
#define jau_fprintf_td2(elapsed_ms, stream, fmt,...)
std::shared_ptr< Shape > ShapeRef
@ dir
Type: Entity is a directory, might be in combination with link.
constexpr T PI
Alias for π or half-circle radians (180 degrees), i.e. std::numbers::pi_v<T>
constexpr T PI_4
Alias for π/4 or half right-angle radians (45 degrees), i.e. std::numbers::pi_v<T>/T(4)
constexpr T adeg_to_rad(const T arc_degree) noexcept
Converts arc-degree to radians.
constexpr T PI_2
Alias for π/2 or right-angle radians (90 degrees), i.e. std::numbers::pi_v<T>/T(2)
constexpr bool is_zero(const T &a, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if the given value is less than epsilon, w/ epsilon > 0.
@ std
Denotes a func::std_target_t.
GLArrayDataServer< float > GLFloatArrayDataServer
GLArrayDataServerSRef< float > GLFloatArrayDataServerSRef
std::shared_ptr< ShaderCode > ShaderCodeSRef
std::shared_ptr< ShaderProgram > ShaderProgramSRef
@ verbose
Verbose operations (debugging).
std::shared_ptr< Window > WindowSRef
constexpr orientation_t orientation(const Point2f &a, const Point2f &b, const Point2f &c) noexcept
Return the orientation of the given point triplet a, b and c using triArea()
PMVData
PMVMatrix4 derived matrices and values.
Quaternion< float > Quat4f
PMVMatrix4< float > PMVMat4f
@ inv_mv
Bit value for inverse modelview matrix (Mvi), updated via update().
@ inv_proj
Bit value for inverse projection matrix (Pi), updated via update().
@ inv_tps_mv
Bit value for inverse transposed modelview matrix (Mvit), updated via update().
Gamp: Graphics, Audio, Multimedia and Processing Framework (Native C++, WebAssembly,...
ssize_t fprintf_td(const uint64_t elapsed_ms, FILE *stream, std::string_view format, const Args &...args) noexcept
Convenient secure fprintf() invocation, prepending the given elapsed_ms timestamp and using jau:forma...
void appendCobraMkIII(std::vector< OutlineShape > &oshapes, const float height=1.0f, const float width=2.0f, const float deep=0.3f)
bool m_hasFrustumClipping
constexpr const float & zOffset() const noexcept
SpatialState() noexcept=default
constexpr const Quat4f & rotation() const noexcept
constexpr const Vec3f & position() const noexcept
void updateMat(const Vec3f &ctr, Mat4f &iMat, bool &iMatIdent) const noexcept
constexpr void set_position(Vec3f new_pos) noexcept
constexpr const Vec3f & rotationPivot() const noexcept
constexpr const Vec3f & scale() const noexcept
void setVelo(float v) noexcept
const Vec3f velo(const Vec3f &dir) const noexcept
constexpr float veloScalar() const noexcept
void addVelo(float v) noexcept
void addVelo(Vec3f dir, float v) noexcept
void setVelo(Vec3f dir, float v) noexcept
const Vec3f & velo() const noexcept
constexpr float veloScalar() const noexcept
constexpr uint64_t to_ms() const noexcept
Returns time in milliseconds.
int printf(const char *format,...)
Operating Systems predefined macros.