1 /** 2 * Copyright: Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved 3 * Authors: $(Gabriel Gheorghe) 4 * License: $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007) 5 * Source 6 * Documentation: 7 * Coverage: 8 */ 9 module liberty.core.scenegraph.node; 10 import liberty.core.engine: CoreEngine; 11 import liberty.core.scenegraph.services: Startable, Updatable, Processable; 12 import liberty.core.scenegraph.scene: Scene; 13 import liberty.core.scenegraph.camera: Camera; 14 import liberty.core.components: Transform; 15 /// Represents an object in the scene tree. 16 abstract class Node : Startable, Updatable, Processable { 17 private { 18 string _id; 19 Node _parent; 20 Node[string] _children; 21 Scene _scene; 22 Node[string] _singletonList; 23 } 24 /// Transform component. 25 Transform transform; 26 /// Default constructor. 27 this(string id, Node parent) { 28 transform = Transform(this); 29 _scene = CoreEngine.get.activeScene; 30 if (id in _scene._ids) { 31 assert(0, "You already have an object with ID \"" ~ id ~ "\" in the current scene!"); 32 } 33 _scene._ids[id] = true; 34 _id = id; 35 _parent = parent; 36 } 37 /// Returns node ID. 38 string id() pure nothrow const { 39 return _id; 40 } 41 /// Returns an array with parent references. 42 @property Node parent() pure nothrow { 43 return _parent; 44 } 45 /// Returns an array with children references. 46 @property Node[string] children() pure nothrow { 47 return _children; 48 } 49 /// Returns a child reference using its ID. 50 @property T child(T)(string id) pure nothrow { 51 return cast(T)_children[id]; 52 } 53 /// 54 @property Scene scene() pure nothrow { 55 return _scene; 56 } 57 /// Called after all objects instantiation. Optional. 58 void start() {} 59 /// Called every frame. Optional. 60 void update(in float deltaTime) {} 61 /// Called every physics tick. Optional. 62 void process() {} 63 /// Insert a child node using its reference. 64 private void insert(T: Node)(ref T child) { 65 _children[child.id] = child; 66 } 67 /// Insert a child node using its id. 68 private void insert(float _id) pure nothrow { 69 70 } 71 /// Remove a child node using its reference. 72 void remove(T: Node)(ref T child) { 73 if (child in _children) { 74 _children.remove(child.id); 75 _scene.startList.remove(child.id); 76 _scene.updateList.remove(child.id); 77 _scene.processList.remove(child.id); 78 _scene.renderList.remove(child.id); 79 _scene._ids.remove(child.id); 80 static if (is(T == Camera)) { 81 _scene.clearCamera(child); 82 } 83 child.destroy(); 84 child = null; 85 return; 86 } 87 //throw new CoreEngineException("CoreEngine tried to remove an nonexistent object!"); 88 } 89 /// Remove a child node using its id. 90 void remove(string id) { 91 foreach (child; _children) { 92 if (child.id == id) { 93 _children.remove(child.id); 94 _scene.startList.remove(child.id); 95 _scene.updateList.remove(child.id); 96 _scene.processList.remove(child.id); 97 _scene.renderList.remove(child.id); 98 _scene._ids.remove(child.id); 99 static if (is(T == Camera)) { 100 _scene.clearCamera(child); 101 } 102 child.destroy(); 103 child = null; 104 return; 105 } 106 } 107 //throw new CoreEngineException("CoreEngine tried to remove an nonexistent object!"); 108 } 109 /// Spawn an object using its reference. 110 /// You can specify where to spawn. By default is set to scene tree. 111 /// Returns new nodes reference. 112 ref T spawn(T: Node)(ref T node, string id, bool start = true) { 113 node = new T(id, this); 114 insert(node); 115 static if (is(T == Camera)) { 116 _scene.registerCamera(node); 117 } 118 if (start) { 119 node.start(); 120 } 121 return node; 122 } 123 /// Spawn an object using its ID. 124 /// Second time you call this method for the same id, an assertion is produced. 125 /// Returns new node reference. 126 T spawn(T: Node)(string id, bool start = true) { 127 T node = new T(id, this); 128 insert(node); 129 static if (is(T == Camera)) { 130 _scene.registerCamera(node); 131 } 132 if (start) { 133 node.start(); 134 } 135 return node; 136 } 137 /// Spawn an object using its reference. 138 /// Second time you call this method for the same id, nothing happens. 139 /// Returns old/new node reference. 140 ref T spawnOnce(T: Node)(ref T node, string id, bool start = true) { 141 if (id in _singletonList) { 142 return cast(T)_singletonList[id]; 143 } 144 node = new T(id, this); 145 insert(node); 146 static if (is(T == Camera)) { 147 _scene.registerCamera(node); 148 } 149 _singletonList[id] = node; 150 if (start) { 151 node.start(); 152 } 153 return node; 154 } 155 /// Spawn an object using its ID only once. 156 /// Second time you call this method for the same id, nothing happens. 157 /// Returns old/new node reference. 158 T spawnOnce(T: Node)(string id, bool start = true) { 159 if (id in _singletonList) { 160 return cast(T)_singletonList[id]; 161 } 162 T node = new T(id, this); 163 insert(node); 164 static if (is(T == Camera)) { 165 _scene.registerCamera(node); 166 } 167 _singletonList[id] = node; 168 if (start) { 169 node.start(); 170 } 171 return node; 172 } 173 } 174 /// Root node of a scene. A scene can have only one root node. 175 final class Root: Node { 176 this() { 177 super("Root", null); 178 } 179 } 180 /// 181 //final class Group: Node { 182 // this(string id) { 183 // super(id, null); // ??????? 184 // } 185 //}