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