00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "xrb_engine2_worldview.hpp"
00012
00013 #include "xrb_engine2_entity.hpp"
00014 #include "xrb_engine2_objectlayer.hpp"
00015 #include "xrb_engine2_sprite.hpp"
00016 #include "xrb_engine2_world.hpp"
00017 #include "xrb_engine2_worldviewwidget.hpp"
00018 #include "xrb_gl.hpp"
00019 #include "xrb_math.hpp"
00020 #include "xrb_render.hpp"
00021 #include "xrb_screen.hpp"
00022
00023 namespace Xrb
00024 {
00025
00026 Engine2::WorldView::WorldView (Engine2::WorldViewWidget *const parent_world_view_widget)
00027 :
00028 FloatTransform2(FloatTransform2::ms_identity, false),
00029 FrameHandler()
00030 {
00031 ASSERT1(parent_world_view_widget != NULL);
00032
00033 m_grid_line_type = GR_NO_DRAW;
00034 m_grid_number_base = 4;
00035 m_current_grid_scale = 1;
00036
00037 m_zoom_factor = 0.0625f;
00038 m_min_zoom_factor = 0.0f;
00039 m_max_zoom_factor = 1000000.0f;
00040 m_is_view_locked = false;
00041 m_is_gl_projection_matrix_in_use = false;
00042 m_draw_border_grid_lines = false;
00043 m_is_transform_scaling_based_upon_widget_radius = false;
00044
00045 m_is_parallaxed_world_to_view_dirty = true;
00046 m_is_parallaxed_view_to_world_dirty = true;
00047 m_is_parallaxed_world_to_screen_dirty = true;
00048 m_is_parallaxed_screen_to_world_dirty = true;
00049
00050 m_world = NULL;
00051 m_parent_world_view_widget = parent_world_view_widget;
00052 m_parent_world_view_widget->SetWorldView(this);
00053 }
00054
00055 Engine2::WorldView::~WorldView ()
00056 {
00057
00058 ASSERT1(m_world == NULL);
00059 }
00060
00061 FloatMatrix2 Engine2::WorldView::CompoundTransformation () const
00062 {
00063 ASSERT1(m_parent_world_view_widget != NULL);
00064 return m_parent_world_view_widget->Transformation() * Transformation();
00065 }
00066
00067 Float Engine2::WorldView::ViewDepth (Engine2::ObjectLayer const *object_layer) const
00068 {
00069 if (object_layer == NULL)
00070 object_layer = GetWorld()->MainObjectLayer();
00071
00072 return object_layer->ZDepth() - 1.0f / ZoomFactor();
00073 }
00074
00075 Float Engine2::WorldView::ParallaxedViewRadius (Engine2::ObjectLayer const *object_layer) const
00076 {
00077 if (object_layer == NULL)
00078 object_layer = GetWorld()->MainObjectLayer();
00079
00080 ASSERT1(ViewDepth(object_layer) != object_layer->ZDepth());
00081
00082 FloatMatrix2 world_to_screen(
00083 ParentWorldViewWidget()->Transformation() *
00084 Transformation());
00085 return
00086 CalculateViewRadius(
00087 world_to_screen.Inverse(),
00088 ParentWorldViewWidget()->ScreenRect(),
00089 Center())
00090 *
00091 ParallaxFactor(
00092 ViewDepth(MainObjectLayer()),
00093 object_layer->ZDepth());
00094 }
00095
00096 FloatMatrix2 Engine2::WorldView::ParallaxedTransformation (
00097 FloatMatrix2 const &world_to_view,
00098 FloatMatrix2 const &view_to_whatever,
00099 Engine2::ObjectLayer const *object_layer) const
00100 {
00101 if (object_layer == NULL)
00102 object_layer = GetWorld()->MainObjectLayer();
00103
00104 ASSERT1(ViewDepth(object_layer) != object_layer->ZDepth());
00105
00106
00107
00108 FloatMatrix2 world_to_whatever(world_to_view);
00109 world_to_whatever.Scale(
00110 1.0f / ParallaxFactor(ViewDepth(NULL), object_layer->ZDepth()));
00111 world_to_whatever *= view_to_whatever;
00112
00113 return world_to_whatever;
00114 }
00115
00116 Float Engine2::WorldView::MinorAxisRadius () const
00117 {
00118 FloatVector2 minor_axis;
00119 if (m_parent_world_view_widget->Width() < m_parent_world_view_widget->Height())
00120 minor_axis =
00121 0.5f * FloatVector2(
00122 static_cast<Float>(m_parent_world_view_widget->Width()),
00123 0.0f);
00124 else
00125 minor_axis =
00126 0.5f * FloatVector2(
00127 0.0f,
00128 static_cast<Float>(m_parent_world_view_widget->Height()));
00129 return
00130 (ParallaxedScreenToWorld() * minor_axis -
00131 ParallaxedScreenToWorld() * FloatVector2::ms_zero).Length();
00132 }
00133
00134 Float Engine2::WorldView::MajorAxisRadius () const
00135 {
00136 FloatVector2 major_axis;
00137 if (m_parent_world_view_widget->Width() > m_parent_world_view_widget->Height())
00138 major_axis =
00139 0.5f * FloatVector2(
00140 static_cast<Float>(m_parent_world_view_widget->Width()),
00141 0.0f);
00142 else
00143 major_axis =
00144 0.5f * FloatVector2(
00145 0.0f,
00146 static_cast<Float>(m_parent_world_view_widget->Height()));
00147 return
00148 (ParallaxedScreenToWorld() * major_axis -
00149 ParallaxedScreenToWorld() * FloatVector2::ms_zero).Length();
00150 }
00151
00152 Float Engine2::WorldView::CornerRadius () const
00153 {
00154 FloatVector2 corner_vector(
00155 0.5f * static_cast<Float>(m_parent_world_view_widget->Width()),
00156 0.5f * static_cast<Float>(m_parent_world_view_widget->Height()));
00157 return
00158 (ParallaxedScreenToWorld() * corner_vector -
00159 ParallaxedScreenToWorld() * FloatVector2::ms_zero).Length();
00160 }
00161
00162 void Engine2::WorldView::SetCenter (FloatVector2 const &position)
00163 {
00164 if (!IsViewLocked())
00165 {
00166 SetTranslation(-position);
00167
00168 DirtyAllParallaxedTransformations();
00169 }
00170 }
00171
00172 void Engine2::WorldView::SetZoomFactor (Float zoom_factor)
00173 {
00174 ASSERT1(zoom_factor > 0.0);
00175
00176 if (zoom_factor < m_min_zoom_factor)
00177 zoom_factor = m_min_zoom_factor;
00178
00179 if (zoom_factor > m_max_zoom_factor)
00180 zoom_factor = m_max_zoom_factor;
00181
00182 if (!IsViewLocked())
00183 {
00184 m_zoom_factor = zoom_factor;
00185
00186 DirtyAllParallaxedTransformations();
00187 }
00188 }
00189
00190 void Engine2::WorldView::SetAngle (Float const angle)
00191 {
00192 if (!IsViewLocked())
00193 {
00194 FloatTransform2::SetAngle(-angle);
00195
00196 DirtyAllParallaxedTransformations();
00197 }
00198 }
00199
00200 void Engine2::WorldView::SetMinZoomFactor (Float const min_zoom_factor)
00201 {
00202 ASSERT1(min_zoom_factor >= 0.0f);
00203
00204 if (m_max_zoom_factor < min_zoom_factor)
00205 m_max_zoom_factor = min_zoom_factor;
00206
00207 m_min_zoom_factor = min_zoom_factor;
00208
00209 SetZoomFactor(m_zoom_factor);
00210 }
00211
00212 void Engine2::WorldView::SetMaxZoomFactor (Float const max_zoom_factor)
00213 {
00214 ASSERT1(max_zoom_factor > 0.0f);
00215
00216 if (m_min_zoom_factor > max_zoom_factor)
00217 m_min_zoom_factor = max_zoom_factor;
00218
00219 m_max_zoom_factor = max_zoom_factor;
00220
00221 SetZoomFactor(m_zoom_factor);
00222 }
00223
00224 void Engine2::WorldView::SetIsTransformScalingBasedUponWidgetRadius (bool const is_transform_scaling_based_upon_widget_radius)
00225 {
00226 if (m_is_transform_scaling_based_upon_widget_radius != is_transform_scaling_based_upon_widget_radius)
00227 {
00228 m_is_transform_scaling_based_upon_widget_radius = is_transform_scaling_based_upon_widget_radius;
00229 ParentWorldViewWidget()->SetIsTransformScalingBasedUponWidgetRadius(m_is_transform_scaling_based_upon_widget_radius);
00230 }
00231 }
00232
00233 void Engine2::WorldView::MoveView (FloatVector2 const &delta_position)
00234 {
00235 if (!IsViewLocked())
00236 {
00237 Translate(-delta_position);
00238
00239 DirtyAllParallaxedTransformations();
00240 }
00241 }
00242
00243 void Engine2::WorldView::ZoomView (Float const delta_zoom_factor)
00244 {
00245 ASSERT1(delta_zoom_factor > 0.0);
00246 SetZoomFactor(m_zoom_factor * delta_zoom_factor);
00247 }
00248
00249 void Engine2::WorldView::RotateView (Float const delta_angle)
00250 {
00251 if (!IsViewLocked())
00252 {
00253 Rotate(-delta_angle);
00254
00255 DirtyAllParallaxedTransformations();
00256 }
00257 }
00258
00259 void Engine2::WorldView::DetachFromWorld ()
00260 {
00261 ASSERT1(m_world != NULL);
00262 m_world->DetachWorldView(this);
00263 ASSERT1(m_world == NULL);
00264 }
00265
00266 void Engine2::WorldView::Draw (RenderContext const &render_context)
00267 {
00268 ASSERT1(m_parent_world_view_widget != NULL);
00269 ASSERT1(m_world != NULL && "You must call Engine2::World::AttachWorldView before anything happens");
00270
00271
00272
00273 Float const fade_distance_upper_limit = 1.0;
00274 Float const fade_distance_lower_limit = 0.25;
00275 Float distance_fade;
00276 ASSERT1(fade_distance_upper_limit > fade_distance_lower_limit);
00277
00278
00279 RenderContext view_render_context(render_context);
00280
00281
00282
00283 m_draw_info.Reset();
00284
00285 Float pixels_in_view_radius =
00286 0.5f * render_context.ClipRect().Size().StaticCast<Float>().Length();
00287
00288
00289
00290 bool main_object_layer_has_been_drawn = false;
00291 FloatMatrix2 parallaxed_world_to_screen;
00292
00293 for (World::ObjectLayerList::iterator
00294 it = GetWorld()->GetObjectLayerList().begin(),
00295 it_end = GetWorld()->GetObjectLayerList().end();
00296 it != it_end;
00297 ++it)
00298 {
00299 ObjectLayer *object_layer;
00300 Float layer_offset;
00301 Float parallaxed_view_radius;
00302
00303 object_layer = *it;
00304 ASSERT1(object_layer != NULL);
00305
00306
00307 PushParallaxedGLProjectionMatrix(render_context, object_layer);
00308
00309 layer_offset = object_layer->ZDepth() - ViewDepth(NULL);
00310
00311 if (layer_offset > 0.0f)
00312 {
00313
00314 if (main_object_layer_has_been_drawn)
00315 {
00316
00317
00318 distance_fade =
00319 (layer_offset - fade_distance_lower_limit) /
00320 (fade_distance_upper_limit - fade_distance_lower_limit);
00321 }
00322 else
00323 {
00324 distance_fade = 1.0f;
00325 }
00326
00327 view_render_context.ColorMask() = render_context.ColorMask();
00328 view_render_context.ApplyAlphaMaskToColorMask(
00329 Min(Max(distance_fade, 0.0f), 1.0f));
00330
00331
00332 if (object_layer == GetWorld()->MainObjectLayer())
00333 {
00334
00335 main_object_layer_has_been_drawn = true;
00336
00337
00338 parallaxed_world_to_screen = ParallaxedWorldToScreen();
00339
00340 if (m_grid_line_type == GR_BELOW_MAIN_LAYER)
00341 DrawGridLines(view_render_context);
00342 }
00343 else
00344 {
00345
00346 parallaxed_world_to_screen =
00347 ParallaxedTransformation(
00348 Transformation(),
00349 ParentWorldViewWidget()->Transformation(),
00350 object_layer);
00351 }
00352
00353
00354 parallaxed_view_radius = ParallaxedViewRadius(object_layer);
00355
00356
00357
00358 m_draw_info.m_drawn_opaque_object_count =
00359 object_layer->Draw(
00360 view_render_context,
00361 parallaxed_world_to_screen,
00362 pixels_in_view_radius,
00363 Center(),
00364 parallaxed_view_radius,
00365 &m_transparent_object_vector);
00366
00367
00368 if (object_layer == GetWorld()->MainObjectLayer() &&
00369 m_grid_line_type == GR_ABOVE_MAIN_LAYER)
00370 {
00371 DrawGridLines(view_render_context);
00372 }
00373 }
00374
00375
00376 PopGLProjectionMatrix();
00377 }
00378
00379
00380 if (m_draw_border_grid_lines)
00381 {
00382 PushParallaxedGLProjectionMatrix(
00383 render_context,
00384 MainObjectLayer());
00385
00386
00387 Float parallaxed_view_radius = ParallaxedViewRadius(NULL);
00388
00389 DrawGridLineSet(
00390 render_context,
00391 0,
00392 true,
00393 MainObjectLayer()->IsWrapped(),
00394 Center(),
00395 parallaxed_view_radius,
00396 Color(1.0f, 1.0f, 0.0f, 1.0f));
00397
00398 PopGLProjectionMatrix();
00399 }
00400 }
00401
00402 bool Engine2::WorldView::ProcessKeyEvent (EventKey const *const e)
00403 {
00404 return false;
00405 }
00406
00407 bool Engine2::WorldView::ProcessMouseButtonEvent (EventMouseButton const *const e)
00408 {
00409 return false;
00410 }
00411
00412 bool Engine2::WorldView::ProcessMouseWheelEvent (EventMouseWheel const *const e)
00413 {
00414 return false;
00415 }
00416
00417 bool Engine2::WorldView::ProcessMouseMotionEvent (EventMouseMotion const *const e)
00418 {
00419 return false;
00420 }
00421
00422 Engine2::ObjectLayer *Engine2::WorldView::MainObjectLayer () const
00423 {
00424 return GetWorld()->MainObjectLayer();
00425 }
00426
00427 Float Engine2::WorldView::GridScaleUnit (Uint32 const grid_scale) const
00428 {
00429 return 0.5f * MainObjectLayer()->SideLength() /
00430 Math::Pow(static_cast<Float>(m_grid_number_base), static_cast<Float>(grid_scale));
00431 }
00432
00433 FloatMatrix2 const &Engine2::WorldView::ParallaxedWorldToWorldView () const
00434 {
00435 if (m_is_parallaxed_world_to_view_dirty)
00436 {
00437 m_parallaxed_world_to_view =
00438 ParallaxedTransformation(
00439 Transformation(),
00440 FloatMatrix2::ms_identity,
00441 NULL);
00442 m_is_parallaxed_world_to_view_dirty = false;
00443 }
00444
00445 return m_parallaxed_world_to_view;
00446 }
00447
00448 FloatMatrix2 const &Engine2::WorldView::ParallaxedWorldViewToWorld () const
00449 {
00450 if (m_is_parallaxed_view_to_world_dirty)
00451 {
00452 m_parallaxed_view_to_world = ParallaxedWorldToWorldView().Inverse();
00453 m_is_parallaxed_view_to_world_dirty = false;
00454 }
00455
00456 return m_parallaxed_view_to_world;
00457 }
00458
00459 FloatMatrix2 const &Engine2::WorldView::ParallaxedWorldToScreen () const
00460 {
00461 if (m_is_parallaxed_world_to_screen_dirty)
00462 {
00463 m_parallaxed_world_to_screen =
00464 ParallaxedTransformation(
00465 Transformation(),
00466 ParentWorldViewWidget()->Transformation(),
00467 NULL);
00468 m_is_parallaxed_world_to_screen_dirty = false;
00469 }
00470
00471 return m_parallaxed_world_to_screen;
00472 }
00473
00474 FloatMatrix2 const &Engine2::WorldView::ParallaxedScreenToWorld () const
00475 {
00476 if (m_is_parallaxed_screen_to_world_dirty)
00477 {
00478 m_parallaxed_screen_to_world =
00479 ParallaxedWorldToScreen().Inverse();
00480 m_is_parallaxed_screen_to_world_dirty = false;
00481 }
00482
00483 return m_parallaxed_screen_to_world;
00484 }
00485
00486 void Engine2::WorldView::DrawGridLines (RenderContext const &render_context)
00487 {
00488
00489 if (m_grid_line_type == GR_NO_DRAW)
00490 return;
00491
00492 bool is_wrapped = MainObjectLayer()->IsWrapped();
00493 Float view_radius = ParallaxedViewRadius(NULL);
00494
00495
00496 DrawGridLineSet(
00497 render_context,
00498 m_current_grid_scale+1,
00499 false,
00500 is_wrapped,
00501 Center(),
00502 view_radius,
00503 Color(0.4f, 0.4f, 0.4f, 1.0f));
00504
00505 DrawGridLineSet(
00506 render_context,
00507 m_current_grid_scale,
00508 false,
00509 is_wrapped,
00510 Center(),
00511 view_radius,
00512 Color(0.7f, 0.7f, 0.7f, 1.0f));
00513 }
00514
00515 void Engine2::WorldView::DrawGridLineSet (
00516 RenderContext const &render_context,
00517 Uint32 const grid_scale,
00518 bool const is_border_grid,
00519 bool const is_wrapped,
00520 FloatVector2 const &view_center,
00521 Float const view_radius,
00522 Color color)
00523 {
00524
00525 Float const fade_scale_upper_limit = 20.0;
00526 Float const fade_scale_lower_limit = 3.0;
00527
00528 Float grid_scale_unit = GridScaleUnit(grid_scale);
00529
00530
00531 Float transformed_grid_scale_unit =
00532 ((ParallaxedWorldToScreen() * FloatVector2(grid_scale_unit, 0.0)) -
00533 (ParallaxedWorldToScreen() * FloatVector2::ms_zero)).Length();
00534 Float distance_fade =
00535 (transformed_grid_scale_unit - fade_scale_lower_limit) /
00536 (fade_scale_upper_limit - fade_scale_lower_limit);
00537 color[Dim::A] *= Min(Max(distance_fade, 0.0f), 1.0f);
00538
00539
00540 if (color[Dim::A] == 0.0f)
00541 return;
00542
00543 Sint32 x_start;
00544 Sint32 x_stop;
00545 Sint32 y_start;
00546 Sint32 y_stop;
00547 Float view_limit;
00548 Float half_side_length = 0.5f * MainObjectLayer()->SideLength();
00549
00550 view_limit = view_center[Dim::X] - view_radius;
00551 if (!is_wrapped && view_limit < -half_side_length)
00552 view_limit = -half_side_length;
00553 x_start = static_cast<Sint32>(Math::Floor(view_limit / grid_scale_unit));
00554
00555 view_limit = view_center[Dim::X] + view_radius;
00556 if (!is_wrapped && view_limit > half_side_length)
00557 view_limit = half_side_length;
00558 x_stop = static_cast<Sint32>(Math::Ceiling(view_limit / grid_scale_unit));
00559
00560 view_limit = view_center[Dim::Y] - view_radius;
00561 if (!is_wrapped && view_limit < -half_side_length)
00562 view_limit = -half_side_length;
00563 y_start = static_cast<Sint32>(Math::Floor(view_limit / grid_scale_unit));
00564
00565 view_limit = view_center[Dim::Y] + view_radius;
00566 if (!is_wrapped && view_limit > half_side_length)
00567 view_limit = half_side_length;
00568 y_stop = static_cast<Sint32>(Math::Ceiling(view_limit / grid_scale_unit));
00569
00570
00571
00572 for (Sint32 x = x_start; x <= x_stop; ++x)
00573 if (!is_border_grid || (x & 1))
00574 Render::DrawLine(
00575 render_context,
00576 FloatVector2(grid_scale_unit*x, grid_scale_unit*y_start),
00577 FloatVector2(grid_scale_unit*x, grid_scale_unit*y_stop),
00578 color);
00579
00580
00581
00582 for (Sint32 y = y_start; y <= y_stop; ++y)
00583 if (!is_border_grid || (y & 1))
00584 Render::DrawLine(
00585 render_context,
00586 FloatVector2(grid_scale_unit*x_start, grid_scale_unit*y),
00587 FloatVector2(grid_scale_unit*x_stop, grid_scale_unit*y),
00588 color);
00589 }
00590
00591 Float Engine2::WorldView::CalculateViewRadius (
00592 FloatMatrix2 const &screen_to_world,
00593 ScreenCoordRect const &view_rect,
00594 FloatVector2 const &view_center) const
00595 {
00596 Float retval;
00597 Float distance_squared;
00598 FloatRect cast_view_rect(view_rect.StaticCast<Float>());
00599
00600
00601 distance_squared =
00602 (screen_to_world * cast_view_rect.TopLeft() -
00603 view_center).LengthSquared();
00604 retval = distance_squared;
00605
00606 distance_squared =
00607 (screen_to_world * cast_view_rect.TopRight() -
00608 view_center).LengthSquared();
00609 if (distance_squared > retval)
00610 retval = distance_squared;
00611
00612 distance_squared =
00613 (screen_to_world * cast_view_rect.BottomLeft() -
00614 view_center).LengthSquared();
00615 if (distance_squared > retval)
00616 retval = distance_squared;
00617
00618 distance_squared =
00619 (screen_to_world * cast_view_rect.BottomRight() -
00620 view_center).LengthSquared();
00621 if (distance_squared > retval)
00622 retval = distance_squared;
00623
00624 return Math::Sqrt(retval);
00625 }
00626
00627 void Engine2::WorldView::PushParallaxedGLProjectionMatrix (
00628 RenderContext const &render_context,
00629 Engine2::ObjectLayer const *const object_layer)
00630 {
00631 ASSERT1(!m_is_gl_projection_matrix_in_use);
00632 m_is_gl_projection_matrix_in_use = true;
00633
00634 glMatrixMode(GL_PROJECTION);
00635
00636 glPushMatrix();
00637
00638 glLoadIdentity();
00639
00640
00641
00642 if (m_is_transform_scaling_based_upon_widget_radius)
00643 {
00644 Float viewport_radius = render_context.ClipRect().Size().StaticCast<Float>().Length();
00645 glScalef(
00646 viewport_radius / render_context.ClipRect().Size()[Dim::X],
00647 viewport_radius / render_context.ClipRect().Size()[Dim::Y],
00648 1.0f);
00649 }
00650 else
00651 {
00652 Float min_viewport_size =
00653 static_cast<Float>(
00654 Min(render_context.ClipRect().Size()[Dim::X],
00655 render_context.ClipRect().Size()[Dim::Y]));
00656 glScalef(
00657 min_viewport_size / render_context.ClipRect().Size()[Dim::X],
00658 min_viewport_size / render_context.ClipRect().Size()[Dim::Y],
00659 1.0f);
00660 }
00661
00662
00663 Float parallax_factor =
00664 ParallaxFactor(
00665 ViewDepth(NULL),
00666 object_layer->ZDepth());
00667 ASSERT1(parallax_factor != 0.0f);
00668 glScalef(
00669 1.0f / parallax_factor,
00670 1.0f / parallax_factor,
00671 1.0f);
00672
00673
00674 ASSERT1(ScaleFactors()[Dim::X] == 1.0f);
00675 ASSERT1(ScaleFactors()[Dim::Y] == 1.0f);
00676 glRotatef(FloatTransform2::Angle(), 0.0f, 0.0f, 1.0f);
00677 glTranslatef(
00678 Translation()[Dim::X],
00679 Translation()[Dim::Y],
00680 0.0f);
00681 }
00682
00683 void Engine2::WorldView::PopGLProjectionMatrix ()
00684 {
00685 ASSERT1(m_is_gl_projection_matrix_in_use);
00686
00687 glMatrixMode(GL_PROJECTION);
00688 glPopMatrix();
00689 m_is_gl_projection_matrix_in_use = false;
00690 }
00691
00692 void Engine2::WorldView::DirtyAllParallaxedTransformations ()
00693 {
00694 m_is_parallaxed_world_to_view_dirty = true;
00695 m_is_parallaxed_view_to_world_dirty = true;
00696 m_is_parallaxed_world_to_screen_dirty = true;
00697 m_is_parallaxed_screen_to_world_dirty = true;
00698 }
00699
00700 }