1 /********************************************************************
3 * Copyright (C) 2008 Tobia Zorzan
5 * This file is part of GoMoku3D.
7 * GoMoku3D is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * GoMoku3D is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with GoMoku3D. If not, see <http://www.gnu.org/licenses/>.
20 *******************************************************************/
22 #include "SceneGraph.h"
24 SceneGraph::SceneGraph(CubeRenderArea
*render
, int dim
) : _renderarea(render
),
27 _directionOfExplosion(CubeRenderArea::NO_ORIENTATION
),
28 _desiredDirectionOfExplosion(CubeRenderArea::NO_ORIENTATION
),
32 //allocating all new nodes for the scene graph
34 //root of the graph, must be referenced manually, all its childs don't need it
35 _root
= new SoSeparator();
38 _camera
= new SoPerspectiveCamera();
40 _light
= new SoDirectionalLight();
41 //container for all cubes, make them clickable
42 _selector
= new SoSelection();
43 //group for the fixed part of the cube
44 _fixedCubes
= new SoGroup();
46 //List of SoSeparator *, all cubes moved by the explosion
47 _movedCubes
= new SoBaseList(dim
- 1);
48 //List of Sotranslation *, used for animation
49 _movers
= new SoBaseList(dim
- 1);
50 //List of SoInterpolateVec3f, used for animation
51 _interpolators
= new SoBaseList(dim
- 1);
52 //List of SoOneShot *, used for animation
53 _timers
= new SoBaseList(dim
- 1);
55 //shape shared by all the cubes
56 _shape
= new SoCube();
57 //trigger to invoke explosion method
58 _explodeSensor
= new SoOneShotSensor();
59 //trigger to invoke implosion method
60 _implodeSensor
= new SoOneShotSensor();
61 //matrix to have a direct acces to cubes by indexes
62 _graphMatrix
= new SoSeparator
*[dim
* dim
* dim
];
64 _explodeSensor
->setFunction(SceneGraph::explodeCube
);
65 _explodeSensor
->setData(this);
67 _implodeSensor
->setFunction(SceneGraph::implodeCube
);
68 _implodeSensor
->setData(this);
70 _selector
->policy
= SoSelection::SINGLE
;
71 _selector
->renderCaching
= SoSeparator::ON
;
72 _selector
->boundingBoxCaching
= SoSeparator::ON
;
73 _selector
->renderCulling
= SoSeparator::ON
;
74 _selector
->pickCulling
= SoSeparator::ON
;
76 _root
->addChild(_light
);
77 _root
->addChild(_camera
);
78 _root
->addChild(_selector
);
79 _selector
->addChild(_fixedCubes
);
81 SoShapeHints
*hints
= new SoShapeHints();
82 hints
->vertexOrdering
= SoShapeHints::COUNTERCLOCKWISE
;
83 hints
->shapeType
= SoShapeHints::SOLID
;
86 QColor defaultColor
= settings
.defaultCubeColor();
87 //qDebug() << "Default cube color is:" << defaultColor << ".";
89 CubeRenderArea::convertColor(defaultColor
).getRGB(coincolor
);
91 for (int x
= 0; x
< dim
; x
++) {
92 for (int y
= 0; y
< dim
; y
++) {
93 for (int z
= 0; z
< dim
; z
++) {
94 SoSeparator
*cubeInstance
= new SoSeparator();
96 SoTranslation
*cubeTranslation
= new SoTranslation();
97 cubeTranslation
->translation
= SbVec3f(x
* 3.0, y
* 3.0, z
* 3.0);
99 SoMaterial
*cubeMaterial
= new SoMaterial();
102 cubeMaterial
->diffuseColor
.setValue(coincolor
);
103 cubeMaterial
->shininess
.setValue(0.2);
104 cubeMaterial
->transparency
.setValue(defaultColor
.alphaF());
106 cubeInstance
->addChild(cubeTranslation
);
107 cubeInstance
->addChild(cubeMaterial
);
108 cubeInstance
->addChild(hints
);
109 cubeInstance
->addChild(_shape
);
111 _fixedCubes
->addChild(cubeInstance
);
113 _graphMatrix
[x
* dim
* dim
+ y
* dim
+ z
] = cubeInstance
;
119 SceneGraph::~SceneGraph()
121 delete _explodeSensor
;
122 delete _implodeSensor
;
124 // Destructor for the scene graph
127 delete[] _graphMatrix
;
132 delete _interpolators
;
135 void SceneGraph::applyRotationToCamera(SbRotation rotation
)
137 // Find global coordinates of focal point.
139 _camera
->orientation
.getValue().multVec(SbVec3f(0, 0, -1), direction
);
140 SbVec3f focalpoint
= _camera
->position
.getValue() + _camera
->focalDistance
.getValue() * direction
;
142 // Set new orientation value by accumulating the new rotation.
143 _camera
->orientation
= rotation
* _camera
->orientation
.getValue();
145 // Reposition camera so we are still pointing at the same old focal point.
146 _camera
->orientation
.getValue().multVec(SbVec3f(0, 0, -1), direction
);
147 _camera
->position
= focalpoint
- _camera
->focalDistance
.getValue() * direction
;
149 //update CubeRenderArea::frontside/upside
152 for (int i
= 0; i
< 3; i
++) {
153 if (SoQtAbs(direction
[i
]) > SoQtAbs(direction
[max
])) {
159 if (direction
[max
] < 0) {
160 _renderarea
->setFrontside(CubeRenderArea::X_MINUS
);
162 _renderarea
->setFrontside(CubeRenderArea::X_PLUS
);
167 if (direction
[max
] < 0) {
168 _renderarea
->setFrontside(CubeRenderArea::Y_MINUS
);
170 _renderarea
->setFrontside(CubeRenderArea::Y_PLUS
);
175 if (direction
[max
] < 0) {
176 _renderarea
->setFrontside(CubeRenderArea::Z_MINUS
);
178 _renderarea
->setFrontside(CubeRenderArea::Z_PLUS
);
186 _camera
->orientation
.getValue().multVec(SbVec3f(0, 1, 0), upside
);
188 for (int i
= 0; i
< 3; i
++) {
189 if (SoQtAbs(upside
[i
]) > SoQtAbs(upside
[maxu
])) {
195 if (upside
[maxu
] < 0) {
196 _renderarea
->setUpside(CubeRenderArea::X_MINUS
);
198 _renderarea
->setUpside(CubeRenderArea::X_PLUS
);
203 if (upside
[maxu
] < 0) {
204 _renderarea
->setUpside(CubeRenderArea::Y_MINUS
);
206 _renderarea
->setUpside(CubeRenderArea::Y_PLUS
);
211 if (upside
[maxu
] < 0) {
212 _renderarea
->setUpside(CubeRenderArea::Z_MINUS
);
214 _renderarea
->setUpside(CubeRenderArea::Z_PLUS
);
219 qDebug() << "Rotation:" << _renderarea
->frontside() << " " << _renderarea
->upside();
222 void SceneGraph::applyTranslationToCamera(int delta
)
225 _camera
->orientation
.getValue().multVec(SbVec3f(0, 0, -1), direction
);
226 SbVec3f oldpos
= _camera
->position
.getValue();
227 SbVec3f newpos
= oldpos
+ (delta
) * - direction
;
229 _camera
->position
= newpos
;
230 _camera
->focalDistance
.setValue(_camera
->focalDistance
.getValue() + delta
);
231 _camera
->farDistance
.setValue(_camera
->farDistance
.getValue() + delta
);
236 void SceneGraph::explodeCube(void *userdata
, SoSensor
*sensor
)
238 //qDebug() << "Explosion sensor callback.";
240 SceneGraph
*scene
= (SceneGraph
*) userdata
;
241 scene
->applyExplosionToCube(scene
->desiredDirectionOfExplosion());
245 void SceneGraph::implodeCube(void *userdata
, SoSensor
*sensor
)
247 //qDebug() << "Implosion sensor callback.";
249 SceneGraph
*scene
= (SceneGraph
*) userdata
;
250 scene
->applyImplosionToCube(scene
->allImplosion());
254 void SceneGraph::applyExplosionToCube(CubeRenderArea::ORIENTATION desired
)
256 //qDebug() << "Apply EXPLOSION to cube.\nDesired direction : " << desired << ".\nCurrent direction : " << _directionOfExplosion << ".";
257 Q_ASSERT(_selectedCube
);
258 SoTranslation
*cursorPosition
= static_cast<SoTranslation
*>(_selectedCube
->getChild(0));
260 if (desired
!= _directionOfExplosion
) {
261 //clean lists and graph
262 //qDebug() << "New direction of explosion. Cleaning scene graph....done!";
263 _directionOfExplosion
= CubeRenderArea::NO_ORIENTATION
;
266 //qDebug() << "Lists size: " << _movedCubes->getLength() <<".";
268 for (int i
= 0; i
< _movedCubes
->getLength(); i
++) {
269 SoSeparator
*oldGroup
= static_cast<SoSeparator
*>(_movedCubes
->get(i
));
270 SoChildList
*cubeList
= oldGroup
->getChildren();
271 for (int j
= 1; j
< cubeList
->getLength(); j
++) {
272 _fixedCubes
->addChild(static_cast<SoSeparator
*>(cubeList
->get(j
)));
274 oldGroup
->removeAllChildren();
275 _selector
->removeChild(oldGroup
);
278 _movedCubes
->truncate(0);
279 _movers
->truncate(0);
280 _interpolators
->truncate(0);
281 _timers
->truncate(0);
283 //Fix cube path in _selector
284 Point
p(static_cast<int>(cursorPosition
->translation
.getValue()[0]) / 3, static_cast<int>(cursorPosition
->translation
.getValue()[1]) / 3, static_cast<int>(cursorPosition
->translation
.getValue()[2]) / 3);
285 _renderarea
->selectCube(p
);
287 _directionOfExplosion
= desired
;
290 //New Explosion to apply
292 //calculate number of levels to explode
294 int levelsToExplode
= 0;
295 switch (_directionOfExplosion
) {
296 case CubeRenderArea::X_PLUS
: {
297 levelsToExplode
= static_cast<int>(cursorPosition
->translation
.getValue()[0]) / 3 - _explodedLevels
;
300 case CubeRenderArea::X_MINUS
: {
301 levelsToExplode
= _dim
- static_cast<int>(cursorPosition
->translation
.getValue()[0]) / 3 - _explodedLevels
- 1;
304 case CubeRenderArea::Y_PLUS
: {
305 levelsToExplode
= static_cast<int>(cursorPosition
->translation
.getValue()[1]) / 3 - _explodedLevels
;
308 case CubeRenderArea::Y_MINUS
: {
309 levelsToExplode
= _dim
- static_cast<int>(cursorPosition
->translation
.getValue()[1]) / 3 - _explodedLevels
- 1;
312 case CubeRenderArea::Z_PLUS
: {
313 levelsToExplode
= static_cast<int>(cursorPosition
->translation
.getValue()[2]) / 3 - _explodedLevels
;
316 case CubeRenderArea::Z_MINUS
: {
317 levelsToExplode
= _dim
- static_cast<int>(cursorPosition
->translation
.getValue()[2]) / 3 - _explodedLevels
- 1;
324 //qDebug() << "Need to explode " << levelsToExplode << " levels.";
326 //call explodeLevel() n times to explode n levels
327 for (int i
= 0; i
< levelsToExplode
; i
++) {
328 this->explodeLevel();
331 if (_directionOfExplosion
!= CubeRenderArea::NO_ORIENTATION
) {
332 _renderarea
->setIsExploded(true);
336 void SceneGraph::applyImplosionToCube(bool all
)
338 //qDebug() << "Apply IMPLOSION to cube.\nBoolean is: " << all << ".";
340 int levelsToImplode
= 0;
343 levelsToImplode
= _explodedLevels
;
344 _renderarea
->setIsExploded(false);
346 SoTranslation
*cursorPosition
= static_cast<SoTranslation
*>(_selectedCube
->getChild(0));
348 switch (_directionOfExplosion
) {
349 case CubeRenderArea::X_PLUS
: {
350 levelsToImplode
= _explodedLevels
- static_cast<int>(cursorPosition
->translation
.getValue()[0]) / 3;
353 case CubeRenderArea::X_MINUS
: {
354 levelsToImplode
= _explodedLevels
- _dim
+ 1 + static_cast<int>(cursorPosition
->translation
.getValue()[0]) / 3;
357 case CubeRenderArea::Y_PLUS
: {
358 levelsToImplode
= _explodedLevels
- static_cast<int>(cursorPosition
->translation
.getValue()[1]) / 3;
361 case CubeRenderArea::Y_MINUS
: {
362 levelsToImplode
= _explodedLevels
- _dim
+ 1 + static_cast<int>(cursorPosition
->translation
.getValue()[1]) / 3;
365 case CubeRenderArea::Z_PLUS
: {
366 levelsToImplode
= _explodedLevels
- static_cast<int>(cursorPosition
->translation
.getValue()[2]) / 3;
369 case CubeRenderArea::Z_MINUS
: {
370 levelsToImplode
= _explodedLevels
- _dim
+ 1 + static_cast<int>(cursorPosition
->translation
.getValue()[2]) / 3;
373 case CubeRenderArea::NO_ORIENTATION
: {
374 qDebug() << "Error: Cannot apply Implosion with _directionOfExplosion == NO_ORIENTATION.";
380 //qDebug() << "Need to implode " << levelsToImplode << " levels.";
382 for (int i
= 0; i
< levelsToImplode
;i
++) {
383 this->implodeLevel();
389 void SceneGraph::explodeLevel()
392 if (_explodedLevels
- 1 < _movedCubes
->getLength()) {
393 //qDebug() << "Restore old explosion for level: " << _explodedLevels-1 << ".";
395 SoInterpolateVec3f
*levelInterpolator
= static_cast<SoInterpolateVec3f
*>(_interpolators
->get(_explodedLevels
- 1));
396 SoOneShot
*levelTimer
= static_cast<SoOneShot
*>(_timers
->get(_explodedLevels
- 1));
397 levelInterpolator
->input1
= levelInterpolator
->input0
;
398 levelInterpolator
->input0
= SbVec3f(0, 0, 0);
399 levelTimer
->trigger
.touch();
402 //qDebug() << "New explosion for level: " << _explodedLevels - 1 << ".";
404 SoGroup
*newGroup
= new SoSeparator();
405 SoTranslation
*newMover
= new SoTranslation();
406 SoInterpolateVec3f
*newInterpolator
= new SoInterpolateVec3f();
407 SoOneShot
*newTimer
= new SoOneShot();
409 _movedCubes
->append(newGroup
);
410 _movers
->append(newMover
);
411 _interpolators
->append(newInterpolator
);
412 _timers
->append(newTimer
);
414 _selector
->addChild(newGroup
);
415 newGroup
->addChild(newMover
);
416 newMover
->translation
.connectFrom(&newInterpolator
->output
);
417 newInterpolator
->input0
= SbVec3f(0, 0, 0);
418 newInterpolator
->alpha
.connectFrom(&newTimer
->ramp
);
419 newTimer
->flags
= SoOneShot::HOLD_FINAL
;
422 SbVec3f targetPosition
= SbVec3f(0, 0, 0);
423 for (int i
= 0; i
< _dim
; i
++) {
424 for (int j
= 0; j
< _dim
; j
++) {
425 switch (_directionOfExplosion
) {
426 case CubeRenderArea::X_PLUS
: {
427 qDebug() << "Explosion: " << _explodedLevels
- 1 << " " << i
<< " " << j
<< " selected for move.";
429 SoSeparator
*cubeInstance
= _graphMatrix
[(_explodedLevels
-1) * _dim
* _dim
+ i
* _dim
+ j
];
430 newGroup
->addChild(cubeInstance
);
431 _fixedCubes
->removeChild(cubeInstance
);
433 targetPosition
= SbVec3f(0, 0, _dim
* 3);
436 case CubeRenderArea::X_MINUS
: {
437 qDebug() << "Explosion: " << _dim
- _explodedLevels
<< " " << i
<< " " << j
<< " selected for move.";
439 SoSeparator
*cubeInstance
= _graphMatrix
[(_dim
-_explodedLevels
) * _dim
* _dim
+ i
* _dim
+ j
];
440 newGroup
->addChild(cubeInstance
);
441 _fixedCubes
->removeChild(cubeInstance
);
443 targetPosition
= SbVec3f(0, 0, -_dim
* 3);
446 case CubeRenderArea::Y_PLUS
: {
447 qDebug() << "Explosion: " << i
<< " " << _explodedLevels
- 1 << " " << j
<< " selected for move.";
449 SoSeparator
*cubeInstance
= _graphMatrix
[i
* _dim
* _dim
+ (_explodedLevels
-1) * _dim
+ j
];
450 newGroup
->addChild(cubeInstance
);
451 _fixedCubes
->removeChild(cubeInstance
);
453 targetPosition
= SbVec3f(_dim
* 3, 0, 0);
456 case CubeRenderArea::Y_MINUS
: {
457 qDebug() << "Explosion: " << i
<< " " << _dim
- _explodedLevels
<< " " << j
<< " selected for move.";
459 SoSeparator
*cubeInstance
= _graphMatrix
[i
* _dim
* _dim
+ (_dim
-_explodedLevels
) * _dim
+ j
];
460 newGroup
->addChild(cubeInstance
);
461 _fixedCubes
->removeChild(cubeInstance
);
463 targetPosition
= SbVec3f(-_dim
* 3, 0, 0);
466 case CubeRenderArea::Z_PLUS
: {
467 qDebug() << "Explosion: " << j
<< " " << i
<< " " << _explodedLevels
- 1 << " selected for move.";
469 SoSeparator
*cubeInstance
= _graphMatrix
[j
* _dim
* _dim
+ i
* _dim
+ _explodedLevels
-1];
470 newGroup
->addChild(cubeInstance
);
471 _fixedCubes
->removeChild(cubeInstance
);
473 targetPosition
= SbVec3f(_dim
* 3, 0, 0);
476 case CubeRenderArea::Z_MINUS
: {
477 qDebug() << "Explosion: " << j
<< " " << i
<< " " << _dim
- _explodedLevels
<< " selected for move.";
479 SoSeparator
*cubeInstance
= _graphMatrix
[j
* _dim
* _dim
+ i
* _dim
+ _dim
-_explodedLevels
];
480 newGroup
->addChild(cubeInstance
);
481 _fixedCubes
->removeChild(cubeInstance
);
483 targetPosition
= SbVec3f(-_dim
* 3, 0, 0);
486 case CubeRenderArea::NO_ORIENTATION
: {
487 qDebug() << "Error: Cannot apply Explosion with frontside == NO_ORIENTATION.";
494 //target position is not upside dependent.
496 newInterpolator
->input1
= targetPosition
;
499 newTimer
->trigger
.touch();
501 //qDebug() << "Explosion for level " << _explodedLevels - 1 << "done.";
505 void SceneGraph::implodeLevel()
507 //qDebug() << "Implosion for level " << _explodedLevels -1 << ".";
509 SoInterpolateVec3f
*levelInterpolator
= static_cast<SoInterpolateVec3f
*>(_interpolators
->get(_explodedLevels
));
510 SoOneShot
*levelTimer
= static_cast<SoOneShot
*>(_timers
->get(_explodedLevels
));
511 levelInterpolator
->input0
= levelInterpolator
->input1
;
512 levelInterpolator
->input1
= SbVec3f(0, 0, 0);
513 levelTimer
->trigger
.touch();
517 CubeRenderArea
*SceneGraph::renderarea()
521 SoSeparator
*SceneGraph::root()
525 SoCamera
*SceneGraph::camera()
529 SoSelection
*SceneGraph::selector()
533 SoGroup
*SceneGraph::fixedCubes()
537 SoBaseList
*SceneGraph::movedCubes()
541 SoShape
*SceneGraph::shape()
545 SoBaseList
*SceneGraph::movers()
549 SoBaseList
*SceneGraph::interpolators()
551 return _interpolators
;
553 SoBaseList
*SceneGraph::timers()
557 SoOneShotSensor
*SceneGraph::explodeSensor()
559 return _explodeSensor
;
561 SoOneShotSensor
*SceneGraph::implodeSensor()
563 return _implodeSensor
;
565 SoSeparator
**SceneGraph::graphMatrix()
569 SoSeparator
*SceneGraph::selectedCube()
571 return _selectedCube
;
573 CubeRenderArea::ORIENTATION
SceneGraph::directionOfExplosion()
575 return _directionOfExplosion
;
577 CubeRenderArea::ORIENTATION
SceneGraph::desiredDirectionOfExplosion()
579 return _desiredDirectionOfExplosion
;
581 bool SceneGraph::allImplosion()
583 return _allImplosion
;
585 int SceneGraph::explodedLevels()
587 return _explodedLevels
;
590 void SceneGraph::setSelectedCube(SoSeparator
*cube
)
592 _selectedCube
= cube
;
595 void SceneGraph::setDirectionOfExplosion(CubeRenderArea::ORIENTATION direction
)
597 _directionOfExplosion
= direction
;
600 void SceneGraph::setDesiredDirectionOfExplosion(CubeRenderArea::ORIENTATION direction
)
602 _desiredDirectionOfExplosion
= direction
;
605 void SceneGraph::setAllImplosion(bool value
)
607 _allImplosion
= value
;
610 void SceneGraph::setExplodedLevels(int num
)
612 _explodedLevels
= num
;