Работа с графом сцены
Работа с графом сцены
Каждая 3D‑сцена в Aspose.3D для .NET организована как дерево из Node объектов. Scene.RootNode является корнем этого дерева, и каждый элемент геометрии, материал и трансформация находятся под ним как дочерний элемент или потомок.
Создание сцены и доступ к корневому узлу
Scene инициализируется автоматически с корневым узлом с именем "RootNode":
using Aspose.ThreeD;
var scene = new Scene();
Node root = scene.RootNode; // always "RootNode"Добавление дочерних узлов
Call CreateChildNode() на любом узле, чтобы добавить дочерний элемент. Метод имеет три часто используемых перегрузки:
using Aspose.ThreeD;
using Aspose.ThreeD.Entities;
var scene = new Scene();
Node root = scene.RootNode;
// 1. Named node with no entity — useful as a pivot or transform container
Node pivot = root.CreateChildNode("pivot");
// 2. Named node with an entity
var box = new Box(2, 2, 2);
Node boxNode = root.CreateChildNode("box", box);
// 3. Named node with entity and material
var mat = new Aspose.ThreeD.Shading.PhongMaterial("red");
mat.Diffuse = new Vector4(0.8, 0.2, 0.2, 1.0);
Node decorated = pivot.CreateChildNode("red_box", box, mat);Чтобы присоединить отдельно сконструированный узел, используйте AddChildNode():
var detached = new Node("standalone");
root.AddChildNode(detached);Запрос дочерних узлов
Найдите прямого дочернего узла по имени или по индексу, либо переберите всех прямых дочерних узлов:
// By name — returns null if no direct child has that name
Node? found = root.GetChild("box");
if (found != null)
Console.WriteLine("Found: " + found.Name);
// By index
Node first = root.GetChild(0);
// Iterate all direct children
foreach (Node child in root.ChildNodes)
Console.WriteLine(child.Name);GetChild(string name) ищет только прямые дочерние элементы, а не полное поддерево. Используйте рекурсивный вспомогательный метод или Accept() для поиска всего дерева.
Обход полного дерева
Для полного обхода в глубину, выполните итерацию ChildNodes рекурсивно:
static void Walk(Node node, int depth = 0)
{
Console.WriteLine(new string(' ', depth * 2) + node.Name);
foreach (var child in node.ChildNodes)
Walk(child, depth + 1);
}
Walk(scene.RootNode);Для обхода с ранним выходом, node.Accept(NodeVisitor) доступен и вызывает посетитель для каждого потомка, пока посетитель не вернёт false.
Использование Group для организации связанных объектов
Group является контейнерной сущностью, которая логически организует узлы без добавления геометрии. Присоедините её к узлу, чьи дочерние элементы представляют логическую единицу:
using Aspose.ThreeD;
using Aspose.ThreeD.Entities;
var scene = new Scene();
// A group node for all furniture in a room
var furnitureGroup = new Group("furniture");
Node roomNode = scene.RootNode.CreateChildNode("living_room", furnitureGroup);
// Child nodes under the group node
Node sofaNode = roomNode.CreateChildNode("sofa", new Box(3, 1, 1));
Node tableNode = roomNode.CreateChildNode("coffee_table", new Box(2, 0.5, 1));Перемещение roomNode преобразует всю мебель вместе, потому что дочерние элементы наследуют её Transform.
Управление видимостью и исключением из экспорта
Узлы можно скрыть или исключить из экспорта, не удаляя их из иерархии:
Node ground = scene.RootNode.CreateChildNode("ground_plane", new Box(100, 0.1, 100));
ground.Visible = false; // hidden in viewport / renderer
Node helperNode = scene.RootNode.CreateChildNode("debug_arrow", new Box());
helperNode.Excluded = true; // omitted from all export operationsVisible = false является подсказкой отображения. Excluded = true не позволяет узлу появляться в экспортированных файлах независимо от формата.
Привязка нескольких сущностей к одному узлу
У узла есть основной сущность (Entity свойство), но может содержать дополнительные сущности через AddEntity(). Это полезно, когда разные части сетки используют один общий трансформ:
var bodyMesh = new Mesh("body");
var wheelMesh = new Mesh("wheel");
Node carNode = scene.RootNode.CreateChildNode("car");
carNode.AddEntity(bodyMesh);
carNode.AddEntity(wheelMesh);
// Retrieve all entities on this node
foreach (Entity ent in carNode.Entities)
Console.WriteLine(ent.GetType().Name + ": " + ent.Name);Объединение узлов
Merge() перемещает всех дочерних объектов, сущности и материалы из исходного узла в целевой узел. Исходный узел остаётся пустым:
Node lod0 = scene.RootNode.CreateChildNode("lod0");
lod0.CreateChildNode("mesh_high", new Box(1, 1, 1));
Node lod1 = scene.RootNode.CreateChildNode("lod1");
lod1.CreateChildNode("mesh_low", new Box(1, 1, 1));
// Consolidate lod0 children into lod1
lod1.Merge(lod0);
// lod1 now has both mesh_high and mesh_low; lod0 is emptyСледующие шаги
- Применение трансформов — позиционировать, вращать и масштабировать любой узел, используя его
Transform - Создание примитивной геометрии — используйте
Box,Sphere, иCylinderв качестве сущностей сцены - Создание и работа с Meshes — построить полигональную геометрию и прикрепить её к узлам
Быстрый справочник по API
| Элемент | Описание |
|---|---|
scene.RootNode | Корень дерева сцены; всегда присутствует после new Scene() |
node.CreateChildNode(name) | Создать именованный дочерний узел без entity |
node.CreateChildNode(name, entity) | Создать именованный дочерний узел с entity |
node.CreateChildNode(name, entity, material) | Создать именованный дочерний узел с entity и material |
node.AddChildNode(node) | Присоединить отдельно построенный Node |
node.GetChild(name) | Найти прямого потомка по имени; возвращает null если не найдено |
node.GetChild(index) | Получить прямого потомка по заданному индексу |
node.ChildNodes | IList<Node> из всех прямых потомков |
node.Accept(visitor) | Обойти этот узел и всех его потомков в глубину |
node.AddEntity(entity) | Присоединить дополнительную сущность к узлу |
node.Entities | IList<Entity> из всех сущностей на этом узле |
node.Visible | Показать или скрыть узел |
node.Excluded | Включить или исключить узел из экспорта |
node.Merge(other) | Переместить всех дочерних элементов и сущностей из other в этот узел |
new Group(name) | Контейнерная сущность для логической группировки дочерних узлов |