14
14
from arcade .gl import Framebuffer
15
15
16
16
from arcade .window_commands import get_window
17
+
17
18
if TYPE_CHECKING :
18
19
from arcade .application import Window
19
20
20
-
21
21
__all__ = [
22
22
'Camera2D'
23
23
]
@@ -49,104 +49,40 @@ class Camera2D:
49
49
Replacing the camera data and projection data may break controllers. Their
50
50
contents are exposed via properties rather than directly to prevent this.
51
51
52
- :param window: The Arcade Window to bind the camera to.
53
- Defaults to the currently active window.
54
- :param camera_data: A :py:class:`~arcade.camera.data.CameraData`
55
- describing the position, up, forward and zoom.
56
- :param projection_data: A :py:class:`~arcade.camera.data.OrthographicProjectionData`
57
- which describes the left, right, top, bottom, far, near planes and the viewport
58
- for an orthographic projection.
52
+ :param viewport: A 4-int tuple which defines the pixel bounds which the camera will project to.
53
+ :param position: The 2D position of the camera in the XY plane.
54
+ :param up: A 2D vector which describes which direction is up (defines the +Y-axis of the camera space).
55
+ :param zoom: A scalar value which is inversely proportional to the size of the camera projection.
56
+ i.e. a zoom of 2.0 halves the size of the projection, doubling the perceived size of objects.
57
+ :param projection: A 4-float tuple which defines the world space
58
+ bounds which the camera projects to the viewport.
59
+ :param near: The near clipping plane of the camera.
60
+ :param far: The far clipping plane of the camera.
59
61
:param render_target: The FrameBuffer that the camera uses. Defaults to the screen.
60
62
If the framebuffer is not the default screen nothing drawn after this camera is used will
61
63
show up. The FrameBuffer's internal viewport is ignored.
64
+ :param window: The Arcade Window to bind the camera to.
65
+ Defaults to the currently active window.
62
66
"""
63
- def __init__ (self , * ,
64
- camera_data : Optional [CameraData ] = None ,
65
- projection_data : Optional [OrthographicProjectionData ] = None ,
67
+
68
+ def __init__ (self ,
69
+ viewport : Optional [Tuple [int , int , int , int ]] = None ,
70
+ position : Optional [Tuple [float , float ]] = None ,
71
+ up : Tuple [float , float ] = (0.0 , 1.0 ),
72
+ zoom : float = 1.0 ,
73
+ projection : Optional [Tuple [float , float , float , float ]] = None ,
74
+ near : float = - 100.0 ,
75
+ far : float = 100.0 ,
76
+ * ,
66
77
render_target : Optional [Framebuffer ] = None ,
67
78
window : Optional ["Window" ] = None ):
68
79
69
- if projection_data :
70
- left , right = projection_data .left , projection_data .right
71
- if projection_data .left == projection_data .right :
72
- raise ZeroProjectionDimension ((
73
- f"projection width is 0 due to equal { left = } "
74
- f"and { right = } values" ))
75
- bottom , top = projection_data .bottom , projection_data .top
76
- if bottom == top :
77
- raise ZeroProjectionDimension ((
78
- f"projection height is 0 due to equal { bottom = } "
79
- f"and { top = } " ))
80
- near , far = projection_data .near , projection_data .far
81
- if near == far :
82
- raise ZeroProjectionDimension (
83
- f"projection depth is 0 due to equal { near = } "
84
- f"and { far = } values"
85
- )
86
-
87
80
self ._window : "Window" = window or get_window ()
88
81
self .render_target : Framebuffer = render_target or self ._window .ctx .screen
89
82
width , height = self .render_target .size
90
83
half_width = width / 2
91
84
half_height = height / 2
92
85
93
- self ._camera_data = camera_data or CameraData (
94
- position = (half_width , half_height , 0.0 ),
95
- up = (0.0 , 1.0 , 0.0 ),
96
- forward = (0.0 , 0.0 , - 1.0 ),
97
- zoom = 1.0
98
- )
99
- self ._projection_data : OrthographicProjectionData = projection_data or OrthographicProjectionData (
100
- left = - half_width , right = half_width ,
101
- bottom = - half_height , top = half_height ,
102
- near = - 100.0 , far = 100.0 ,
103
- viewport = (0 , 0 , width , height )
104
- )
105
-
106
- self ._ortho_projector : OrthographicProjector = OrthographicProjector (
107
- window = self ._window ,
108
- view = self ._camera_data ,
109
- projection = self ._projection_data
110
- )
111
-
112
- @classmethod
113
- def from_raw_data (
114
- cls ,
115
- viewport : Optional [Tuple [int , int , int , int ]] = None ,
116
- position : Optional [Tuple [float , float ]] = None ,
117
- up : Tuple [float , float ] = (0.0 , 1.0 ),
118
- zoom : float = 1.0 ,
119
- projection : Optional [Tuple [float , float , float , float ]] = None ,
120
- near : float = - 100.0 ,
121
- far : float = 100.0 ,
122
- * ,
123
- render_target : Optional [Framebuffer ] = None ,
124
- window : Optional ["Window" ] = None
125
- ) -> Self :
126
- """
127
- Create a Camera2D without first defining CameraData or an OrthographicProjectionData object.
128
-
129
- :param viewport: A 4-int tuple which defines the pixel bounds which the camera with project to.
130
- :param position: The 2D position of the camera in the XY plane.
131
- :param up: The 2D unit vector which defines the +Y-axis of the camera space.
132
- :param zoom: A scalar value which is inversely proportional to the size of the camera projection.
133
- i.e. a zoom of 2.0 halves the size of the projection, doubling the perceived size of objects.
134
- :param projection: A 4-float tuple which defines the world space
135
- bounds which the camera projects to the viewport.
136
- :param near: The near clipping plane of the camera.
137
- :param far: The far clipping plane of the camera.
138
- :param render_target: The FrameBuffer that the camera uses. Defaults to the screen.
139
- If the framebuffer is not the default screen nothing drawn after this camera is used will
140
- show up. The FrameBuffer's internal viewport is ignored.
141
- :param window: The Arcade Window to bind the camera to.
142
- Defaults to the currently active window.
143
- """
144
- window = window or get_window ()
145
- render_target = render_target or window .ctx .screen
146
- width , height = render_target .size
147
- half_width = width / 2
148
- half_height = height / 2
149
-
150
86
# Unpack projection, but only validate when it's given directly
151
87
left , right , bottom , top = projection or (- half_width , half_width , - half_height , half_height )
152
88
if projection :
@@ -165,26 +101,97 @@ def from_raw_data(
165
101
)
166
102
167
103
_pos = position or (half_width , half_height )
168
- _data = CameraData (
104
+ self . _camera_data = CameraData (
169
105
position = (_pos [0 ], _pos [1 ], 0.0 ),
170
106
up = (up [0 ], up [1 ], 0.0 ),
171
107
forward = (0.0 , 0.0 , - 1.0 ),
172
108
zoom = zoom
173
109
)
174
-
175
- _projection : OrthographicProjectionData = OrthographicProjectionData (
110
+ self ._projection_data : OrthographicProjectionData = OrthographicProjectionData (
176
111
left = left , right = right ,
177
112
top = top , bottom = bottom ,
178
113
near = near , far = far ,
179
114
viewport = viewport or (0 , 0 , width , height )
180
115
)
116
+ self ._ortho_projector : OrthographicProjector = OrthographicProjector (
117
+ window = self ._window ,
118
+ view = self ._camera_data ,
119
+ projection = self ._projection_data
120
+ )
121
+
122
+ @classmethod
123
+ def from_camera_data (cls , * ,
124
+ camera_data : Optional [CameraData ] = None ,
125
+ projection_data : Optional [OrthographicProjectionData ] = None ,
126
+ render_target : Optional [Framebuffer ] = None ,
127
+ window : Optional ["Window" ] = None ) -> Self :
128
+ """
129
+ Make a ``Camera2D`` directly from data objects.
130
+
131
+ This :py:class:`classmethod` allows advanced users to:
132
+
133
+ #. skip or replace the default validation
134
+ #. share ``camera_data`` or ``projection_data`` between cameras
135
+
136
+ .. warning:: Be careful when sharing data objects!
137
+ **Any** action on a camera which changes a shared
138
+ object changes it for **every** camera which uses
139
+ the same object.
140
+
141
+ .. list-table::
142
+ :header-rows: 1
143
+ * - Shared Value
144
+ - Example Use(s)
145
+ * - ``camera_data``
146
+ - Mini-maps, reflection, and ghosting effects.
147
+ * - ``projection_data``
148
+ - Simplified rendering configuration
149
+ * - ``render_target``
150
+ - Complex rendering setups
151
+
152
+ :param camera_data: A :py:class:`~arcade.camera.data.CameraData`
153
+ describing the position, up, forward and zoom.
154
+ :param projection_data: A :py:class:`~arcade.camera.data.OrthographicProjectionData`
155
+ which describes the left, right, top, bottom, far, near planes and the viewport
156
+ for an orthographic projection.
157
+ :param render_target: The FrameBuffer that the camera uses. Defaults to the screen.
158
+ If the framebuffer is not the default screen nothing drawn after this camera is used will
159
+ show up. The FrameBuffer's internal viewport is ignored.
160
+ :param window: The Arcade Window to bind the camera to.
161
+ Defaults to the currently active window.
162
+ """
163
+
164
+ if projection_data :
165
+ left , right = projection_data .left , projection_data .right
166
+ if projection_data .left == projection_data .right :
167
+ raise ZeroProjectionDimension ((
168
+ f"projection width is 0 due to equal { left = } "
169
+ f"and { right = } values" ))
170
+ bottom , top = projection_data .bottom , projection_data .top
171
+ if bottom == top :
172
+ raise ZeroProjectionDimension ((
173
+ f"projection height is 0 due to equal { bottom = } "
174
+ f"and { top = } " ))
175
+ near , far = projection_data .near , projection_data .far
176
+ if near == far :
177
+ raise ZeroProjectionDimension (
178
+ f"projection depth is 0 due to equal { near = } "
179
+ f"and { far = } values"
180
+ )
181
+
182
+ # build a new camera with defaults and then apply the provided camera objects.
183
+ new_camera = cls (render_target = render_target , window = window )
184
+ if camera_data :
185
+ new_camera ._camera_data = camera_data
186
+ if projection_data :
187
+ new_camera ._projection_data = projection_data
181
188
182
- return cls (
183
- camera_data = _data ,
184
- projection_data = _projection ,
185
- window = window ,
186
- render_target = render_target
189
+ new_camera ._ortho_projector = OrthographicProjector (
190
+ window = new_camera ._window ,
191
+ view = new_camera ._camera_data ,
192
+ projection = new_camera ._projection_data
187
193
)
194
+ return new_camera
188
195
189
196
@property
190
197
def view_data (self ) -> CameraData :
@@ -235,12 +242,12 @@ def left(self) -> float:
235
242
236
243
Useful for checking if a :py:class:`~arcade.Sprite` is on screen.
237
244
"""
238
- return self ._camera_data .position [0 ] + self ._projection_data .left / self ._camera_data .zoom
245
+ return self ._camera_data .position [0 ] + self ._projection_data .left / self ._camera_data .zoom
239
246
240
247
@left .setter
241
248
def left (self , _left : float ) -> None :
242
- self ._camera_data .position = \
243
- (_left - self ._projection_data .left / self ._camera_data .zoom ,)\
249
+ self ._camera_data .position = \
250
+ (_left - self ._projection_data .left / self ._camera_data .zoom ,) \
244
251
+ self ._camera_data .position [1 :]
245
252
246
253
@property
@@ -249,12 +256,12 @@ def right(self) -> float:
249
256
250
257
Useful for checking if a :py:class:`~arcade.Sprite` is on screen.
251
258
"""
252
- return self ._camera_data .position [0 ] + self ._projection_data .right / self ._camera_data .zoom
259
+ return self ._camera_data .position [0 ] + self ._projection_data .right / self ._camera_data .zoom
253
260
254
261
@right .setter
255
262
def right (self , _right : float ) -> None :
256
- self ._camera_data .position = \
257
- (_right - self ._projection_data .right / self ._camera_data .zoom ,)\
263
+ self ._camera_data .position = \
264
+ (_right - self ._projection_data .right / self ._camera_data .zoom ,) \
258
265
+ self ._camera_data .position [1 :]
259
266
260
267
@property
@@ -263,7 +270,7 @@ def bottom(self) -> float:
263
270
264
271
Useful for checking if a :py:class:`~arcade.Sprite` is on screen.
265
272
"""
266
- return self ._camera_data .position [1 ] + self ._projection_data .bottom / self ._camera_data .zoom
273
+ return self ._camera_data .position [1 ] + self ._projection_data .bottom / self ._camera_data .zoom
267
274
268
275
@bottom .setter
269
276
def bottom (self , _bottom : float ) -> None :
@@ -279,7 +286,7 @@ def top(self) -> float:
279
286
280
287
Useful for checking if a :py:class:`~arcade.Sprite` is on screen.
281
288
"""
282
- return self ._camera_data .position [1 ] + self ._projection_data .top / self ._camera_data .zoom
289
+ return self ._camera_data .position [1 ] + self ._projection_data .top / self ._camera_data .zoom
283
290
284
291
@top .setter
285
292
def top (self , _top : float ) -> None :
0 commit comments