JNR
laAnimatedModel.cpp
1 /*
2 
3 Jump'n'Run Engine
4 http://www.atanaslaskov.com/jnr/
5 
6 BSD LICENSE
7 Copyright (c) 2007-2013, Atanas Laskov
8 All rights reserved.
9 
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
12 1. Redistributions of source code must retain the above copyright notice,
13 this list of conditions and the following disclaimer.
14 2. Redistributions in binary form must reproduce the above copyright notice,
15 this list of conditions and the following disclaimer in the documentation
16 and/or other materials provided with the distribution.
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL ATANAS LASKOV BE LIABLE FOR ANY
21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 */
29 
30 //
31 // FILE: laAnimatedModel.cpp
32 //
33 // Copyright (C) 2007-2013 Atanas Laskov, <latanas@gmail.com>
34 //
35 #include "stdafx.h"
36 #include "Core.h"
37 #include "Core.h"
38 
39 // Constructor
40 //
41 laAnimatedModel::laAnimatedModel(void)
42 {
43  _arFrames = NULL;
44  _nFrameCnt = 1;
45  _nCrFrame = 0;
46  _dSPF = 1.0;
47  _dTimeElapsed = 0;
48 
49  _bCycle = M_TRUE;
50  _bInterpolation = M_TRUE;
51  _bTraceEffect = M_FALSE;
52 }
53 
54 laAnimatedModel::~laAnimatedModel(void) {
55  discard();
56 }
57 
58 void laAnimatedModel::discard() {
59  if(_arFrames) delete [] _arFrames;
60  _arFrames = NULL;
61 }
62 
63 bool laAnimatedModel::load(char* strFile)
64 {
65  ERRORLEVEL_BEGIN;
66  ASSERT(strnlen(strFile, 64) < 64, "Invalid file name");
67  laFileParser f;
68  char strFilePath[128]=M_DIR_MODEL;
69  static char junk[1024];
70  static char ver[128];
71  static char cr[128];
72  static int nverts,ntverts,nfaces;
73  static double sh;
74  _pCR = ::laSystemIntegrator::getRenderer();
75 
76  // Check if the requested file is a static model
77  //
78  if( strstr(strFile, ".jrm") )
79  {
80  _arFrames = new laStaticModel[1];
81  _nFrameCnt = 1;
82  _nCrFrame = 0;
83  _bInterpolation = M_FALSE;
84 
85  _arFrames[0].load(strFile);
86  setTexture( _arFrames[0]._nTex, _arFrames[0]._bEmissive, _arFrames[0]._nShininess);
87 
88  return true;
89  }
90 
91  MLOG("Loading animated model '%s'...", strFile);
92 
93  // Open the model file
94  //
95  if(!_bAddDirectoryPrefix) strFilePath[0]='\0';
96  strcat(strFilePath, strFile);
97 
98  try{ f.fileOpen(strFilePath); }catch(laError& ){ throw laError("Unable to open model file '%s'", strFile); }
99 
100  // General Information
101  //
102  try{ f.readSectionSeparator(junk); }catch(laError& ){ throw laError("Section separator expected but not found (while parsing '%s')", strFile); }
103  _strupr(junk);
104  if(strcmp(junk, "GENERAL INFORMATION")) throw laError("Section [GENERAL INFORMATION] expected, [%s] found ('%s')", junk, strFile);
105 
106  try{
107 
108  f.readText(ver);
109  ASSERT( !strcmp(ver, "Animated JRM 1"), "Unappropriate model format '%s' (while parsing '%s')", ver, f.getFileName());
110 
111  f.readText(cr);
112 
113  f.readInt(&nverts);
114  f.readInt(&ntverts, M_FALSE);
115  f.readInt(&nfaces, M_FALSE);
116 
117  ASSERT( nverts && ntverts && nverts, "Corrupted file '%s'", f.getFileName());
118  }
119  catch( laError& ) {
120  throw laError("Section [GENERAL INFORMATION] appears to be corrupted (while parsing '%s')", f.getFileName());
121  }
122 
123  // Texture
124  //
125  try{ f.readSectionSeparator(junk); }catch( laError& ){ throw laError("Section separator expected but not found (while parsing '%s')", strFile); }
126  _strupr(junk);
127  ASSERT(!strcmp(junk, "TEXTURE"), "Section [TEXTURE] expected, [%s] found ('%s')", junk, strFile);
128 
129  try{
130  f.readText(_strTex);
131  f.readBool(&_bEmissive);
132  f.readDouble(&sh);
133 
134  _nShininess = (unsigned) (sh*100);
135  }
136  catch(laError& ) {
137  throw laError("Section [TEXTURE] appears to be corrupted (while parsing '%s')", f.getFileName());
138  }
139 
140  _nTex.load(_strTex);
141  //if(!_nTex.id) throw laError("Texture map '%s' is corrupted or missing", _strTex);
142 
143  // Animation
144  //
145  try{ f.readSectionSeparator(junk); }catch(laError& ){ throw laError("Section separator expected but not found (while parsing '%s')", strFile); }
146  ASSERT( !strcmp(_strupr(junk), "ANIMATION"), "Section [ANIMATION] expected, [%s] found ('%s')", junk, strFile);
147 
148  try{
149  f.ctlSetSeparators(" f");
150 
151  f.readUnsigned(&_nFrameCnt);
152  f.readDouble(&_dSPF);
153  f.ctlSetSeparators(" ");
154  _dSPF = 1/_dSPF;
155 
156  _arFrames = new laStaticModel[_nFrameCnt];
157  }
158  catch(laError& ) {
159  throw laError("Section [ANIMATION] appears to be corrupted (while parsing '%s')", f.getFileName());
160  }
161 
162  //Load individual frames
163  //
164  char strCacheFile[256];
165  sprintf(strCacheFile, "%s.cache", strFilePath);
166  FILE* fCache_Read = fopen(strCacheFile, "rb");
167 
168  if(fCache_Read)
169  {
170  for(unsigned i=0; i<_nFrameCnt; i++)
171  {
172  _arFrames[i].create(nfaces);
173  _arFrames[i].setTexture(_nTex, _bEmissive, _nShininess);
174  _arFrames[i]._cache_load(fCache_Read);
175  }
176 
177  fclose(fCache_Read);
178  }
179  else
180  {
181  FILE* fCache_Write = fopen(strCacheFile, "wb");
182  ASSERT(fCache_Write, "Cannot open cache for writing (%s)", strCacheFile);
183 
184  for(unsigned i=0; i<_nFrameCnt; i++)
185  {
186  char strExpectedTitle[128], ss[2];
187  sprintf(strExpectedTitle, "FRAME %d", i+1);
188 
189  f.ctlGetSectionSep(ss);
190  f.ctlSetSectionSep("##");
191 
192  try{ f.readSectionSeparator(junk); }catch(laError& ){ throw laError("Frame label expected but not found (while parsing '%s')", strFile); }
193  ASSERT(! strcmp(_strupr(junk), strExpectedTitle), "Frame %d expected, #%s# found (while parsing '%s')", strExpectedTitle, junk, strFile);
194  f.ctlSetSectionSep(ss);
195 
196  try{
197  _arFrames[i].load(&f, nfaces, nverts, ntverts);
198  _arFrames[i].setTexture(_nTex, _bEmissive, _nShininess);
199 
200  _arFrames[i]._cache_save(fCache_Write);
201  }
202  catch(laError& pe) {
203  throw laError("%s\n\nFrame %d appears to be corrupted (while parsing '%s')", pe.getText(), i, f.getFileName());
204  }
205  }
206  fclose(fCache_Write);
207  }
208 
209  return true;
210  ERRORLEVEL_END;
211 }
212 
213 // Make a transitional animation between two poses
214 //
215 void laAnimatedModel::blend(laStaticModel *pPose1, laStaticModel *pPose2, double dSec)
216 {
217  ERRORLEVEL_BEGIN;
218  ASSERT( pPose1->vertexCount() == pPose2->vertexCount(), "Incompatible poses");
219 
220  if( frameCount() != 2 ) {
221  discard();
222  _arFrames = new laStaticModel[2];
223  _nFrameCnt = 2;
224  }
225 
226  _arFrames[0].instantiate( pPose1 );
227  _arFrames[1].instantiate( pPose2 );
228 
229  _bInterpolation = M_TRUE;
230  _bCycle = M_FALSE;
231 
232  _dSPF = dSec;
233  _dTimeElapsed = 0;
234  _nCrFrame = 0;
235 
236  setTexture( *(pPose1->texture()), pPose1->_bEmissive, pPose1->_nShininess);
237 
238  ERRORLEVEL_END;
239 }
240 
241 // Make a snapshot of the current state of the animation
242 //
243 
244 struct _blend_data{
245  unsigned n_first, n;
246  double w1, w2;
247 
248  laPoint3 *vt, *v1,* v2;
249  laPoint3 *nt, *n1, *n2;
250  laPoint2 *uvt, *uv1, *uv2;
251 };
252 
253 inline void _blend(const _blend_data &bd)
254 {
255  for(unsigned i=bd.n_first; i < bd.n; i++) {
256  bd.vt[i] = bd.w1*bd.v1[i] + bd.w2*bd.v2[i];
257  bd.nt[i] = bd.w1*bd.n1[i] + bd.w2*bd.n2[i];
258  //bd.uvt[i] = bd.w1*bd.uv1[i] + bd.w2*bd.uv2[i];
259  }
260  memcpy(bd.uvt+bd.n_first, bd.uv1+bd.n_first, sizeof(laPoint2)*bd.n);
261 }
262 
263 /*class laSnapshotThread: public laThread
264 {
265  public:
266  _blend_data bd;
267  volatile M_BOOL bDone;
268 
269  laSnapshotThread() { bDone = M_FALSE; }
270 
271  virtual void run() {
272  _blend( bd );
273  bDone = M_TRUE;
274  }
275 };*/
276 
277 void laAnimatedModel::snapshot(laStaticModel* pm)
278 {
279  PROFILE_REN(laAnimatedModel_snapshot);
280  ERRORLEVEL_BEGIN;
281  //ASSERT(pm, "Nil snapshot destination");
282  //ASSERT(_arFrames && _nFrameCnt, "Nil frameset");
283 
284  laStaticModel *pcurrent = _arFrames + _nCrFrame;
285  laStaticModel *pnext = _arFrames + ( ((_nCrFrame+1)>=_nFrameCnt) ? (_bCycle?0:_nCrFrame) : _nCrFrame+1 );
286 
287  // Ensure snapshot destionation is of the correct size
288  //
289  if( pm->isLightweight() || (pm->vertexCount() != pcurrent->vertexCount()) ) {
290  pm->discard();
291  pm->create( pcurrent->vertexCount()/3 );
292  }
293  pm->setTexture( *(pcurrent->texture()), pcurrent->_bEmissive, pcurrent->_nShininess );
294 
295  // Blend current and next frame into the snapshot
296  //
297  _blend_data bd;
298  bd.w2 = M_MIN(1, _dTimeElapsed/_dSPF); bd.w1 = 1-bd.w2;
299  bd.vt = pm->_arFlatVertex; bd.v1 = pcurrent->_arFlatVertex; bd.v2 = pnext->_arFlatVertex;
300  bd.nt = pm->_arFlatNormal; bd.n1 = pcurrent->_arFlatNormal; bd.n2 = pnext->_arFlatNormal;
301  bd.uvt = pm->_arFlatTex; bd.uv1 = pcurrent->_arFlatTex; bd.uv2 = pnext->_arFlatTex;
302  bd.n_first = 0; bd.n = pm->vertexCount();
303 
304  /*laSnapshotThread thSnapshot;
305  thSnapshot.bd = bd;
306  laSystemIntegrator::getEnvironment()->thread(&thSnapshot);
307  bd.n_first = pm->vertexCount()/2;*/
308 
309  _blend( bd );
310 
311  //while( ! thSnapshot.bDone );
312  ERRORLEVEL_END;
313 }
314 
315 // Sample a vertex in the current state of the animation
316 //
317 laPoint3 laAnimatedModel::sample(unsigned nVertexIndex)
318 {
319  ERRORLEVEL_BEGIN;
320  ASSERT(_arFrames && _nFrameCnt, "Nil frameset");
321 
322  laStaticModel *pcurrent = _arFrames+_nCrFrame;
323  laStaticModel *pnext = _arFrames + ( ((_nCrFrame+1)>=_nFrameCnt) ? (_bCycle?0:_nCrFrame) : _nCrFrame+1 );
324  ASSERT(nVertexIndex < pcurrent->vertexCount(), "Requested vertex sample out of bounds", nVertexIndex);
325 
326  double w2 = M_MIN(1, _dTimeElapsed/_dSPF), w1 = 1-w2;
327 
328  return ( w1*(pcurrent->_arFlatVertex[nVertexIndex]) + w2*(pnext->_arFlatVertex[nVertexIndex]) );
329  ERRORLEVEL_END;
330 }
331 
332 //#include <GL\GL.h>
333 
334 void laAnimatedModel::draw(laRenderer *pr)
335 {
336  ERRORLEVEL_BEGIN;
337  PROFILE_REN(laAnimatedModel_draw);
338  ASSERT(_arFrames && _nFrameCnt, "Nil frameset");
339  ASSERT(_nCrFrame < _nFrameCnt, "Invalid frame");
340 
341  if(_nFrameCnt==1) {
342  _arFrames[0].draw(pr);
343  return;
344  }
345 
346  /*if(_bTraceEffect)
347  {
348  //glPushAttrib();
349 
350  unsigned a = 0.0;
351 
352  pr->modeBlend(M_TRUE);
353  pr->modeBlendFilter(0);
354 
355  //pr->modeTexture(M_FALSE);
356  pr->modeLight(M_FALSE);
357 
358  glDepthMask(GL_FALSE);
359 
360  for(int i=0; i<_nCrFrame; i++)
361  {
362  a += 30.0/_nCrFrame;
363 
364  pr->styleSet( laColor(255,255,255,a) );
365  _arFrames[i].texture()->use();
366  _arFrames[i]._draw_traingles( pos );
367  }
368 
369  pr->styleSet( laColor(255,255,255,255) );
370  glDepthMask(GL_TRUE);
371 
372  //glPopAttrib();
373  }*/
374 
375  if(_bInterpolation) { snapshot( &_snapshot ); _snapshot.draw( pr ); }
376  else _arFrames[_nCrFrame].draw(pr);
377 
378  ERRORLEVEL_END;
379 }
380 
381 void laAnimatedModel::animate(laTimer &t)
382 {
383  if( _nFrameCnt<=1 ) return;
384 
385  if( (_nCrFrame>=(_nFrameCnt-1)) && (!_bCycle) ) {
386  _nCrFrame = _nFrameCnt-1;
387  return;
388  }
389 
390  _dTimeElapsed += t.delta();
391 
392  while(_dTimeElapsed >= _dSPF)
393  {
394  _dTimeElapsed -= _dSPF;
395  _nCrFrame++;
396 
397  if(_nCrFrame>=_nFrameCnt)
398  {
399  if(_bCycle) _nCrFrame=0;
400  else
401  {
402  _nCrFrame = _nFrameCnt-1;
403  _dTimeElapsed = 0;
404  return;
405  }
406  }
407  }
408 
409  //snapshot( &_snapshot );
410 }
411 
412 void laAnimatedModel::buildCollisionData(class laCollisionDomain* pDomain, rpgTrap* pTrap) {
413  _arFrames[0].buildCollisionData(pDomain, pTrap);
414 }
415 
416 char* laAnimatedModel::strTexture() {
417  return _arFrames[_nCrFrame].strTexture();
418 }
419 
420 unsigned laAnimatedModel::vertexCount() {
421  return _arFrames[_nCrFrame].vertexCount();
422 }
423 
424 laPoint3 laAnimatedModel::boundaryMin() { return _arFrames[0].boundaryMin(); }
425 laPoint3 laAnimatedModel::boundaryMax() { return _arFrames[0].boundaryMax(); }
426 
427 void laAnimatedModel::edScale(laPoint3 sz) {
428  for(unsigned i=0; i<_nFrameCnt; i++) _arFrames[i].edScale(sz);
429 }
430 
431 void laAnimatedModel::edTranslate(laPoint3 pos) {
432  for(unsigned i=0; i<_nFrameCnt; i++) _arFrames[i].edTranslate(pos);
433 }
434 
435 void laAnimatedModel::edNormalize() {
436  for(unsigned i=0; i<_nFrameCnt; i++) _arFrames[i].edNormalize();
437 }
438 
439 void laAnimatedModel::edMinaxis() {
440  for(unsigned i=0; i<_nFrameCnt; i++) _arFrames[i].edMinaxis();
441 }
442 
443 void laAnimatedModel::edMaxaxis() {
444  for(unsigned i=0; i<_nFrameCnt; i++) _arFrames[i].edMaxaxis();
445 }
446 
447 void laAnimatedModel::edCentralize() {
448  for(unsigned i=0; i<_nFrameCnt; i++) _arFrames[i].edCentralize();
449 }
450 
451 void laAnimatedModel::edSnap() {
452  for(unsigned i=0; i<_nFrameCnt; i++) _arFrames[i].edSnap();
453 }
454 
455 void laAnimatedModel::edInvertAnimation()
456 {
457  laStaticModel f;
458 
459  for(unsigned i=0; i<_nFrameCnt/2; i++)
460  {
461  f.instantiate( _arFrames+i );
462  _arFrames[i].instantiate( _arFrames + _nFrameCnt-i-1 );
463  _arFrames[_nFrameCnt-i-1].instantiate( &f );
464  }
465 }
2D Point
Definition: laPoint_novec.h:41
#define M_DIR_MODEL
3D models directory
Trap Properties.
Definition: rpgTrap.h:42
Collision Domain.
Virtual interface for the Engine graphics renderer.
Definition: laRenderer.h:98
Static 3D Model.
Definition: laStaticModel.h:44
Error handling class.
Definition: laError.h:46
File Parser.
Definition: laFileParser.h:41