1 /* 2 * DSFML - The Simple and Fast Multimedia Library for D 3 * 4 * Copyright (c) 2013 - 2018 Jeremy DeHaan (dehaan.jeremiah@gmail.com) 5 * 6 * This software is provided 'as-is', without any express or implied warranty. 7 * In no event will the authors be held liable for any damages arising from the 8 * use of this software. 9 * 10 * Permission is granted to anyone to use this software for any purpose, 11 * including commercial applications, and to alter it and redistribute it 12 * freely, subject to the following restrictions: 13 * 14 * 1. The origin of this software must not be misrepresented; you must not claim 15 * that you wrote the original software. If you use this software in a product, 16 * an acknowledgment in the product documentation would be appreciated but is 17 * not required. 18 * 19 * 2. Altered source versions must be plainly marked as such, and must not be 20 * misrepresented as being the original software. 21 * 22 * 3. This notice may not be removed or altered from any source distribution 23 * 24 * 25 * DSFML is based on SFML (Copyright Laurent Gomila) 26 */ 27 28 /** 29 * $(U View) defines a camera in the 2D scene. This is a very powerful concept: 30 * you can scroll, rotate or zoom the entire scene without altering the way that 31 * your drawable objects are drawn. 32 * 33 * A view is composed of a source rectangle, which defines what part of the 2D 34 * scene is shown, and a target viewport, which defines where the contents of 35 * the source rectangle will be displayed on the render target (window or 36 * texture). 37 * 38 * The viewport allows to map the scene to a custom part of the render target, 39 * and can be used for split-screen or for displaying a minimap, for example. 40 * If the source rectangle has not the same size as the viewport, its contents 41 * will be stretched to fit in. 42 * 43 * To apply a view, you have to assign it to the render target. Then, every 44 * objects drawn in this render target will be affected by the view until you 45 * use another view. 46 * 47 * Example: 48 * --- 49 * auto window = RenderWindow(); 50 * auto view = View(); 51 * 52 * // Initialize the view to a rectangle at (100, 100) and a size of 400x200 53 * view.reset(FloatRect(100, 100, 400, 200)); 54 * 55 * // Rotate it by 45 degrees 56 * view.rotate(45); 57 * 58 * // Set its target viewport to be half of the window 59 * view.setViewport(FloatRect(0.f, 0.f, 0.5f, 1.f)); 60 * 61 * // Apply it 62 * window.view = view; 63 * 64 * // Render stuff 65 * window.draw(someSprite); 66 * 67 * // Set the default view back 68 * window.view = window.getDefaultView(); 69 * 70 * // Render stuff not affected by the view 71 * window.draw(someText); 72 * --- 73 * 74 * $(PARA See also the note on coordinates and undistorted rendering in 75 * $(TRANSFORMABLE_LINK).) 76 * 77 * See_Also: 78 * $(RENDERWINDOW_LINK), $(RENDERTEXTURE_LINK) 79 */ 80 module dsfml.graphics.view; 81 82 import dsfml.graphics.rect; 83 import dsfml.system.vector2; 84 import dsfml.graphics.transform; 85 86 /** 87 * 2D camera that defines what region is shown on screen. 88 */ 89 struct View 90 { 91 package 92 { 93 Vector2f m_center = Vector2f(500, 500); 94 Vector2f m_size = Vector2f(1000, 1000); 95 float m_rotation = 0; 96 FloatRect m_viewport = FloatRect(0, 0, 1, 1); 97 } 98 private 99 { 100 bool m_transformUpdated; 101 bool m_invTransformUpdated; 102 Transform m_transform; 103 Transform m_inverseTransform; 104 } 105 106 /** 107 * Construct the view from a rectangle 108 * 109 * Params: 110 * rectangle = Rectangle defining the zone to display 111 */ 112 this (FloatRect rectangle) 113 { 114 reset (rectangle); 115 } 116 117 /** 118 * Construct the view from its center and size 119 * 120 * Params: 121 * center = Center of the zone to display 122 * size = Size of zone to display 123 */ 124 this (Vector2f center, Vector2f size) 125 { 126 m_center = center; 127 m_size = size; 128 } 129 130 @property 131 { 132 /// The center of the view. 133 Vector2f center(Vector2f newCenter) 134 { 135 m_center = newCenter; 136 137 m_transformUpdated = false; 138 m_invTransformUpdated = false; 139 140 return newCenter; 141 } 142 143 /// ditto 144 Vector2f center() const 145 { 146 return m_center; 147 } 148 } 149 150 @property 151 { 152 /** 153 * The orientation of the view, in degrees. The default rotation is 0 154 * degrees. 155 */ 156 float rotation(float newRotation) 157 { 158 m_rotation = newRotation % 360.0; 159 if (m_rotation <0) 160 m_rotation += 360.0; 161 162 m_transformUpdated = false; 163 m_invTransformUpdated = false; 164 165 return newRotation; 166 } 167 168 /// ditto 169 float rotation() const 170 { 171 return m_rotation; 172 173 } 174 } 175 176 @property 177 { 178 /// The size of the view. The default size is (1, 1). 179 Vector2f size(Vector2f newSize) 180 { 181 m_size = newSize; 182 m_transformUpdated = false; 183 m_invTransformUpdated = false; 184 return newSize; 185 } 186 187 /// ditto 188 Vector2f size() const 189 { 190 return m_size; 191 } 192 } 193 194 @property 195 { 196 /** 197 * The target viewpoirt. 198 * 199 * The viewport is the rectangle into which the contents of the view are 200 * displayed, expressed as a factor (between 0 and 1) of the size of the 201 * RenderTarget to which the view is applied. For example, a view which 202 * takes the left side of the target would be defined with 203 * `View.setViewport(FloatRect(0, 0, 0.5, 1))`. By default, a view has a 204 * viewport which covers the entire target. 205 */ 206 FloatRect viewport(FloatRect newTarget) 207 { 208 m_viewport = newTarget; 209 210 return newTarget; 211 } 212 213 /// ditto 214 FloatRect viewport() const 215 { 216 return m_viewport; 217 } 218 } 219 220 /** 221 * Move the view relatively to its current position. 222 * 223 * Params: 224 * offset = Move offset 225 */ 226 void move(Vector2f offset) 227 { 228 center = m_center + offset; 229 } 230 231 /** 232 * Reset the view to the given rectangle. 233 * 234 * Note that this function resets the rotation angle to 0. 235 * 236 * Params: 237 * rectangle = Rectangle defining the zone to display. 238 */ 239 void reset(FloatRect rectangle) 240 { 241 m_center.x = rectangle.left + rectangle.width / 2.0; 242 m_center.y = rectangle.top + rectangle.height / 2.0; 243 m_size.x = rectangle.width; 244 m_size.y = rectangle.height; 245 m_rotation = 0; 246 247 m_transformUpdated = false; 248 m_invTransformUpdated = false; 249 } 250 251 /** 252 * Resize the view rectangle relatively to its current size. 253 * 254 * Resizing the view simulates a zoom, as the zone displayed on screen grows 255 * or shrinks. factor is a multiplier: 256 * $(UL 257 * $(LI `1` keeps the size unchanged.) 258 * $(LI `> 1` makes the view bigger (objects appear smaller).) 259 * $(LI `< 1` makes the view smaller (objects appear bigger).)) 260 * 261 * Params: 262 * factor = Zoom factor to apply 263 */ 264 void zoom(float factor) 265 { 266 size = m_size * factor; 267 } 268 269 /** 270 * Get the projection transform of the view. 271 * 272 * This function is meant for internal use only. 273 * 274 * Returns: Projection transform defining the view. 275 * 276 */ 277 //We have both const and mutable overloads so when the object is mutable, we can lazily cache the transform. 278 Transform getTransform() 279 { 280 import std.math; 281 // Recompute the matrix if needed 282 if (!m_transformUpdated) 283 { 284 // Rotation components 285 float angle = m_rotation * 3.141592654f / 180.0; 286 float cosine = cos(angle); 287 float sine = sin(angle); 288 float tx = -m_center.x * cosine - m_center.y * sine + m_center.x; 289 float ty = m_center.x * sine - m_center.y * cosine + m_center.y; 290 291 // Projection components 292 float a = 2.0 / m_size.x; 293 float b = -2.0 / m_size.y; 294 float c = -a * m_center.x; 295 float d = -b * m_center.y; 296 297 // Rebuild the projection matrix 298 m_transform = Transform( a * cosine, a * sine, a * tx + c, 299 -b * sine, b * cosine, b * ty + d, 300 0.0, 0.0, 1.0); 301 m_transformUpdated = true; 302 } 303 304 return m_transform; 305 } 306 307 /// ditto 308 Transform getTransform() const 309 { 310 import std.math; 311 // Recompute the matrix 312 Transform currentTransform; 313 314 // Rotation components 315 float angle = m_rotation * 3.141592654f / 180.0; 316 float cosine = cos(angle); 317 float sine = sin(angle); 318 float tx = -m_center.x * cosine - m_center.y * sine + m_center.x; 319 float ty = m_center.x * sine - m_center.y * cosine + m_center.y; 320 321 // Projection components 322 float a = 2.0 / m_size.x; 323 float b = -2.0 / m_size.y; 324 float c = -a * m_center.x; 325 float d = -b * m_center.y; 326 327 // Rebuild the projection matrix 328 currentTransform = Transform( a * cosine, a * sine, a * tx + c, 329 -b * sine, b * cosine, b * ty + d, 330 0.0, 0.0, 1.0); 331 332 return currentTransform; 333 } 334 335 /** 336 * Get the inverse projection transform of the view. 337 * 338 * This function is meant for internal use only. 339 * 340 * Returns: Inverse of the projection transform defining the view. 341 * 342 */ 343 Transform getInverseTransform() 344 { 345 // Recompute the matrix if needed 346 if (!m_invTransformUpdated) 347 { 348 m_inverseTransform = getTransform().getInverse(); 349 m_invTransformUpdated = true; 350 } 351 352 return m_inverseTransform; 353 } 354 355 /// ditto 356 Transform getInverseTransform() const 357 { 358 // Recompute the matrix if needed 359 return getTransform().getInverse(); 360 } 361 } 362 363 unittest 364 { 365 version(DSFML_Unittest_Graphics) 366 { 367 import std.stdio; 368 369 import dsfml.graphics.rendertexture; 370 371 writeln("Unit test for View"); 372 373 //the portion of the screen the view is displaying is at position (0,0) with a width of 100 and a height of 100 374 auto view = View(FloatRect(0,0,100,100)); 375 376 //the portion of the screen the view is displaying is at position (0,0) and takes up the remaining size of the screen.(expressed as a ratio) 377 view.viewport = FloatRect(0,0,1,1); 378 379 auto renderTexture = new RenderTexture(); 380 381 renderTexture.create(1000,1000); 382 383 renderTexture.clear(); 384 385 //set the view of the renderTexture 386 renderTexture.view = view; 387 388 //draw some things using this view 389 390 //get it ready for rendering 391 renderTexture.display(); 392 393 writeln(); 394 } 395 }