JNR
laSegment.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: laSegment.cpp
32 // This class represents a signle segment of the level. The whole level is slinced in several
33 // such segments so that at any given time only the visible ones have to be rendered.
34 //
35 // Copyright (C) 2007-2013 Atanas Laskov, <latanas@gmail.com>
36 //
37 #include "stdafx.h"
38 #include "Core-Level-JR.h"
39 
40 // Constructor
41 //
42 laSegment::laSegment(unsigned nIndex)
43 {
44  // Initalize member variables
45  //
46  _nSegmentIndex = nIndex;
47 
48  _pTileset = NULL;
49  _pPrev = _pNext = NULL;
50 
51  _nBackgroundElement = 0;
52  _nLitterBoxCount = 0;
53  _nCompilationID = 0;
54 
55  _bObjectListReset = M_FALSE;
56 
57  // Link the collision domains of individual tiles
58  //
59  laCollisionDomain *pNext;
60 
61  for(int y=0; y<M_SEGH; y++) for(int x=0; x<M_SEGW; x++)
62  {
63  if( x>=(M_SEGW-1) )
64  {
65  if(y>=(M_SEGH-1)) pNext = NULL;
66  else pNext = _arTiles[0][y+1].getDomain();
67  }
68  else pNext = _arTiles[x+1][y].getDomain();
69 
70  _arTiles[x][y].getDomain()->setNextDomain(pNext);
71  _arTiles[x][y]._pSegment = this;
72  _arTiles[x][y]._nSegX = x;
73  _arTiles[x][y]._nSegY = y;
74  }
75 
76  srand(::GetTickCount());
77  _update_wave();
78 }
79 
80 laSegment::~laSegment(void){
81 }
82 
83 // Get terrain curviture properties form the TS
84 // and randomize them
85 //
86 void laSegment::_update_curviture_parameters()
87 {
88  laPropertyList *pl = getTS()->getElement("terrain-curviture");
89 
90  _arTerrainCurvitureParams[0] = pl->getDouble("big") + pl->getDouble("big-variation")*(rand()%100)/100.0; // big-scale
91  _arTerrainCurvitureParams[1] = pl->getDouble("strange") + pl->getDouble("strange-variation")*(rand()%100)/100.0; // strange
92  _arTerrainCurvitureParams[2] = pl->getDouble("small") + pl->getDouble("small-variation")*(rand()%100)/100.0; // small-scale
93  _arTerrainCurvitureParams[3] = 0;
94 }
95 
96 
97 // Load segment from the level file
98 //
99 void laSegment::load(laFileParser* fp)
100 {
101  unsigned j=0;
102  laCollisionDomain *pNext;
103 
104  //Load the litter boxes
105  fp->readUnsigned(&_nLitterBoxCount);
106 
107  if(_nLitterBoxCount>=M_MAXLITTER_BOX)
108  throw laError("laSegment::load() failed: Too many litter boxes (%d requested, %d allowed)",
109  _nLitterBoxCount, M_MAXLITTER_BOX);
110 
111  for(unsigned i=0; i<_nLitterBoxCount; i++)
112  {
113  fp->readObj(_arLitterBox+i, M_FALSE);
114  _arLitterBox[i].litter(_pTileset);
115  }
116 
117  //Load the tiles
118  //
119  for(int y=0; y<M_SEGH; y++)
120  {
121  for(int x=0; x<M_SEGW; x++)
122  {
123  //Read the object
124  _arTiles[x][y].setTS(_pTileset);
125  fp->readObj( &(_arTiles[x][y]), M_FALSE );
126  }
127  }
128 }
129 
130 // Draw the terrain
131 //
132 void laSegment::drawTerrain(laRenderer *r)
133 {
134  PROFILE_REN(laSegment_drawTerrain);
135  ASSERT(_pTileset, "Invalid tileset object");
136  ASSERT(_pLevel, "Invalid level object");
137 
138  // Draw background
139  // TODO: This should be moved to drawBackground() and the offset appied ther for all segs at once ?
140  //
141  r->transPush();
142  r->transTranslate( laPoint3(0,M_UNIT*M_SEGH) );
143  _pTileset->getElement(_nBackgroundElement)->drawGeometry(r);
144 
145  for(unsigned i=0; i<_nLitterBoxCount; i++)
146  _arLitterBox[i].drawGeometry(r, laPoint3());
147 
148  r->transPop();
149 
150  // Foreground
151  //
152  if(_nCompilationID)
153  {
154  // Draw everything as a precompitled object
155  //
156  r->compiledDraw( _nCompilationID );
157  _draw_dynamic(r); //< except animated stuff
158  }
159  else
160  {
161  // Draw tiles indivudualy; remember in compilation _nCompilationID
162  //
163  _nCompilationID = r->compiledActivate(M_TRUE);
164  _draw_static(r);
165  r->compiledActivate(M_FALSE);
166  }
167 }
168 
169 void laSegment::_draw_static(laRenderer *r)
170 {
171  PROFILE_REN(laSegment__draw_static);
172 
173  double dCurrentOffset, dCurrentAngle, dPreviousAngle, dScale;
174 
175  for(int x=0; x<M_SEGW; x++)
176  {
177  // For each column calculate the Z-offset
178  // and curivture angle
179  //
180  dCurrentOffset = terrainZOffset(x);
181  dCurrentAngle = terrainAngle(x);
182  dPreviousAngle = terrainAngle(x-1);
183  dScale = terrainScale(x);
184 
185  for(unsigned y=0; y<M_SEGH; y++)
186  {
187  if( _arTiles[x][y].isEmpty() ) continue;
188 
189  //r->vtexOffset( laPoint3(M_UNIT*x, M_UNIT*y) );
190  laModel::edRotate(M_TRUE, -dCurrentAngle);
191  laModel::edBend(M_TRUE, -dPreviousAngle + dCurrentAngle, dScale);
192  laModel::edOffset(M_TRUE, laPoint3(M_UNIT*x, M_UNIT*y, dCurrentOffset) );
193 
194  _arTiles[x][y].drawGeometry(r);
195 
196  laModel::edRotate(M_FALSE);
197  laModel::edBend(M_FALSE);
198  laModel::edOffset(M_FALSE);
199  }
200  }
201 }
202 
203 // Draw tile geometry that cannot be cached
204 //
205 void laSegment::_draw_dynamic(laRenderer *r)
206 {
207  PROFILE_REN(laSegment__draw_dynamic);
208 
209  // Terrain
210  //
211  double dCurrentOffset, dCurrentAngle;
212 
213  for(int x=0; x<M_SEGW; x++)
214  {
215  dCurrentOffset = terrainZOffset(x);
216  dCurrentAngle = M_R2D(terrainAngle(x));
217 
218  for(unsigned y=0; y<M_SEGH; y++)
219  {
220  if( _arTiles[x][y].isEmpty() || _arTiles[x][y].isStatic() ) continue;
221 
222  r->transPush();
223  r->transTranslate( laPoint3(M_UNIT*x, M_UNIT*y, dCurrentOffset + M_UNIT/2.0) );
224  r->transRotate( dCurrentAngle, laPoint3(0,1,0)) ;
225  r->transTranslate( laPoint3(0, 0, -M_UNIT/2.0) );
226 
227  //Draw
228  _arTiles[x][y].drawDynamicGeometry( r );
229  r->transPop();
230  }
231  }
232 
233 }
234 
235 void laSegment::drawObjects(laRenderer *r) {
236  PROFILE_REN(laSegment__draw_objects);
237  std::list<class laObject*>::iterator i;
238 
239  for(i=_listObjects.begin(); i!= _listObjects.end(); i++) (*i)->drawGeometry(r, laPoint3());
240 }
241 
242 // Draw visual effects associated with the terrain grid
243 //
244 void laSegment::drawFx(laRenderer *r, const laPoint3 &pos)
245 {
246  PROFILE_REN(laSegment_drawFx);
247  ASSERT(_pTileset, "Invalid tileset object");
248  ASSERT(_pLevel, "Invalid level object");
249 
250  // Terrain
251  //
252  double dCurrentOffset, dCurrentAngle;
253 
254  for(unsigned x=0; x<M_SEGW; x++)
255  {
256  dCurrentOffset = terrainZOffset(x);
257  dCurrentAngle = M_R2D(terrainAngle(x));
258 
259  for(unsigned i=0; i<M_SEGH; i++)
260  {
261  if( _arTiles[x][i].isEmpty() || _arTiles[x][i].isWithoutFx() ) continue;
262 
263  // Position the tile
264  // NOTE: No rotation intentionaly - it slows down things noticeably; it should not be needed for Fx,
265  //
266  r->transPush();
267  r->transTranslate( pos + laPoint3(M_UNIT*x, M_UNIT*i, dCurrentOffset) );
268  _arTiles[x][i].drawFx(r);
269  r->transPop();
270  }
271  }
272 
273  // Objects
274  //
275  std::list<class laObject*>::iterator i;
276 
277  for(i=_listObjects.begin(); i!= _listObjects.end(); i++) (*i)->drawFx(r, laPoint3());
278 
279  // Litter boxes bg
280  //
281  for(unsigned i=0; i<_nLitterBoxCount; i++) _arLitterBox[i].drawFx(r, laPoint3(0,M_UNIT*M_SEGH));
282 }
283 
284 // Animate the segment
285 //
286 void laSegment::animate(laTimer &t)
287 {
288  ERRORLEVEL_BEGIN;
289  PROFILE_ANIM(laSegment_animate);
290  ASSERT(_pTileset, "Invalid tileset object");
291  ASSERT(_pLevel, "Invalid level object");
292  std::list<class laObject*>::iterator i = _listObjects.begin();
293 
294  // Animate objects contained in this segment
295  // NOTE: This also takes care that any object out of bounds migrate to neighbouring segments
296  //
297  while( i != _listObjects.end() )
298  {
299  (*i)->animate(t);
300 
301  // In case sombody called _init_object_list() ( or otherwise changed the list ),
302  // it's no longer possible to use the old iterator.
303  // NOTE: This happens on rewspawn events / when the player character dies
304  //
305  if(_bObjectListReset) {
306  _bObjectListReset = M_FALSE;
307  break;
308  }
309 
310  if( _pNext && ((*i)->getPosition().x() >= (_nSegmentIndex + 1) * M_UNIT * M_SEGW) )
311  {
312  _pNext->_migrate_object( *i );
313  i = _listObjects.erase( i );
314  }
315  else if( _pPrev && ((*i)->getPosition().x() < _nSegmentIndex * M_UNIT * M_SEGW) )
316  {
317  _pPrev->_migrate_object( *i );
318  i = _listObjects.erase( i );
319  }
320  else i++;
321 
322  ERRORLEVEL_ARG(ERRORLEVEL_ARGVAL + 1);
323  }
324 
325  // Terrain
326  for(unsigned i=0; i<M_SEGH; i++) for(unsigned j=0; j<M_SEGW; j++)
327  _arTiles[j][i].animate(t);
328 
329  _update_wave();
330 
331  ERRORLEVEL_END;
332 }
333 
334 // Initialize the list of objects within bounds
335 // Called by friend laLevel::load()
336 //
337 void laSegment::_init_object_list ( class laObject** par, unsigned n )
338 {
339  _bObjectListReset = M_TRUE; // Indicate the list has changed; laSegment::animate() needs to know if it is active
340  _listObjects.clear();
341 
342  for(unsigned i=0; i<n; i++)
343  {
344  if( par[i]->getPosition().x() >= (_nSegmentIndex + 1) * M_UNIT * M_SEGW ) continue;
345  if( par[i]->getPosition().x() < _nSegmentIndex * M_UNIT * M_SEGW ) continue;
346 
347  _migrate_object( par[i] );
348  }
349 
350  //MLOG("%d objects distributed to segment %d", _listObjects.size(), _nSegmentIndex);
351 }
352 
353 // Animate the sin wave
354 //
355 void laSegment::_update_wave()
356 {
357  PROFILE_ANIM(laSegment__update_wave);
358 
359  double dWaveOffset = (_pTileset) ? _pTileset->getAnimatedWaveOffset() : 0;
360  double dx;
361 
362  for(unsigned x=0; x<=M_SEGW; x++)
363  {
364  dx = (_nSegmentIndex*(M_SEGW) + x)*M_UNIT*WAVE_FREQUENCY + dWaveOffset;
365  _arWave[x] = sin( dx );
366  }
367 }
368 
369 // Initalize the collision domains of all tiles in the segment
370 //
371 void laSegment::bulidDomains(laPoint3 pos)
372 {
373  laPoint3 ptTilePosition;
374 
375  _nCollisionDomainSize = 0;
376  _nCollisionDomains = 0;
377 
378  for(unsigned i=0; i<M_SEGH; i++) for(unsigned j=0; j<M_SEGW; j++)
379  {
380  ptTilePosition = pos + laPoint3(j, i)*M_UNIT;
381 
382  unsigned n = _arTiles[j][i].bulidDomain( ptTilePosition );
383 
384  _nCollisionDomainSize += n;
385  if(n) _nCollisionDomains++;
386  }
387 
388  MLOG("This segment contains %d collision lines in %d domains", _nCollisionDomainSize, _nCollisionDomains);
389 }
390 
391 
392 double laSegment::terrainZOffset(int nX) {
393  return _pLevel->terrainZOffset(nX + _nSegmentIndex*M_SEGW);
394 }
395 
396 double laSegment::terrainAngle(int nX) {
397  return _pLevel->terrainAngle(nX + _nSegmentIndex*M_SEGW);
398 }
399 
400 double laSegment::terrainScale(int nX) {
401  return _pLevel->terrainScale(nX + _nSegmentIndex*M_SEGW);
402 }
#define M_R2D(r)
Convert radians to degrees.
#define M_UNIT
Unit of 1 meter.
Collision Domain.
Virtual interface for the Engine graphics renderer.
Definition: laRenderer.h:98
Error handling class.
Definition: laError.h:46
Base Class for Level Objects.
Definition: laObject.h:43
File Parser.
Definition: laFileParser.h:41