Docs

Mesh

Description

Mesh is the base class for all renderable 3D objects. It exposes the material system (color, texture, PBR material), shadow casting and receiving, transparency control, face culling, automatic detail levels, and GPU instancing (including distance fade and per-view instance culling) for drawing thousands of objects in a single draw call.

Inherits: Object → EntityHandle

#include "Doriax.h"
using namespace doriax;

Mesh mesh(&scene);
mesh.setTexture("textures/crate.png");
mesh.setColor(1.0f, 1.0f, 1.0f, 1.0f);
mesh.setCastShadows(true);
local mesh = Mesh(scene)
mesh:setTexture("textures/crate.png")
mesh:setColor(1, 1, 1, 1)
mesh.castShadows = true

Constructors

Available in C++ and Lua:

  • Mesh(Scene* scene) — creates a new entity.
  • Mesh(Scene* scene, Entity entity) — wraps an existing entity without taking ownership.

In Lua, use Mesh(scene, entity) to access an existing mesh. The entity must already have the components required by this type; wrapping does not add them. See EntityHandle ownership.

Properties

Type Name Default Languages
Vector4 color (1,1,1,1) C++ | Lua
float alpha 1.0 C++ | Lua
Material material default C++ | Lua
PrimitiveType primitiveType TRIANGLES C++ | Lua
bool faceCulling true C++ | Lua
CullingMode cullingMode BACK C++ | Lua
WindingOrder windingOrder CCW C++ | Lua
bool receiveLights true C++ | Lua
bool receiveIBL false C++ | Lua
bool castShadows true C++ | Lua
bool receiveShadows true C++ | Lua
bool lodEnabled true C++ | Lua
float lodBias 1.0 C++ | Lua
bool shadowsBillboard true C++ | Lua
bool renderInReflectionProbes true C++ | Lua
bool castShadowsWithTexture false C++ | Lua
bool transparent false C++ | Lua
bool autoTransparency true C++ | Lua
string customShader "" C++ | Lua
string customDepthShader "" C++ | Lua
unsigned int maxInstances 100 C++ | Lua
bool instancedBillboard false C++ | Lua
bool instancedCylindricalBillboard false C++ | Lua
bool distanceFade false C++ | Lua
float fadeStart 0 C++ | Lua
float fadeEnd 0 C++ | Lua
bool cullInstances true C++ | Lua
float cullDistance 0 C++ | Lua

Methods

Returns Name Languages
bool load C++ | Lua
void setTexture C++ | Lua
void setColor C++ | Lua
Vector4 getColor C++ | Lua
void setAlpha C++ | Lua
float getAlpha C++ | Lua
void setMaterial C++ | Lua
Material getMaterial C++ | Lua
void setPrimitiveType C++ | Lua
PrimitiveType getPrimitiveType C++ | Lua
void setFaceCulling C++ | Lua
bool isFaceCulling C++ | Lua
void setCullingMode C++ | Lua
CullingMode getCullingMode C++ | Lua
void setWindingOrder C++ | Lua
WindingOrder getWindingOrder C++ | Lua
AABB getAABB C++ | Lua
AABB getVerticesAABB C++ | Lua
AABB getWorldAABB C++ | Lua
unsigned int getNumSubmeshes C++ | Lua
void setReceiveLights C++ | Lua
bool isReceiveLights C++ | Lua
void setCastShadows C++ | Lua
bool isCastShadows C++ | Lua
void setReceiveShadows C++ | Lua
bool isReceiveShadows C++ | Lua
void setLodEnabled C++ | Lua
bool isLodEnabled C++ | Lua
void setLodBias C++ | Lua
float getLodBias C++ | Lua
void setShadowsBillboard C++ | Lua
bool isShadowsBillboard C++ | Lua
void setCastShadowsWithTexture C++ | Lua
bool isCastShadowsWithTexture C++ | Lua
void setTransparent C++ | Lua
bool isTransparent C++ | Lua
void setAutoTransparency C++ | Lua
bool isAutoTransparency C++ | Lua
void setCustomShader C++ | Lua
string getCustomShader C++ | Lua
void setCustomDepthShader C++ | Lua
string getCustomDepthShader C++ | Lua
void setShaderUniform C++ | Lua
Vector4 getShaderUniform C++ | Lua
bool removeShaderUniform C++ | Lua
void setAsMirror C++ | Lua
void removeMirror C++ | Lua
bool isMirror C++ | Lua
void createInstancedMesh C++ | Lua
void removeInstancedMesh C++ | Lua
bool hasInstancedMesh C++ | Lua
void addInstance C++ | Lua
InstanceData& getInstance C++ | Lua
void updateInstance C++ | Lua
void removeInstance C++ | Lua
bool isInstanceVisible C++ | Lua
void setInstanceVisible C++ | Lua
void updateInstances C++ | Lua
size_t getNumInstances C++ | Lua
void clearInstances C++ | Lua
void setInstancedBillboard C++ | Lua
bool isInstancedBillboard C++ | Lua
void setInstancedCylindricalBillboard C++ | Lua
bool isInstancedCylindricalBillboard C++ | Lua
void setMaxInstances C++ | Lua
unsigned int getMaxInstances C++ | Lua
void setDistanceFade C++ | Lua
bool isDistanceFade C++ | Lua
void setFadeRange C++ | Lua
void setFadeStart C++ | Lua
float getFadeStart C++ | Lua
void setFadeEnd C++ | Lua
float getFadeEnd C++ | Lua
void setCullInstances C++ | Lua
bool isCullInstances C++ | Lua
void setCullDistance C++ | Lua
float getCullDistance C++ | Lua

Enumerations

PrimitiveType

  • TRIANGLES — Solid triangle meshes (default).
  • TRIANGLE_STRIP — Triangle strip.
  • POINTS — Renders each vertex as a point.
  • LINES — Renders edges as lines.

CullingMode

  • BACK — Culls back faces (default, outward-facing normals).
  • FRONT — Culls front faces (useful for inside-out rendering).

WindingOrder

  • CCW — Counter-clockwise winding defines the front face (OpenGL convention, default).
  • CW — Clockwise winding defines the front face.

Property details

color

  • Setter: void setColor(Vector4 color)
  • Setter: void setColor(float red, float green, float blue, float alpha)
  • Setter: void setColor(float red, float green, float blue)
  • Getter: Vector4 getColor() const

Tint color multiplied with the material texture. (1,1,1,1) means no tint.


alpha

  • Setter: void setAlpha(float alpha)
  • Getter: float getAlpha() const

Shorthand for the alpha channel of color. Values below 1.0 require transparent to be enabled for correct rendering order.


material

  • Setter: void setMaterial(const Material& material) (all submeshes)
  • Setter: void setMaterial(unsigned int submesh, const Material& material) (per submesh)
  • Getter: Material getMaterial() const
  • Getter: Material getMaterial(unsigned int submesh) const

PBR material descriptor. Contains base color, metallic/roughness factors, normal map, emissive settings, and shader overrides.


primitiveType

  • Setter: void setPrimitiveType(PrimitiveType primitiveType)
  • Getter: PrimitiveType getPrimitiveType() const

GPU draw mode. Overloads are available per submesh.


faceCulling

  • Setter: void setFaceCulling(bool faceCulling)
  • Getter: bool isFaceCulling() const

Enables or disables back-face culling globally for this mesh. Disable for double-sided geometry (e.g. leaves, cards): back faces are then lit with their normal flipped toward the viewer, as glTF double-sided materials are.


castShadows / receiveShadows

  • Setter: void setCastShadows(bool castShadows) / void setReceiveShadows(bool receiveShadows)
  • Getter: bool isCastShadows() const / bool isReceiveShadows() const

Shadow participation flags. Enabling both adds depth-map cost; disable on distant or small objects for performance.


lodEnabled / lodBias

  • lodEnabled Setter: void setLodEnabled(bool lodEnabled)
  • lodEnabled Getter: bool isLodEnabled() const
  • lodBias Setter: void setLodBias(float lodBias)
  • lodBias Getter: float getLodBias() const

Mesh detail levels. lodEnabled (default true) lets this mesh follow the scene's detail levels; turn it off to keep it at full detail, for example a hero asset whose silhouette must never change. Toggling it reloads the mesh. lodBias (default 1) scales how far detail is kept: above 1 keeps the source longer, below 1 drops to a coarser level sooner.

Nothing happens until the scene has meshLodEnabled on, which is off by default. Terrain and tilemaps ignore these flags and keep their own LOD.

mesh.setLodBias(1.5f);       // keep detail longer than the scene threshold
statue.setLodEnabled(false); // always full detail
mesh.lodBias = 1.5         -- keep detail longer than the scene threshold
statue.lodEnabled = false  -- always full detail

renderInReflectionProbes

  • Setter: void setRenderInReflectionProbes(bool renderInReflectionProbes)
  • Getter: bool isRenderInReflectionProbes() const

Whether the mesh is drawn into ReflectionProbe captures. Turn it off when an object surrounds or sits on a probe and would otherwise show up inside its own reflection. Normal rendering is unaffected — this only controls the capture pass.

Changing it re-captures every probe in the scene: a probe that already baked its cubemap keeps the old contents until it captures again.


receiveIBL

  • Setter: void setReceiveIBL(bool receiveIBL)
  • Getter: bool isReceiveIBL() const

When true, the mesh samples image-based lighting: diffuse irradiance plus specular reflections on top of punctual lights. The source is the scene's Sky environment, a Reflection Probe whose volume covers the mesh, or both. Sky lighting needs a Sky entity with a cubemap texture; probes need none. Also requires Receive Lights enabled and surface normals.

Also settable in the editor on the Mesh component (Receive IBL); the flag is serialized with the scene. Metallic, low-roughness materials show the strongest reflections.

Beyond reflective materials, this is what keeps unlit sides of an object from going flat. A surface no light reaches falls back to the scene's global illumination, which is a single flat colour — with a bright white ambient it washes out to a pale grey. With receiveIBL the same surface takes its colour from the sky instead. This shows up most in mirrors, which show the side of an object facing the mirror — usually the side the key light misses.


transparent / autoTransparency

  • transparent Setter: void setTransparent(bool transparent)
  • autoTransparency Setter: void setAutoTransparency(bool autoTransparency)

transparent marks the mesh for the transparent render pass (sorted back-to-front). autoTransparency detects blending from the material (BLEND, or AUTO when the texture or factor is translucent) and enables the transparent pass automatically. A blended submesh keeps depth testing but disables depth writes in the colour pass so overlapping translucent surfaces composite correctly.


customShader

  • Setter: void setCustomShader(const std::string& path)
  • Getter: std::string getCustomShader() const

Project-relative base path of a forked shader used for this mesh's colour pass — "shaders/wave" resolves to shaders/wave.vert and shaders/wave.frag, or "a.vert|b.frag" names the two files separately. Empty (default) uses the scene's defaultMeshShader, or the engine built-in when the scene has none. Changing it reloads the mesh. The engine loads the compiled shader; the sources are only needed by the editor. See Custom Shaders.

Like colour and material, this is per Mesh component: a multi-node glTF Model renders through its part entities, so assign the shader (and its uniforms) on the parts — Mesh(scene, partEntity) — not on the Model root, whose own Mesh is empty unless the model is merged or single-node.

mesh.setCustomShader("shaders/wave");
mesh.customShader = "shaders/wave"

customDepthShader

  • Setter: void setCustomDepthShader(const std::string& path)
  • Getter: std::string getCustomDepthShader() const

Project-relative base path of a forked depth shader — the one the mesh is drawn with into shadow maps, into the optional scene depth prepass, and into the depth pre-pass that feeds SSAO and post-process depth while SSR is off. Same path forms as customShader; empty (default) uses the engine built-in, and there is no scene default. Fork it when the colour fork displaces vertices or discards fragments, so the shadow follows; it reads the same shader uniforms. Changing it reloads the mesh. See Custom Shaders — The Depth Shader row.

mesh.setCustomShader("shaders/wave");
mesh.setCustomDepthShader("shaders/wave_depth");
mesh.customShader = "shaders/wave"
mesh.customDepthShader = "shaders/wave_depth"

maxInstances

  • Setter: void setMaxInstances(unsigned int maxInstances)
  • Getter: unsigned int getMaxInstances() const

Slots in the GPU instance buffer, 100 by default. Drawing clamps to it, but addInstance grows the cap as the list outgrows it — reloading the mesh each time — so the clamp only bites when the instance list is filled without going through addInstance. Set the capacity up front when the count is already known, to skip those reloads. Unlike the billboard setters, this one logs an error if the mesh is not instanced yet — call createInstancedMesh first.


instancedBillboard / instancedCylindricalBillboard

  • Setter: void setInstancedBillboard(bool billboard)
  • Setter: void setInstancedBillboard(bool billboard, bool cylindrical)
  • Getters: bool isInstancedBillboard() const, bool isInstancedCylindricalBillboard() const

Turns every instance to face the camera, the per-instance counterpart of Object.billboard. Both default to false, and instancedCylindricalBillboard only matters while instancedBillboard is on. The setters create the instanced mesh if it does not exist yet.

Cylindrical is not Y-only yet

The instanced path builds its rotation from the inverse view matrix whichever flag is set, so instanced cylindrical facing currently matches the spherical one. Object.cylindricalBillboard does constrain to Y.

grass.setInstancedBillboard(true, true);
grass.instancedBillboard = true
grass.instancedCylindricalBillboard = true

distanceFade / fadeStart / fadeEnd

  • distanceFade Setter: void setDistanceFade(bool distanceFade)
  • distanceFade Getter: bool isDistanceFade() const
  • Setter: void setFadeRange(float fadeStart, float fadeEnd)
  • fadeStart Setter / Getter: void setFadeStart(float fadeStart) / float getFadeStart() const
  • fadeEnd Setter / Getter: void setFadeEnd(float fadeEnd) / float getFadeEnd() const

Instanced-mesh distance fade. When distanceFade is on, instances shrink toward their base between fadeStart and fadeEnd, both model-space distances from the camera. Toggling the flag reloads the mesh (shader variant Ifd); the two distances are uniforms and can change without a reload. Fade End must be greater than Fade Start. Logs an error if the mesh is not instanced yet.

On the built-in triangle shaders, instances past fadeEnd are also omitted from the draw. Custom shaders and the SSR G-buffer still submit them and rely on the vertex scale.

trees.createInstancedMesh();
trees.setDistanceFade(true);
trees.setFadeRange(40.0f, 60.0f);
trees:createInstancedMesh()
trees.distanceFade = true
trees:setFadeRange(40, 60)

cullInstances

  • Setter: void setCullInstances(bool cullInstances)
  • Getter: bool isCullInstances() const

When true (default), instances outside the main camera or a shadow atlas slot are left out of that view. Turn it off when a shader moves instances away from the bounds the CPU computed; the whole entity then skips frustum culling too. Logs an error if the mesh is not instanced yet. See Culling granularity.


cullDistance

  • Setter: void setCullDistance(float distance)
  • Getter: float getCullDistance() const

World-space distance from the main camera past which instances are dropped from the culled views (main camera and shadow slots). 0 (default) means unlimited; negative or non-finite values are ignored. A hard cut, unlike distanceFade: instances keep their size until they vanish, and it applies with custom shaders and SSR too, so hide it with fog. Requires cullInstances. Logs an error if the mesh is not instanced yet.

trees.createInstancedMesh();
trees.setCullDistance(150.0f);
trees:createInstancedMesh()
trees.cullDistance = 150

Method details

load

  • bool load()

Uploads geometry and material resources to the GPU. Must be called after building or modifying geometry programmatically. Subclasses like Sprite call this internally when needed.


setTexture

  • void setTexture(const std::string& path)
  • void setTexture(const std::string& id, TextureData data)
  • void setTexture(Framebuffer* framebuffer)

Assigns the diffuse/base-color texture. Accepts a file path, raw TextureData, or a framebuffer render target.

mesh.setTexture("textures/stone.png");
// or use a render-to-texture framebuffer:
Camera cam(&scene);
cam.setRenderToTexture(true);
mesh.setTexture(cam.getFramebuffer());
mesh:setTexture("textures/stone.png")

setAsMirror / removeMirror / isMirror

  • void setAsMirror() — turn this flat mesh into a planar reflection surface, using the default +Z normal (matching a Wall).
  • void setAsMirror(Vector3 normal) — same, with an explicit surface normal in local space (e.g. Vector3(0, 1, 0) for a createPlane floor).
  • void removeMirror() — stop being a mirror.
  • bool isMirror() const — whether this mesh is currently a mirror.

The engine creates and drives the reflection camera internally and feeds its render target to the mesh's base texture — no camera setup is required. Set receiveLights to false for a reflection shown without surface shading. See Rendering Pipeline — Mirrors.

Shape mirror(&scene);
mirror.createWall(10.0f, 10.0f);
mirror.setAsMirror();
mirror.setReceiveLights(false);
mirror:createWall(10.0, 10.0)
mirror:setAsMirror()
mirror.receiveLights = false

Use these to make an existing mesh reflective. To create a mirror outright, the Mirror class does both steps at once and exposes the surface normal as a property.


setShaderUniform

  • void setShaderUniform(const std::string& name, const Vector4& value)
  • void setShaderUniform(const std::string& name, const Vector3& value)
  • void setShaderUniform(const std::string& name, const Vector2& value)
  • void setShaderUniform(const std::string& name, float value)
  • Vector4 getShaderUniform(const std::string& name) const
  • bool removeShaderUniform(const std::string& name)

Values for the members of the custom shader's u_vs_customParams / u_fs_customParams blocks, keyed by member name (see Custom Shaders — Shader uniforms). A value is stored on the mesh and applied to every submesh — of the colour fork and of the depth fork alike; a narrower value leaves the remaining components at zero, and an int member takes the truncated value. Setting a value only rewrites the uploaded block, so it can be called every frame. Names the shader does not declare are kept but unused, and a member with no value reads zero, or the scene's value while the mesh inherits a scene default shader.

getShaderUniform returns the stored value (zero when unset) — the mesh's own value list, not the uploaded block or the scene's values, so an int member set to 3.7 reads back 3.7 while the shader sees 3, and the engine-written names are not in it. removeShaderUniform drops a value and returns whether it existed; the member reads zero again. Matrix and array members take no value.

Two names are reserved and written by the engine every frame — time (wall-clock seconds since startup, the same clock as Engine.systemTime; it does not stop for a pause) and resolution (xy = size of the render target the mesh is drawn into, zw = 1 / size); setting either logs an error. For a glTF model, call this on the part entities (see customShader).

mesh.setCustomShader("shaders/wave");
mesh.setShaderUniform("amplitude", 0.3f);
mesh.setShaderUniform("direction", Vector2(1.0f, 0.0f));
mesh.setShaderUniform("tint", Vector4(0.2f, 0.5f, 1.0f, 0.5f));

Vector4 tint = mesh.getShaderUniform("tint");
mesh.customShader = "shaders/wave"
mesh:setShaderUniform("amplitude", 0.3)
mesh:setShaderUniform("direction", Vector2(1, 0))
mesh:setShaderUniform("tint", Vector4(0.2, 0.5, 1.0, 0.5))

local tint = mesh:getShaderUniform("tint")

getAABB / getVerticesAABB / getWorldAABB

  • AABB getAABB() const — Axis-aligned bounding box of the mesh in local space.
  • AABB getVerticesAABB() const — AABB computed from raw vertex positions, ignoring the transform.
  • AABB getWorldAABB() const — AABB in world space after applying the full transform chain.

Useful for frustum culling, click picking, and physics proxy fitting.


getNumSubmeshes

  • unsigned int getNumSubmeshes() const

Returns the number of submeshes. Each submesh can have its own material, primitive type, and culling settings.


createInstancedMesh

  • void createInstancedMesh()

Enables GPU instancing for this mesh. After calling this, use addInstance / updateInstance to populate the instance list, then updateInstances to flush changes to the GPU.

Instancing draws geometry on this entity only. Multi-node GLTF models that keep child mesh entities need static merge first (or use a single-mesh / procedural mesh).

Mesh tree(&scene);
tree.setTexture("textures/tree.png");
tree.createInstancedMesh();
tree.setMaxInstances(500);

for (int i = 0; i < 500; i++) {
    tree.addInstance(Vector3(i * 2.0f, 0.0f, 0.0f));
}
tree.updateInstances();
local tree = Mesh(scene)
tree:setTexture("textures/tree.png")
tree:createInstancedMesh()
tree.maxInstances = 500

for i = 0, 499 do
    tree:addInstance(Vector3(i * 2, 0, 0))
end
tree:updateInstances()

addInstance

  • void addInstance(InstanceData instance)
  • void addInstance(Vector3 position)
  • void addInstance(float x, float y, float z)
  • void addInstance(Vector3 position, Quaternion rotation, Vector3 scale)
  • void addInstance(Vector3 position, Quaternion rotation, Vector3 scale, Vector4 color)
  • void addInstance(Vector3 position, Quaternion rotation, Vector3 scale, Vector4 color, Rect textureRect)

Appends a new instance with the specified transform, creating the instanced mesh if the entity does not have one yet. Growing past maxInstances doubles the capacity and reloads the mesh.


updateInstance

  • void updateInstance(size_t index, InstanceData instance) (and overloads)

Replaces the data for an existing instance at index. Call updateInstances afterwards to sync changes to the GPU.


updateInstances

  • void updateInstances()

Uploads all pending instance data changes to the GPU vertex buffer. Must be called after any addInstance or updateInstance calls.


clearInstances

  • void clearInstances()

Removes all instances from the instance list. The mesh geometry is preserved; call updateInstances afterwards.