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 //}