静的シーン (Mesh / Node / Scene)
概要
モデルを「頂点配列、マテリアル、テクスチャ、ノードツリー」の集合として 全て const データ で表し、
putScene() で描くための仕組みです。glTF から bin/gltf2cpp で生成するのが主な用途ですが (gltf2cpp: glTF モデルを C++ コードに変換する)、
手書きすることもできます。
ヒープを使いません。全ての参照は静的記憶域のオブジェクトを指すので、寿命や所有権の問題が生じません。
ノードごとの変換はツリーに埋め込まれていますが、
NodeVisitorで描画時に差し替えたり、部分木を飛ばしたりできます。
構造体
struct Mesh {
const Primitive *primitives; // 各プリミティブは自身のマテリアルを持つ
uint16_t primitiveCount;
};
struct Node {
const char *name; // nullptr でもよい
mat4f transform; // 親からのローカル変換
const Mesh *mesh; // nullptr でもよい
const Node *const *children; // childCount == 0 なら nullptr でもよい
uint16_t childCount;
};
struct Scene {
const Node *const *roots;
uint16_t rootCount;
};
// 手書きの例: 2 つの子を持つルート
static const g3::Node wheelL = {"WheelL", g3::mat4f::translation(-1, 0, 0), &wheelMesh, nullptr, 0};
static const g3::Node wheelR = {"WheelR", g3::mat4f::translation(1, 0, 0), &wheelMesh, nullptr, 0};
static const g3::Node *const carChildren[] = {&wheelL, &wheelR};
static const g3::Node car = {"Car", g3::mat4f::identity(), &bodyMesh, carChildren, 2};
static const g3::Node *const roots[] = {&car};
static const g3::Scene scene = {roots, 1};
mat4f は集成体なので {{16 個の float}} (列優先) でも初期化できます。gltf2cpp はこの形式で出力します。
描画関数
メンバー |
説明 |
|---|---|
|
全プリミティブをそれぞれのマテリアルで描く |
|
|
|
全ルートに |
状態スタック (16 段) が満杯のときは、そのノード以下を飛ばして Stats::nodesDropped に数えます。
深すぎるツリーや壊れたデータが行列スタックを壊すことはありません。
NodeVisitor
class NodeVisitor {
public:
virtual ~NodeVisitor() = default;
// node を描く直前に呼ばれる。local はノードのローカル変換のコピーで、書き換えると
// このフレームの描画に反映される。false を返すとノードとその子孫を描かない。
virtual bool onNode(const Node &node, mat4f &local);
};
ノードの名前で部品を見つけ、回転させたり隠したりする例:
class Animator : public g3::NodeVisitor {
public:
float angle = 0;
bool showWheels = true;
bool onNode(const g3::Node &node, g3::mat4f &local) override {
if (!node.name) return true;
if (std::strcmp(node.name, "Blades") == 0) {
local = local * g3::mat4f::rotation(angle, {0, 0, 1}); // ローカル軸で回す
} else if (!showWheels && std::strncmp(node.name, "Wheel", 5) == 0) {
return false;
}
return true;
}
};
Animator anim;
anim.angle = t * 2.0f;
g3d.putScene(windmill::scene, &anim);
生成されたヘッダには名前付きのノード定数 (windmill::node_Blades など) も含まれるので、
部分木だけを別の場所に描く (g3d.putNode(windmill::node_Blades)) こともできます。
メモリ安全性について
静的シーンのデータは shared_ptr のような所有権管理を使いません。安全性は次の 3 点で担保しています。
全ての参照先が静的記憶域にある (解放されない)。
配列は必ず個数と対で表され、走査はその範囲を超えない。
putPrimitive()が添字を頂点数で検査し、範囲外の三角形を破棄する (Stats::badIndices)。
gltf2cpp は生成時にも添字範囲、頂点数の上限 (65535)、テクスチャサイズを検証します。