22 APR 2026

rahulmnavneeth

scene

homedocs

Location

include/mop/core/scene.h   — Public mesh + scene API
src/core/viewport.c        — Pointer-stable mesh pool + auto-lock setters

The Pointer-Stable Mesh Pool

vp->meshes is an array of MopMesh *, not an array of MopMesh structs. Each MopMesh is heap-allocated individually; growing the pointer array via realloc does not move the structs. Consequences:

The same design applies to MopInstancedMesh *.

Types

Handles

typedef struct MopMesh          MopMesh;
typedef struct MopInstancedMesh MopInstancedMesh;

MopMeshDesc

typedef struct MopMeshDesc {
    const MopVertex      *vertices;
    uint32_t              vertex_count;
    const uint32_t       *indices;
    uint32_t              index_count;
    uint32_t              object_id;         /* 1 .. 0xFFFCFFFF */
    const MopVertexFormat *vertex_format;    /* NULL = standard MopVertex */
} MopMeshDesc;

MopMeshDescEx

Flexible-format variant — supply raw interleaved bytes plus an explicit format descriptor. Required for meshes with joints/weights or custom attribute layouts.

typedef struct MopMeshDescEx {
    const void           *vertex_data;
    uint32_t              vertex_count;
    const uint32_t       *indices;
    uint32_t              index_count;
    uint32_t              object_id;
    const MopVertexFormat *vertex_format;    /* required */
} MopMeshDescEx;

Functions

Lifecycle

MopMesh *mop_viewport_add_mesh   (MopViewport *vp, const MopMeshDesc *desc);
MopMesh *mop_viewport_add_mesh_ex(MopViewport *vp, const MopMeshDescEx *desc);
void     mop_viewport_remove_mesh(MopViewport *vp, MopMesh *mesh);
void     mop_mesh_update_geometry(MopMesh *mesh, MopViewport *vp,
                                  const MopVertex *v, uint32_t vc,
                                  const uint32_t *idx, uint32_t ic);

Vertex / index data is copied on add_mesh; callers may free their arrays immediately. update_geometry re-uploads and reallocates GPU buffers if the counts changed.

Transform (TRS — preferred)

void    mop_mesh_set_position(MopMesh *m, MopVec3 position);
void    mop_mesh_set_rotation(MopMesh *m, MopVec3 rotation); /* euler rad, Rz*Ry*Rx */
void    mop_mesh_set_scale   (MopMesh *m, MopVec3 scale);
MopVec3 mop_mesh_get_position(const MopMesh *m);
MopVec3 mop_mesh_get_rotation(const MopMesh *m);
MopVec3 mop_mesh_get_scale   (const MopMesh *m);

Transform (matrix — power user)

void mop_mesh_set_transform(MopMesh *m, const MopMat4 *transform);

TRS is faster and integrates with the gizmo and undo system. Use the matrix form only when you have a transform that can't be decomposed cleanly.

Hierarchy

void mop_mesh_set_parent  (MopMesh *m, MopMesh *parent, MopViewport *vp);
void mop_mesh_clear_parent(MopMesh *m);

Cycles are rejected. Transforms propagate top-down in a single pass.

Opacity / blend

void mop_mesh_set_opacity   (MopMesh *m, float opacity);      /* [0, 1]            */
void mop_mesh_set_blend_mode(MopMesh *m, MopBlendMode mode);  /* OPAQUE / ALPHA /  */
                                                              /* ADDITIVE / MULT.  */

Textures

MopTexture *mop_viewport_create_texture(MopViewport *vp, int w, int h,
                                        const uint8_t *rgba_data);
void        mop_viewport_destroy_texture(MopViewport *vp, MopTexture *t);
void        mop_mesh_set_texture(MopMesh *m, MopTexture *tex);

For the richer streaming / compressed / async path, see Texture Pipeline.

Shading override

void mop_mesh_set_shading(MopMesh *m, MopShadingMode mode); /* -1 = inherit */

Skinning

Requires a flexible vertex format with MOP_ATTRIB_JOINTS and MOP_ATTRIB_WEIGHTS.

void mop_mesh_set_bone_hierarchy(MopMesh *m, const int32_t *parent_indices,
                                 uint32_t bone_count);
void mop_mesh_set_bone_matrices (MopMesh *m, MopViewport *vp,
                                 const MopMat4 *matrices, uint32_t bone_count);

First set_bone_matrices call records the bind pose. Subsequent calls animate bind-relative.

Morph targets

void mop_mesh_set_morph_targets(MopMesh *m, MopViewport *vp,
                                const float *targets,   /* target_count * vc * 3 */
                                const float *weights,   /* target_count floats   */
                                uint32_t target_count);
void mop_mesh_set_morph_weights(MopMesh *m,
                                const float *weights, uint32_t target_count);

Instanced meshes

MopInstancedMesh *mop_viewport_add_instanced_mesh(MopViewport *vp,
                                                  const MopMeshDesc *desc,
                                                  const MopMat4 *transforms,
                                                  uint32_t instance_count);
void mop_instanced_mesh_update_transforms(MopInstancedMesh *m,
                                          const MopMat4 *transforms,
                                          uint32_t count);
void mop_viewport_remove_instanced_mesh(MopViewport *vp, MopInstancedMesh *m);

LOD

#define MOP_MAX_LOD_LEVELS 8

int32_t mop_mesh_add_lod(MopMesh *m, MopViewport *vp,
                         const MopMeshDesc *desc, float screen_threshold);
void    mop_viewport_set_lod_bias(MopViewport *vp, float bias);
float   mop_viewport_get_lod_bias(const MopViewport *vp);

screen_threshold is the projected pixel diameter below which the next-lower LOD is selected. Higher bias ⇒ lower detail globally.

Debug visualization

void       mop_viewport_set_debug_viz(MopViewport *vp, MopDebugViz mode);
MopDebugViz mop_viewport_get_debug_viz(const MopViewport *vp);

Modes: MOP_DEBUG_VIZ_NONE, OVERDRAW, SHADOW_CASCADES, LOD_LEVEL, CULL_RESULT, DEPTH, NORMALS, MIPMAP.

Object IDs

Application-defined object_id must be in [1, 0xFFFCFFFF]. The top range [0xFFFD0000, 0xFFFFFFFF] is reserved for chrome (gizmo handles, grid, lights). 0 indicates "no object" in the pick buffer.

See Also