1 // SuperTuxKart - a fun racing game with go-kart
2 // Copyright (C) 2011-2015 Marianne Gagnon
3 // based on code Copyright 2002-2010 Nikolaus Gebhardt
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 3
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 #include "graphics/camera.hpp"
20 #include "graphics/central_settings.hpp"
21 #include "graphics/irr_driver.hpp"
22 #include "graphics/lod_node.hpp"
23 #include "config/user_config.hpp"
24 #include "karts/abstract_kart.hpp"
25
26 #include <ISceneManager.h>
27 #include <ICameraSceneNode.h>
28 #include <IMeshSceneNode.h>
29 #include <IAnimatedMeshSceneNode.h>
30
31 /**
32 * @param group_name Only useful for getGroupName()
33 */
LODNode(std::string group_name,scene::ISceneNode * parent,scene::ISceneManager * mgr,s32 id)34 LODNode::LODNode(std::string group_name, scene::ISceneNode* parent,
35 scene::ISceneManager* mgr, s32 id)
36 : ISceneNode(parent, mgr, id)
37 {
38 assert(mgr != NULL);
39 assert(parent != NULL);
40
41 m_group_name = group_name;
42
43 m_previous_visibility = FIRST_PASS;
44
45 // At this stage refcount is two: one because of the object being
46 // created, and once because it is a child of the parent. Drop once,
47 // so that only the reference from the parent is active, causing this
48 // node to be deleted when it is removed from the parent.
49 drop();
50
51 m_forced_lod = -1;
52 m_last_tick = 0;
53 }
54
~LODNode()55 LODNode::~LODNode()
56 {
57 }
58
render()59 void LODNode::render()
60 {
61 //ISceneNode::render();
62 }
63
64 /** Returns the level to use, or -1 if the object is too far
65 * away.
66 */
getLevel()67 int LODNode::getLevel()
68 {
69 if (m_nodes.size() == 0)
70 return -1;
71
72 // If a level is forced, use it
73 if(m_forced_lod>-1)
74 return m_forced_lod;
75
76 Camera* camera = Camera::getActiveCamera();
77 if (camera == NULL)
78 return (int)m_detail.size() - 1;
79 const Vec3 &pos = camera->getCameraSceneNode()->getAbsolutePosition();
80
81 const int dist =
82 (int)((m_nodes[0]->getAbsolutePosition()).getDistanceFromSQ(pos.toIrrVector() ));
83
84 for (unsigned int n=0; n<m_detail.size(); n++)
85 {
86 if (dist < m_detail[n])
87 return n;
88 }
89
90 return -1;
91 } // getLevel
92
93 // ---------------------------------------------------------------------------
94 /** Forces the level of detail to be n. If n>number of levels, the most
95 * detailed level is used. This is used to disable LOD when the end
96 * camera is activated, since it zooms in to the kart. */
forceLevelOfDetail(int n)97 void LODNode::forceLevelOfDetail(int n)
98 {
99 m_forced_lod = (n >=(int)m_detail.size()) ? (int)m_detail.size()-1 : n;
100 } // forceLevelOfDetail
101
102 // ----------------------------------------------------------------------------
OnAnimate(u32 timeMs)103 void LODNode::OnAnimate(u32 timeMs)
104 {
105 if (isVisible() && m_nodes.size() > 0)
106 {
107 // update absolute position
108 updateAbsolutePosition();
109
110 #ifndef SERVER_ONLY
111 if (CVS->isGLSL())
112 {
113 for (size_t i = 0; i < m_nodes.size(); i++)
114 {
115 m_nodes[i]->setVisible(true);
116 m_nodes[i]->OnAnimate(timeMs);
117 }
118 }
119 else
120 #endif
121 {
122 int level = getLevel();
123 // Assume all the scene node have the same bouding box
124 if(level>=0)
125 m_nodes[level]->OnAnimate(timeMs);
126 }
127
128 Box = m_nodes[m_detail.size()-1]->getBoundingBox();
129
130 // If this node has children other than the LOD nodes, animate it
131 core::list<ISceneNode*>::Iterator it;
132 for (it = Children.begin(); it != Children.end(); it++)
133 {
134 if (m_nodes_set.find(*it) == m_nodes_set.end())
135 {
136 assert(*it != NULL);
137 if ((*it)->isVisible())
138 {
139 (*it)->OnAnimate(timeMs);
140 }
141 }
142 }
143
144 }
145 }
146
updateVisibility(bool * shown)147 void LODNode::updateVisibility(bool* shown)
148 {
149 if (!isVisible()) return;
150 if (m_nodes.size() == 0) return;
151
152 unsigned int level = getLevel();
153 for (size_t i = 0; i < m_nodes.size(); i++)
154 {
155 m_nodes[i]->setVisible(i == level);
156 if (i == level && shown != NULL)
157 *shown = (i > 0);
158 }
159 }
160
OnRegisterSceneNode()161 void LODNode::OnRegisterSceneNode()
162 {
163 bool shown = false;
164 updateVisibility(&shown);
165
166 #ifndef SERVER_ONLY
167 if (CVS->isGLSL())
168 {
169 return;
170 }
171 #endif
172
173 const u32 now = irr_driver->getDevice()->getTimer()->getTime();
174
175 // support an optional, mostly hard-coded fade-in/out effect for objects with a single level
176 if (m_nodes.size() == 1 && (m_nodes[0]->getType() == scene::ESNT_MESH ||
177 m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH) &&
178 now > m_last_tick)
179 {
180 if (m_previous_visibility == WAS_HIDDEN && shown)
181 {
182 scene::IMesh* mesh;
183
184 if (m_nodes[0]->getType() == scene::ESNT_MESH)
185 {
186 scene::IMeshSceneNode* node = (scene::IMeshSceneNode*)(m_nodes[0]);
187 mesh = node->getMesh();
188 }
189 else
190 {
191 assert(m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH);
192 scene::IAnimatedMeshSceneNode* node =
193 (scene::IAnimatedMeshSceneNode*)(m_nodes[0]);
194 assert(node != NULL);
195 mesh = node->getMesh();
196 }
197 }
198 else if (m_previous_visibility == WAS_SHOWN && !shown)
199 {
200 scene::IMesh* mesh;
201
202 if (m_nodes[0]->getType() == scene::ESNT_MESH)
203 {
204 scene::IMeshSceneNode* node = (scene::IMeshSceneNode*)(m_nodes[0]);
205 mesh = node->getMesh();
206 }
207 else
208 {
209 assert(m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH);
210 scene::IAnimatedMeshSceneNode* node =
211 (scene::IAnimatedMeshSceneNode*)(m_nodes[0]);
212 assert(node != NULL);
213 mesh = node->getMesh();
214 }
215
216 }
217 else if (m_previous_visibility == FIRST_PASS && !shown)
218 {
219 scene::IMesh* mesh;
220
221 if (m_nodes[0]->getType() == scene::ESNT_MESH)
222 {
223 scene::IMeshSceneNode* node = (scene::IMeshSceneNode*)(m_nodes[0]);
224 mesh = node->getMesh();
225 }
226 else
227 {
228 assert(m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH);
229 scene::IAnimatedMeshSceneNode* node =
230 (scene::IAnimatedMeshSceneNode*)(m_nodes[0]);
231 assert(node != NULL);
232 mesh = node->getMesh();
233 }
234 }
235 }
236
237 m_previous_visibility = (shown ? WAS_SHOWN : WAS_HIDDEN);
238 m_last_tick = now;
239 #ifndef SERVER_ONLY
240 if (!CVS->isGLSL())
241 {
242 for (core::list<ISceneNode*>::Iterator it = Children.begin();
243 it != Children.end(); it++)
244 {
245 (*it)->updateAbsolutePosition();
246 }
247 }
248 #endif
249 scene::ISceneNode::OnRegisterSceneNode();
250 }
251
add(int level,scene::ISceneNode * node,bool reparent)252 void LODNode::add(int level, scene::ISceneNode* node, bool reparent)
253 {
254 // samuncle suggested to put a slight randomisation in LOD
255 // I'm not convinced (Auria) but he's the artist pro, so I listen ;P
256 // The last level should not be randomized because after that the object disappears,
257 // and the location is disapparition needs to be deterministic
258 if (m_detail.size() > 0)
259 {
260 assert(m_detail.back()<level*level);
261 m_detail[m_detail.size() - 1] += (int)(((rand()%1000)-500)/500.0f*(m_detail[m_detail.size() - 1]*0.2f));
262 }
263
264 assert(node != NULL);
265
266 node->grab();
267 node->remove();
268 node->setPosition(core::vector3df(0,0,0));
269 m_detail.push_back(level*level);
270 m_nodes.push_back(node);
271 m_nodes_set.insert(node);
272 node->setParent(this);
273
274 if (node->getType() == scene::ESNT_ANIMATED_MESH)
275 ((scene::IAnimatedMeshSceneNode *) node)->setReadOnlyMaterials(true);
276 if (node->getType() == scene::ESNT_MESH)
277 ((scene::IMeshSceneNode *) node)->setReadOnlyMaterials(true);
278
279 node->drop();
280
281 node->updateAbsolutePosition();
282 }
283