comparison engine/core/view/camera.cpp @ 0:4a0efb7baf70

* Datasets becomes the new trunk and retires after that :-)
author mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222
date Sun, 29 Jun 2008 18:44:17 +0000
parents
children ab09325f901e
comparison
equal deleted inserted replaced
-1:000000000000 0:4a0efb7baf70
1 /***************************************************************************
2 * Copyright (C) 2005-2008 by the FIFE team *
3 * http://www.fifengine.de *
4 * This file is part of FIFE. *
5 * *
6 * FIFE is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
20 ***************************************************************************/
21
22 // Standard C++ library includes
23
24 // 3rd party library includes
25 #include <SDL.h>
26
27 // FIFE includes
28 // These includes are split up in two parts, separated by one empty line
29 // First block: files included from the FIFE root src directory
30 // Second block: files included from the same folder
31
32 #include "model/metamodel/grids/cellgrid.h"
33 #include "model/metamodel/action.h"
34 #include "model/metamodel/timeprovider.h"
35 #include "model/structures/map.h"
36 #include "model/structures/layer.h"
37 #include "model/structures/instancetree.h"
38 #include "model/structures/instance.h"
39 #include "model/structures/location.h"
40 #include "util/log/logger.h"
41 #include "util/math/fife_math.h"
42 #include "util/time/timemanager.h"
43 #include "video/renderbackend.h"
44 #include "video/image.h"
45 #include "video/imagepool.h"
46 #include "video/animation.h"
47 #include "video/animationpool.h"
48
49 #include "camera.h"
50 #include "visual.h"
51
52
53 namespace FIFE {
54 static Logger _log(LM_CAMERA);
55
56 Camera::Camera(const std::string& id,
57 Layer *layer,
58 Rect viewport,
59 ExactModelCoordinate emc,
60 RenderBackend* renderbackend,
61 ImagePool* ipool,
62 AnimationPool* apool):
63 m_id(id),
64 m_matrix(),
65 m_inverse_matrix(),
66 m_tilt(0),
67 m_rotation(0),
68 m_zoom(1),
69 m_location(),
70 m_prev_origo(ScreenPoint(0,0,0)),
71 m_cur_origo(ScreenPoint(0,0,0)),
72 m_viewport(),
73 m_screen_cell_width(1),
74 m_screen_cell_height(1),
75 m_reference_scale(1),
76 m_enabled(true),
77 m_attachedto(NULL),
78 m_image_dimensions(),
79 m_iswarped(false),
80 m_renderers(),
81 m_pipeline(),
82 m_updated(false),
83 m_renderbackend(renderbackend),
84 m_ipool(ipool),
85 m_apool(apool),
86 m_layer_to_instances() {
87 m_location.setLayer(layer);
88 m_location.setExactLayerCoordinates(emc);
89 m_viewport = viewport;
90 }
91
92 Camera::~Camera() {
93 std::map<std::string, RendererBase*>::iterator r_it = m_renderers.begin();
94 for(; r_it != m_renderers.end(); ++r_it) {
95 delete r_it->second;
96 }
97 m_renderers.clear();
98 }
99
100 void Camera::setTilt(double tilt) {
101 m_tilt = tilt;
102 updateReferenceScale();
103 updateMatrices();
104 m_iswarped = true;
105 }
106
107 double Camera::getTilt() const {
108 return m_tilt;
109 }
110
111 void Camera::setRotation(double rotation) {
112 m_rotation = rotation;
113 updateReferenceScale();
114 updateMatrices();
115 m_iswarped = true;
116 }
117
118 double Camera::getRotation() const {
119 return m_rotation;
120 }
121
122 void Camera::setZoom(double zoom) {
123 m_zoom = zoom;
124 if (m_zoom < 0.001) {
125 m_zoom = 0.001;
126 }
127 updateMatrices();
128 m_iswarped = true;
129 }
130
131 double Camera::getZoom() const {
132 return m_zoom;
133 }
134
135 void Camera::setCellImageDimensions(unsigned int width, unsigned int height) {
136 m_screen_cell_width = width;
137 m_screen_cell_height = height;
138 updateReferenceScale();
139 updateMatrices();
140 m_iswarped = true;
141 }
142
143 void Camera::setLocation(const Location& location) {
144 // initialize first set properly
145 if ((m_prev_origo == m_cur_origo) && (m_prev_origo == ScreenPoint(0,0,0))) {
146 m_cur_origo = toScreenCoordinates(ExactModelCoordinate(0,0,0));
147 m_prev_origo = m_cur_origo;
148 }
149 m_location = location;
150
151 CellGrid* cg = NULL;
152 if (m_location.getLayer()) {
153 cg = m_location.getLayer()->getCellGrid();
154 } else {
155 throw Exception("Location without layer given to Camera::setLocation");
156 }
157 if (!cg) {
158 throw Exception("Camera layer has no cellgrid specified");
159 }
160
161 updateMatrices();
162
163 m_prev_origo = m_cur_origo;
164 m_cur_origo = toScreenCoordinates(ExactModelCoordinate(0,0,0));
165 }
166
167 Point Camera::getCellImageDimensions() {
168 return getCellImageDimensions(m_location.getLayer());
169 }
170
171 Point Camera::getCellImageDimensions(Layer* layer) {
172 if (layer == m_location.getLayer()) {
173 return Point( m_screen_cell_width, m_screen_cell_height );
174 }
175 std::map<Layer*, Point>::iterator it = m_image_dimensions.find(layer);
176 if (it != m_image_dimensions.end()) {
177 return it->second;
178 }
179 Point p;
180 CellGrid* cg = layer->getCellGrid();
181 assert(cg);
182 DoublePoint dimensions = getLogicalCellDimensions(layer);
183 p.x = static_cast<int>(round(m_reference_scale * dimensions.x));
184 p.y = static_cast<int>(round(m_reference_scale * dimensions.y));
185 m_image_dimensions[layer] = p;
186 return p;
187 }
188
189 Location Camera::getLocation() const {
190 return m_location;
191 }
192
193 Location& Camera::getLocationRef() {
194 return m_location;
195 }
196
197 void Camera::setViewPort(const Rect& viewport) {
198 m_viewport = viewport;
199 }
200
201 const Rect& Camera::getViewPort() const {
202 return m_viewport;
203 }
204
205 void Camera::setEnabled(bool enabled) {
206 m_enabled = enabled;
207 }
208
209 bool Camera::isEnabled() {
210 return m_enabled;
211 }
212
213 void Camera::updateMatrices() {
214 double scale = m_reference_scale;
215 m_matrix.loadScale(scale, scale, scale);
216 if (m_location.getLayer()) {
217 CellGrid* cg = m_location.getLayer()->getCellGrid();
218 if (cg) {
219 ExactModelCoordinate pt = m_location.getMapCoordinates();
220 m_matrix.applyTranslate( -pt.x *m_reference_scale,-pt.y *m_reference_scale, 0);
221 }
222 }
223 scale = m_zoom;
224 m_matrix.applyScale(scale, scale, scale);
225 m_matrix.applyRotate(-m_rotation, 0.0, 0.0, 1.0);
226 m_matrix.applyRotate(-m_tilt, 1.0, 0.0, 0.0);
227 m_inverse_matrix = m_matrix.inverse();
228 }
229
230 void Camera::calculateZValue(ScreenPoint& screen_coords) {
231 int dy = -(screen_coords.y - toScreenCoordinates(m_location.getMapCoordinates()).y);
232 screen_coords.z = static_cast<int>(tan(m_tilt * (M_PI / 180.0)) * static_cast<double>(dy));
233 }
234
235 ExactModelCoordinate Camera::toMapCoordinates(ScreenPoint screen_coords, bool z_calculated) {
236 if (!z_calculated) {
237 calculateZValue(screen_coords);
238 }
239 screen_coords.x -= m_viewport.w / 2;
240 screen_coords.y -= m_viewport.h / 2;
241
242 return m_inverse_matrix * intPt2doublePt(screen_coords);
243 }
244
245 ScreenPoint Camera::toScreenCoordinates(ExactModelCoordinate elevation_coords) {
246 ExactModelCoordinate p = elevation_coords;
247 ScreenPoint pt = doublePt2intPt( m_matrix* p );
248 pt.x += m_viewport.w / 2;
249 pt.y += m_viewport.h / 2;
250 return pt;
251 }
252
253 DoublePoint Camera::getLogicalCellDimensions(Layer* layer) {
254 CellGrid* cg = NULL;
255 if (layer) {
256 cg = layer->getCellGrid();
257 }
258 assert(cg);
259
260 ModelCoordinate cell(0,0);
261 std::vector<ExactModelCoordinate> vertices;
262 cg->getVertices(vertices, cell);
263
264 DoubleMatrix mtx;
265 mtx.loadRotate(m_rotation, 0.0, 0.0, 1.0);
266 mtx.applyRotate(m_tilt, 1.0, 0.0, 0.0);
267 double x1, x2, y1, y2;
268 for (unsigned int i = 0; i < vertices.size(); i++) {
269 vertices[i] = cg->toMapCoordinates(vertices[i]);
270 vertices[i] = mtx * vertices[i];
271 if (i == 0) {
272 x1 = x2 = vertices[0].x;
273 y1 = y2 = vertices[0].y;
274 } else {
275 x1 = std::min(vertices[i].x, x1);
276 x2 = std::max(vertices[i].x, x2);
277 y1 = std::min(vertices[i].y, y1);
278 y2 = std::max(vertices[i].y, y2);
279 }
280 }
281 return DoublePoint( x2 - x1, y2 - y1 );
282 }
283
284 void Camera::updateReferenceScale() {
285 DoublePoint dim = getLogicalCellDimensions(m_location.getLayer());
286 m_reference_scale = static_cast<double>(m_screen_cell_width) / dim.x;
287
288 FL_DBG(_log, "Updating reference scale");
289 FL_DBG(_log, LMsg(" tilt=") << m_tilt << " rot=" << m_rotation);
290 FL_DBG(_log, LMsg(" m_screen_cell_width=") << m_screen_cell_width);
291 }
292
293 void Camera::getMatchingInstances(ScreenPoint screen_coords, Layer& layer, std::list<Instance*>& instances) {
294 instances.clear();
295 const std::vector<Instance*>& layer_instances = m_layer_to_instances[&layer];
296 std::vector<Instance*>::const_iterator instance_it = layer_instances.end();
297 while (instance_it != layer_instances.begin()) {
298 --instance_it;
299 Instance* i = (*instance_it);
300 InstanceVisual* visual = i->getVisual<InstanceVisual>();
301 InstanceVisualCacheItem& vc = visual->getCacheItem(this);
302 if ((vc.dimensions.contains(Point(screen_coords.x, screen_coords.y)))) {
303 assert(vc.image);
304 Uint8 r, g, b, a;
305 int x = screen_coords.x - vc.dimensions.x;
306 int y = screen_coords.y - vc.dimensions.y;
307 if (m_zoom != 1.0) {
308 double fx = static_cast<double>(x);
309 double fy = static_cast<double>(y);
310 double fow = static_cast<double>(vc.image->getWidth());
311 double foh = static_cast<double>(vc.image->getHeight());
312 double fsw = static_cast<double>(vc.dimensions.w);
313 double fsh = static_cast<double>(vc.dimensions.h);
314 x = static_cast<int>(round(fx / fsw * fow));
315 y = static_cast<int>(round(fy / fsh * foh));
316 }
317 vc.image->getPixelRGBA(x, y, &r, &b, &g, &a);
318 // instance is hit with mouse if not totally transparent
319 if (a != 0) {
320 instances.push_back(i);
321 }
322 }
323 }
324 }
325
326 void Camera::getMatchingInstances(Location& loc, std::list<Instance*>& instances, bool use_exactcoordinates) {
327 instances.clear();
328 const std::vector<Instance*>& layer_instances = m_layer_to_instances[loc.getLayer()];
329 std::vector<Instance*>::const_iterator instance_it = layer_instances.end();
330 while (instance_it != layer_instances.begin()) {
331 --instance_it;
332 Instance* i = (*instance_it);
333 if (use_exactcoordinates) {
334 if (i->getLocationRef().getExactLayerCoordinatesRef() == loc.getExactLayerCoordinatesRef()) {
335 instances.push_back(i);
336 }
337 } else {
338 if (i->getLocationRef().getLayerCoordinates() == loc.getLayerCoordinates()) {
339 instances.push_back(i);
340 }
341 }
342 }
343 }
344
345 void Camera::attach(Instance *instance) {
346 m_attachedto = instance;
347 }
348
349 void Camera::detach() {
350 m_attachedto = NULL;
351 }
352
353 void Camera::update() {
354 if( !m_attachedto ) {
355 return;
356 }
357 Location loc(m_location);
358 loc.setExactLayerCoordinates( m_attachedto->getLocationRef().getExactLayerCoordinates(m_location.getLayer()) );
359 setLocation(loc);
360 updateMatrices();
361 }
362
363 void Camera::refresh() {
364 updateMatrices();
365 m_iswarped = true;
366 }
367
368 void Camera::resetUpdates() {
369 m_iswarped = false;
370 m_prev_origo = m_cur_origo;
371 }
372
373 bool pipelineSort(const RendererBase* lhs, const RendererBase* rhs) {
374 return (lhs->getPipelinePosition() < rhs->getPipelinePosition());
375 }
376
377 void Camera::addRenderer(RendererBase* renderer) {
378 renderer->setRendererListener(this);
379 m_renderers[renderer->getName()] = renderer;
380 if (renderer->isEnabled()) {
381 m_pipeline.push_back(renderer);
382 }
383 m_pipeline.sort(pipelineSort);
384 }
385
386 void Camera::onRendererPipelinePositionChanged(RendererBase* renderer) {
387 m_pipeline.sort(pipelineSort);
388 }
389
390 void Camera::onRendererEnabledChanged(RendererBase* renderer) {
391 assert(m_renderers[renderer->getName()]);
392 if (renderer->isEnabled()) {
393 FL_LOG(_log, LMsg("Enabling renderer ") << renderer->getName());
394 m_pipeline.push_back(renderer);
395 m_pipeline.sort(pipelineSort);
396 } else {
397 m_pipeline.remove(renderer);
398 }
399 }
400
401 RendererBase* Camera::getRenderer(const std::string& name) {
402 return m_renderers[name];
403 }
404
405 class InstanceDistanceSort {
406 public:
407 Camera* cam;
408 inline bool operator()(const Instance* lhs, const Instance* rhs) {
409 InstanceVisual* liv = lhs->getVisual<InstanceVisual>();
410 InstanceVisual* riv = rhs->getVisual<InstanceVisual>();
411 InstanceVisualCacheItem& lic = liv->getCacheItem(cam);
412 InstanceVisualCacheItem& ric = riv->getCacheItem(cam);
413 if (lic.screenpoint.z == ric.screenpoint.z) {
414 return liv->getStackPosition() < riv->getStackPosition();
415 }
416 return lic.screenpoint.z < ric.screenpoint.z;
417 }
418 };
419
420 void Camera::resetRenderers() {
421 std::map<std::string, RendererBase*>::iterator r_it = m_renderers.begin();
422 for (; r_it != m_renderers.end(); ++r_it) {
423 Map* map = m_location.getMap();
424 r_it->second->reset();
425 r_it->second->activateAllLayers(map);
426 }
427 }
428
429 void Camera::render() {
430 ScreenPoint cammove = getLatestMovement();
431
432 Map* map = m_location.getMap();
433 if (!map) {
434 FL_ERR(_log, "No map for camera found");
435 return;
436 }
437 //if ((!map->isChanged()) && (!m_iswarped) && (cammove == ScreenPoint(0,0,0))) {
438 // return;
439 //}
440
441 const unsigned long curtime = TimeManager::instance()->getTime();
442
443 // update each layer
444 m_renderbackend->pushClipArea(getViewPort());
445
446 m_layer_to_instances.clear();
447
448 const std::list<Layer*>& layers = map->getLayers();
449 std::list<Layer*>::const_iterator layer_it = layers.begin();
450 for (;layer_it != layers.end(); ++layer_it) {
451
452 // sort instances on layer based on stack position + camera distance. done only once
453 // here instead passing it to each renderer.
454 // instances are checked first if their image intersects with the viewport.
455 // this reduces processing load during sorting later
456 std::vector<Instance*> allinstances((*layer_it)->getInstances());
457 std::vector<Instance*>::const_iterator instance_it = allinstances.begin();
458 std::vector<Instance*>& instances_to_render = m_layer_to_instances[*layer_it];
459 for (;instance_it != allinstances.end(); ++instance_it) {
460 Instance* instance = *instance_it;
461 InstanceVisual* visual = instance->getVisual<InstanceVisual>();
462 InstanceVisualCacheItem& vc = visual->getCacheItem(this);
463
464 // use cached values if there is no need to do full recalculation
465 ScreenPoint drawpt;
466 int angle = 0;
467 if (m_updated && (!m_iswarped) && (!(instance->getChangeInfo() & (ICHANGE_LOC | ICHANGE_ROTATION))) && (vc.image)) {
468 int pos_estimate_x = vc.screenpoint.x - cammove.x;
469 int pos_estimate_y = vc.screenpoint.y - cammove.y;
470 int pos_estimate_z = vc.screenpoint.z - cammove.z;
471 angle = vc.facing_angle;
472 //std::cout << "orig x = " << drawpt.x << ", est x = " << pos_estimate_x << "\n";
473 //std::cout << "orig y = " << drawpt.y << ", est y = " << pos_estimate_y << "\n";
474 drawpt.x = pos_estimate_x;
475 drawpt.y = pos_estimate_y;
476 drawpt.z = pos_estimate_z;
477 //drawpt.z = toScreenCoordinates( instance->getLocationRef().getMapCoordinates() ).z;
478 } else {
479 drawpt = toScreenCoordinates( instance->getLocationRef().getMapCoordinates() );
480 vc.facing_angle = angle = getAngleBetween(instance->getLocationRef(), instance->getFacingLocation());
481 }
482 angle += instance->getRotation();
483
484 Image* image = NULL;
485 Action* action = instance->getCurrentAction();
486 if (action) {
487 FL_DBG(_log, "Instance has action");
488 int animation_id = action->getVisual<ActionVisual>()->getAnimationIndexByAngle(angle);
489
490 Animation& animation = m_apool->getAnimation(animation_id);
491 int animtime = scaleTime(instance->getTotalTimeMultiplier(), instance->getActionRuntime()) % animation.getDuration();
492 image = animation.getFrameByTimestamp(animtime);
493 } else {
494 FL_DBG(_log, "No action");
495 int imageid = vc.getStaticImageIndexByAngle(angle, instance);
496 FL_DBG(_log, LMsg("Instance does not have action, using static image with id ") << imageid);
497 if (imageid >= 0) {
498 image = &m_ipool->getImage(imageid);
499 } else {
500 // there was no static image for instance, trying default action
501 action = instance->getObject()->getDefaultAction();
502 if (action) {
503 int animation_id = action->getVisual<ActionVisual>()->getAnimationIndexByAngle(angle);
504 Animation& animation = m_apool->getAnimation(animation_id);
505 int animtime = scaleTime(instance->getTotalTimeMultiplier(), curtime) % animation.getDuration();
506 image = animation.getFrameByTimestamp(animtime);
507 }
508 }
509 }
510 if (image) {
511 vc.image = image;
512 vc.screenpoint = drawpt;
513
514 int w = image->getWidth();
515 int h = image->getHeight();
516 drawpt.x -= w / 2;
517 drawpt.x += image->getXShift();
518 drawpt.y -= h / 2;
519 drawpt.y += image->getYShift();
520 Rect r = Rect(drawpt.x, drawpt.y, w, h);
521
522 vc.dimensions = r;
523 if (m_zoom != 1.0) {
524 // NOTE: Due to image alignment, there is additional additions and substractions on image dimensions
525 // There's probabaly some better solution for this, but works "good enough" for now.
526 // In case additions / substractions are removed, gaps appear between tiles.
527 r.w = static_cast<unsigned int>(ceil(static_cast<double>(vc.dimensions.w) * m_zoom)) + 2;
528 r.h = static_cast<unsigned int>(ceil(static_cast<double>(vc.dimensions.h) * m_zoom)) + 2;
529 int xo = static_cast<int>(ceil(static_cast<double>(vc.image->getXShift()) * m_zoom)) - vc.image->getXShift();
530 int yo = static_cast<int>(ceil(static_cast<double>(vc.image->getYShift()) * m_zoom)) - vc.image->getYShift();
531 r.x = vc.dimensions.x - static_cast<unsigned int>(ceil(static_cast<double>(r.w - vc.dimensions.w) / 2)) + xo - 1;
532 r.y = vc.dimensions.y - static_cast<unsigned int>(ceil(static_cast<double>(r.h - vc.dimensions.h) / 2)) + yo - 1;
533 vc.dimensions = r;
534 }
535
536 if (vc.dimensions.intersects(getViewPort())) {
537 instances_to_render.push_back(instance);
538 }
539 }
540 }
541
542 InstanceDistanceSort ids;
543 ids.cam = this;
544 std::stable_sort(instances_to_render.begin(), instances_to_render.end(), ids);
545
546 std::list<RendererBase*>::iterator r_it = m_pipeline.begin();
547 for (; r_it != m_pipeline.end(); ++r_it) {
548 if ((*r_it)->isActivedLayer(*layer_it)) {
549 (*r_it)->render(this, *layer_it, instances_to_render);
550 }
551 }
552 }
553 m_renderbackend->popClipArea();
554 resetUpdates();
555 m_updated = true;
556 }
557
558 }