Model3D.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. #include "Model3D.h"
  2. #include "Model2D.h"
  3. #include "DXBuffer.h"
  4. #include "Textur.h"
  5. #include "Animation3D.h"
  6. #ifdef WIN32
  7. #include "Render3D.h"
  8. #include <d3d11.h>
  9. #endif
  10. #include <stdexcept>
  11. using namespace Framework;
  12. // Inhalt der Knochen Klasse
  13. // Konstruktor
  14. Knochen::Knochen( int id )
  15. {
  16. pos = Vec3< float >( 0, 0, 0 );
  17. winkel = Vec3< float >( 0, 0, 0 );
  18. geschwister = 0;
  19. kinder = 0;
  20. this->id = id;
  21. }
  22. // Destruktor
  23. Knochen::~Knochen()
  24. {
  25. delete geschwister;
  26. delete kinder;
  27. }
  28. // private
  29. // Fügt dem Knochen ein Geschwister Knochen hinzu
  30. // k: Der Knochen, der hinzugefügt werden soll
  31. void Knochen::addGeschwisterKnochen( Knochen *k )
  32. {
  33. if( !geschwister )
  34. geschwister = k;
  35. else
  36. geschwister->addGeschwisterKnochen( k );
  37. }
  38. // public
  39. // Setzt die Position des Knochens relativ zum Model Ursprung
  40. // pos: Die Position
  41. void Knochen::setPosition( Vec3< float > &pos )
  42. {
  43. this->pos = pos;
  44. }
  45. // Setzt die Drehung des Knochens relativ zum Model Ursprung
  46. // winkel: Ein Vektor der die Drehung um die verschiedenen Achsen als Komponenten hat
  47. void Knochen::setDrehung( Vec3< float > &winkel )
  48. {
  49. this->winkel = winkel;
  50. }
  51. // Fügt einem bestimmten Knochen ein Kind Knochen hinzu
  52. // id: Die id des Knochens, wo der Knochen als Kind hinzugefügt werden soll
  53. // k: Der Knochen, der hinzugefügt werden soll
  54. void Knochen::addKind( int id, Knochen *k )
  55. {
  56. if( this->id == id )
  57. {
  58. if( !kinder )
  59. kinder = k;
  60. else
  61. kinder->addGeschwisterKnochen( k );
  62. }
  63. else
  64. {
  65. if( kinder )
  66. kinder->addKind( id, k );
  67. else
  68. {
  69. Text err = "Es wurde kein Knochen mit der Id: ";
  70. err += id;
  71. err += " im Skelett gefunden, um ein Kind Knochen hinzuzufügen. Datei:" __FILE__ ", Zeile: ";
  72. err += __LINE__;
  73. err += "!";
  74. delete k;
  75. throw std::out_of_range( (const char*)err );
  76. }
  77. }
  78. }
  79. // Berechnet die Matrizen des Knochen und die von all seinen Geschwister Knochen und Kind Knochen
  80. // elternMat: Die fertig berechnete Matrix des Elternknochens
  81. // matBuffer: Ein Array, in dem alle berechneten Matrizen gespeichert werden sollen
  82. // kamMatrix: Die vereiniegung der view und projektions Matrizen
  83. void Knochen::kalkulateMatrix( Mat4< float > &elternMat, Mat4< float > *matBuffer, Mat4< float > &kamMat )
  84. {
  85. if( geschwister )
  86. geschwister->kalkulateMatrix( elternMat, matBuffer, kamMat );
  87. matBuffer[ id ] = matBuffer[ id ].translation( pos ) * matBuffer[ id ].rotationZ( winkel.z ) * matBuffer[ id ].rotationX( winkel.x ) * matBuffer[ id ].rotationY( winkel.y );
  88. matBuffer[ id ] = elternMat * matBuffer[ id ];
  89. if( kinder )
  90. kinder->kalkulateMatrix( matBuffer[ id ], matBuffer, kamMat );
  91. matBuffer[ id ] = kamMat * matBuffer[ id ];
  92. }
  93. // Kopiert den Knochen mit allen Geschwister Knochen und Kind Knochen
  94. Knochen *Knochen::kopiereKnochen() const
  95. {
  96. Knochen *ret = new Knochen( id );
  97. ret->pos = pos;
  98. ret->winkel = winkel;
  99. if( geschwister )
  100. ret->geschwister = geschwister->kopiereKnochen();
  101. if( kinder )
  102. ret->kinder = kinder->kopiereKnochen();
  103. return ret;
  104. }
  105. // Gibt die Id des Knochens zurück
  106. int Knochen::getId() const
  107. {
  108. return id;
  109. }
  110. // Gibt die Drehung des Knochens zurück
  111. Vec3< float > Knochen::getDrehung() const
  112. {
  113. return winkel;
  114. }
  115. // Gibt die Position des Knochens zurück
  116. Vec3< float > Knochen::getPosition() const
  117. {
  118. return pos;
  119. }
  120. // Gibt den Radius des Knochens zurück
  121. float Knochen::getRadius() const
  122. {
  123. float r = pos.getLength();
  124. if( geschwister )
  125. r = MAX( r, geschwister->getRadius() );
  126. if( kinder )
  127. r += kinder->getRadius();
  128. return r;
  129. }
  130. // Inhalt der Skelett Klasse
  131. // Konstruktor
  132. Skelett::Skelett()
  133. {
  134. k = 0;
  135. nextId = 0;
  136. ref = 1;
  137. }
  138. // Destruktor
  139. Skelett::~Skelett()
  140. {
  141. if( k )
  142. delete k;
  143. }
  144. // Gibt die Id des nächsten Knochens zurück und berechnet die neue Id für den Knochen danach
  145. // Es können maximal MAX_KNOCHEN_ANZ Knochen für ein Skelett existieren. Wenn diese Zahl überschritten wird, so wird -1 zurückgegeben
  146. int Skelett::getNextKnochenId()
  147. {
  148. return nextId++;
  149. }
  150. // Fügt dem Skellet einen Knochen hinzu
  151. // k: Der Knochen
  152. // elternId: Die Id des Eltern Knochens. Wenn der Knochen kein Elternknochen besitzt, kannder Parameter weggelassen werden.
  153. void Skelett::addKnochen( Knochen *k, int elternId )
  154. {
  155. if( !this->k )
  156. this->k = k;
  157. else
  158. this->k->addKind( elternId, k );
  159. if( k->getId() >= nextId )
  160. nextId = k->getId() + 1;
  161. }
  162. // Berechnet die Matrizen der Knochen
  163. // modelMatrix: Die Matrix, die das Skelett in den Raum der Welt transformiert
  164. // matBuffer: Ein Array von Matrizen, der durch die Knochen Matrizen gefüllt wird
  165. // return: gibt die Anzahl der verwendeten Matrizen zurück
  166. // kamMatrix: Die vereiniegung der view und projektions Matrizen
  167. int Skelett::kalkulateMatrix( Mat4< float > &modelMatrix, Mat4< float > *matBuffer, Mat4< float > &kamMatrix )
  168. {
  169. k->kalkulateMatrix( modelMatrix, matBuffer, kamMatrix );
  170. return nextId;
  171. }
  172. // Berechnet den Radius des Skeletts
  173. float Skelett::getRadius() const
  174. {
  175. if( k )
  176. return k->getRadius();
  177. return 0;
  178. }
  179. // Kopiert das Skelett
  180. Skelett *Skelett::kopiereSkelett() const
  181. {
  182. Skelett *ret = new Skelett();
  183. ret->nextId = nextId;
  184. if( k )
  185. ret->addKnochen( k->kopiereKnochen() );
  186. return ret;
  187. }
  188. // Erhöht den Reference Counting Zähler.
  189. Skelett *Skelett::getThis()
  190. {
  191. ref++;
  192. return this;
  193. }
  194. // Verringert den Reference Counting Zähler. Wenn der Zähler 0 erreicht, wird das Zeichnung automatisch gelöscht.
  195. // return: 0.
  196. Skelett *Skelett::release()
  197. {
  198. ref--;
  199. if( !ref )
  200. delete this;
  201. return 0;
  202. }
  203. // Inhalt des Polygon3D Struct
  204. // Konstruktor
  205. Polygon3D::Polygon3D()
  206. {
  207. indexAnz = 0;
  208. indexList = 0;
  209. indexBuffer = new DXIndexBuffer( sizeof( int ) );
  210. }
  211. // Destruktor
  212. Polygon3D::~Polygon3D()
  213. {
  214. indexBuffer->release();
  215. delete[] indexList;
  216. }
  217. // Inhalt der Model3DData Klasse
  218. // Konstruktor
  219. Model3DData::Model3DData()
  220. {
  221. id = -1;
  222. skelett = 0;
  223. vertexList = 0;
  224. polygons = new Array< Polygon3D* >();
  225. vertexBuffer = new DXVertexBuffer( sizeof( Vertex3D ) );
  226. radius = 0;
  227. ref = 1;
  228. }
  229. // Destruktor
  230. Model3DData::~Model3DData()
  231. {
  232. clearModel();
  233. vertexBuffer->release();
  234. polygons->release();
  235. }
  236. // Löscht alle Model daten
  237. void Model3DData::clearModel()
  238. {
  239. delete[] vertexList;
  240. vertexList = 0;
  241. for( auto i = polygons->getArray(); i.set; i++ )
  242. delete i.var;
  243. polygons->leeren();
  244. if( skelett )
  245. skelett->release();
  246. skelett = 0;
  247. radius = 0;
  248. }
  249. // Setzt den Zeiger auf ein standartmäßig verwendete Skelett
  250. // s: Das Skelett, das verwendet werden soll
  251. void Model3DData::setSkelettZ( Skelett *s )
  252. {
  253. if( skelett )
  254. skelett->release();
  255. skelett = s;
  256. }
  257. // Setzt einen Zeiger auf eine Liste mit allen Vertecies des Models
  258. // vertexList: Ein Array mit Vertecies
  259. // anz: Die Anzahl der Vertecies im Array
  260. void Model3DData::setVertecies( Vertex3D *vertexList, int anz )
  261. {
  262. delete[] this->vertexList;
  263. this->vertexList = vertexList;
  264. vertexBuffer->setData( vertexList );
  265. vertexBuffer->setLength( (int)sizeof( Vertex3D ) * anz );
  266. radius = 0;
  267. for( int i = 0; i < anz; i++ )
  268. {
  269. float r = vertexList[ i ].pos.getLength();
  270. if( r > radius )
  271. radius = r;
  272. }
  273. }
  274. // Fügt ein Polygon zum Model hinzu
  275. // polygon: Das Polygon, das hinzugefügt erden soll
  276. void Model3DData::addPolygon( Polygon3D *polygon )
  277. {
  278. polygons->add( polygon );
  279. }
  280. // Konvertiert ein 2d Model zu 3D
  281. // model: Das 2d Model, das zu 3d konvertiert werden soll
  282. // z: Die z koordinate aller punkte des Models
  283. void Model3DData::copyModel2D( Model2DData *model, float z )
  284. {
  285. if( model && model->vListen && model->polygons )
  286. {
  287. clearModel();
  288. int vAnz = 0;
  289. for( auto i = model->polygons->getArray(); i.set; i++ )
  290. vAnz += i.var.vertex->getEintragAnzahl();
  291. vertexList = new Vertex3D[ vAnz ];
  292. vertexBuffer->setData( vertexList );
  293. vertexBuffer->setLength( (int)sizeof( Vertex3D ) * vAnz );
  294. int index = 0;
  295. for( auto i = model->vListen->getArray(); i.set; i++ )
  296. {
  297. Polygon3D *p = new Polygon3D();
  298. p->indexAnz = 0;
  299. for( auto j = i.var->getArray(); j.set; j++ )
  300. {
  301. for( auto *k = &j.var->zListe()->getArray(); k->next->next && k->next->next->set; k = k->next )
  302. p->indexAnz += 3;
  303. }
  304. p->indexList = new int[ p->indexAnz ];
  305. p->indexBuffer->setData( p->indexList );
  306. p->indexBuffer->setLength( (int)sizeof( int ) * p->indexAnz );
  307. p->indexAnz = 0;
  308. for( auto j = i.var->getArray(); j.set; j++ )
  309. {
  310. for( auto *k = &j.var->zListe()->getArray(); k && k->set; k = k->next )
  311. {
  312. vertexList[ index ].pos = Vec3< float >( k->var->punkt->x, k->var->punkt->y, z );
  313. vertexList[ index ].tPos = ( Vec2< float > )*k->var->textur;
  314. if( k->next && k->next->set && k->next->next && k->next->next->set )
  315. {
  316. p->indexList[ p->indexAnz ] = index;
  317. p->indexAnz++;
  318. p->indexList[ p->indexAnz ] = index + 1;
  319. p->indexAnz++;
  320. p->indexList[ p->indexAnz ] = index + 2;
  321. p->indexAnz++;
  322. }
  323. index++;
  324. }
  325. }
  326. addPolygon( p );
  327. }
  328. }
  329. }
  330. // Entfernt ein Polygon
  331. // index: Der Index des Polygons
  332. void Model3DData::removePolygon( int index )
  333. {
  334. if( !polygons->hat( index ) )
  335. return;
  336. delete polygons->get( index );
  337. polygons->remove( index );
  338. }
  339. // Aktualisiert die Vertecies
  340. // zRObj: Das Objekt, mit dem die Grafikkarte verwaltet wird
  341. void Model3DData::aktualisiereVertecies( Render3D *zRObj )
  342. {
  343. vertexBuffer->copieren( zRObj );
  344. }
  345. // Berechnet die Matrizen der Knochen
  346. // modelMatrix: Die Matrix, die das Skelett in den Raum der Welt transformiert
  347. // matBuffer: Ein Array von Matrizen, der durch die Knochen Matrizen gefüllt wird
  348. // return: gibt die Anzahl der verwendeten Matrizen zurück
  349. // kamMatrix: Die vereiniegung der view und projektions Matrizen
  350. int Model3DData::kalkulateMatrix( Mat4< float > &modelMatrix, Mat4< float > *matBuffer, Mat4< float > &kamMatrix ) const
  351. {
  352. if( !skelett )
  353. return 0;
  354. return skelett->kalkulateMatrix( modelMatrix, matBuffer, kamMatrix );
  355. }
  356. // Zeichnet alle Polygons
  357. // world: Die Welt Matrix, die das Model in die Welt transformiert
  358. // zTxt: Eine Liste mit Texturen der einzelnen Polygone
  359. // zRObj: Das Objekt, mit dem gezeichnet werden soll
  360. void Model3DData::render( Mat4< float > &welt, const Model3DTextur *zTxt, Render3D *zRObj )
  361. {
  362. int ind = 0;
  363. for( auto *i = &polygons->getArray(); i && i->set; i = i->next )
  364. {
  365. i->var->indexBuffer->copieren( zRObj );
  366. Textur *t = zTxt->zPolygonTextur( ind );
  367. if( t && t->brauchtUpdate() )
  368. t->updateTextur( zRObj );
  369. #ifdef WIN32
  370. zRObj->draw( i->var->indexBuffer, t );
  371. #endif
  372. ind++;
  373. }
  374. }
  375. // Gibt die Anzahl an Polygonen zurück
  376. int Model3DData::getPolygonAnzahl() const
  377. {
  378. return polygons->getEintragAnzahl();
  379. }
  380. // Gibt ein bestimmtes Polygon zurück
  381. // index: Der Index des Polygons
  382. Polygon3D *Model3DData::getPolygon( int index ) const
  383. {
  384. if( !polygons->hat( index ) )
  385. return 0;
  386. return polygons->get( index );
  387. }
  388. // Gibt den radius einer Kugel zurück, die das gesammte Model umschließt
  389. float Model3DData::getRadius() const
  390. {
  391. return radius;
  392. }
  393. // Gibt die Id der Daten zurück, wenn sie in einer Model3DList registriert wurden. (siehe Framework::zM3DRegister())
  394. int Model3DData::getId() const
  395. {
  396. return id;
  397. }
  398. // Gibt einen Buffer mit allen Vertecies des Models zurück
  399. const DXVertexBuffer *Model3DData::zVertexBuffer() const
  400. {
  401. return vertexBuffer;
  402. }
  403. // Erhöht den Reference Counting Zähler.
  404. // return: this.
  405. Model3DData *Model3DData::getThis()
  406. {
  407. ref++;
  408. return this;
  409. }
  410. // Verringert den Reference Counting Zähler. Wenn der Zähler 0 erreicht, wird das Zeichnung automatisch gelöscht.
  411. // return: 0.
  412. Model3DData *Model3DData::release()
  413. {
  414. ref--;
  415. if( !ref )
  416. delete this;
  417. return 0;
  418. }
  419. // Inhalt der Model3DTextur
  420. // Konstruktor
  421. Model3DTextur::Model3DTextur()
  422. {
  423. textures = new RCArray< Textur >();
  424. ref = 1;
  425. }
  426. // Destruktor
  427. Model3DTextur::~Model3DTextur()
  428. {
  429. textures->release();
  430. }
  431. // Legt fest, welche Textur für welches Polygon ist
  432. // pI: Der Index des Polygons
  433. // txt: Die Textur des Polygons
  434. void Model3DTextur::setPolygonTextur( int pI, Textur *txt )
  435. {
  436. textures->set( txt, pI );
  437. }
  438. // Gibt einen Zeiger auf die Textur eines Polygons zurück ohne erhöhten Reference Counter
  439. // i: Der Index des Polygons
  440. Textur *Model3DTextur::zPolygonTextur( int i ) const
  441. {
  442. return textures->z( i );
  443. }
  444. // Erhöht den Reference Counting Zähler.
  445. // return: this.
  446. Model3DTextur *Model3DTextur::getThis()
  447. {
  448. ref++;
  449. return this;
  450. }
  451. // Verringert den Reference Counting Zähler. Wenn der Zähler 0 erreicht, wird das Zeichnung automatisch gelöscht.
  452. // return: 0.
  453. Model3DTextur *Model3DTextur::release()
  454. {
  455. ref--;
  456. if( !ref )
  457. delete this;
  458. return 0;
  459. }
  460. // Inhalt der AnimationData Struktur
  461. Model3D::AnimationData *Model3D::AnimationData::getThis()
  462. {
  463. return this;
  464. }
  465. Model3D::AnimationData *Model3D::AnimationData::release()
  466. {
  467. a->release();
  468. delete this;
  469. return 0;
  470. }
  471. // Inhalt der Model3D Klasse
  472. // Konstruktor
  473. Model3D::Model3D()
  474. : Zeichnung3D()
  475. {
  476. model = 0;
  477. textur = 0;
  478. skelett = 0;
  479. animations = new RCArray< AnimationData >();
  480. ref = 1;
  481. }
  482. // Destruktor
  483. Model3D::~Model3D()
  484. {
  485. if( model )
  486. model->release();
  487. if( textur )
  488. textur->release();
  489. if( skelett )
  490. skelett->release();
  491. animations->release();
  492. }
  493. // Fügt eine Animation hinzu
  494. // a: Die neue Animation
  495. void Model3D::addAnimation( Animation3D *a, double speed )
  496. {
  497. AnimationData *d = new AnimationData();
  498. d->a = a;
  499. d->speed = speed;
  500. d->offset = 0;
  501. animations->add( d );
  502. }
  503. // Entfernt eine Animation
  504. // zA: Die zu entfernende Animation
  505. void Model3D::removeAnimation( Animation3D *zA )
  506. {
  507. for( int i = 0; i < animations->getEintragAnzahl(); i++ )
  508. {
  509. if( animations->z( i )->a == zA )
  510. {
  511. animations->remove( i );
  512. return;
  513. }
  514. }
  515. }
  516. // Setzt den Zeiger auf das zum Annimieren verwendete Skelett
  517. // s: Das Skelett, das verwendet werden soll
  518. void Model3D::setSkelettZ( Skelett *s )
  519. {
  520. if( skelett )
  521. skelett->release();
  522. skelett = s;
  523. }
  524. // Setzt die Daten des Models
  525. // data: Die Daten
  526. void Model3D::setModelDaten( Model3DData *data )
  527. {
  528. if( model )
  529. model->release();
  530. model = data;
  531. }
  532. // Setzt die zum Zeichnen zu benutzenden Texturen
  533. // txt: Ein Liste mit Texturen zu den verschiedenen Polygonen zugeordnet
  534. void Model3D::setModelTextur( Model3DTextur *txt )
  535. {
  536. if( textur )
  537. textur->release();
  538. textur = txt;
  539. }
  540. // Errechnet die Matrizen aller Knochen des Skeletts des Models
  541. // viewProj: Die miteinander multiplizierten Kameramatrizen
  542. // matBuffer: Ein Array mit Matrizen, der gefüllt werden soll
  543. // return: Die Anzahl der Matrizen, die das Model benötigt
  544. int Model3D::errechneMatrizen( Mat4< float > &viewProj, Mat4< float > *matBuffer )
  545. {
  546. int ret = 0;
  547. if( skelett )
  548. ret = skelett->kalkulateMatrix( welt, matBuffer, viewProj );
  549. else if( model )
  550. ret = model->kalkulateMatrix( welt, matBuffer, viewProj );
  551. if( !ret )
  552. return Zeichnung3D::errechneMatrizen( viewProj, matBuffer );
  553. return ret;
  554. }
  555. // Verarbeitet die vergangene Zeit
  556. // tickval: Die zeit in sekunden, die seit dem letzten Aufruf der Funktion vergangen ist
  557. // return: true, wenn sich das Objekt verändert hat, false sonnst.
  558. bool Model3D::tick( double tickval )
  559. {
  560. radius = model ? model->getRadius() : 0;
  561. if( skelett )
  562. {
  563. radius += skelett->getRadius();
  564. for( auto i = animations->getArray(); i.set && i.var; i++ )
  565. {
  566. rend = i.var->speed > 0;
  567. i.var->a->apply( skelett, i.var->offset, tickval * i.var->speed );
  568. }
  569. }
  570. return Zeichnung3D::tick( tickval );
  571. }
  572. // Zeichnet das Model
  573. // zRObj: Ein Zeiger auf das Objekt, das zum Zeichnen verwendet werden soll (ohne erhöhten Reference Counter)
  574. void Model3D::render( Render3D *zRObj )
  575. {
  576. if( !model )
  577. return;
  578. model->aktualisiereVertecies( zRObj );
  579. #ifdef WIN32
  580. zRObj->beginnModel( this );
  581. #endif
  582. model->render( welt, textur, zRObj );
  583. }
  584. // Gibt die Id der Daten zurück, wenn sie in einer Model3DList registriert wurden. (siehe Framework::zM3DRegister())
  585. int Model3D::getDatenId() const
  586. {
  587. return model ? model->getId() : -1;
  588. }
  589. // Gibt einen Buffer mit allen Vertecies des Models zurück
  590. const DXVertexBuffer *Model3D::zVertexBuffer() const
  591. {
  592. return model ? model->zVertexBuffer() : 0;
  593. }
  594. // Erhöht den Reference Counting Zähler.
  595. // return: this.
  596. Model3D *Model3D::getThis()
  597. {
  598. ref++;
  599. return this;
  600. }
  601. // Verringert den Reference Counting Zähler. Wenn der Zähler 0 erreicht, wird das Zeichnung automatisch gelöscht.
  602. // return: 0.
  603. Model3D *Model3D::release()
  604. {
  605. ref--;
  606. if( !ref )
  607. delete this;
  608. return 0;
  609. }