1 //Copyright Paul Reiche, Fred Ford. 1992-2002
2
3 /*
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include "planets.h"
20 #include "../colors.h"
21 #include "../setup.h"
22 #include "libs/graphics/gfx_common.h"
23 #include "libs/graphics/drawable.h"
24 #include "libs/mathlib.h"
25 #include "scan.h"
26 #include "options.h"
27
28 #include <math.h>
29
30
31 // define USE_ADDITIVE_SCAN_BLIT to use additive blittting
32 // instead of transparency for the planet scans.
33 // It still doesn't look right though (it is too bright)
34 #define USE_ADDITIVE_SCAN_BLIT
35
36 static int rotFrameIndex;
37 static int rotDirection;
38 static bool throbShield;
39 static int rotPointIndex;
40
41 // Draw the planet sphere and any extra graphic (like a shield) if present
42 void
DrawPlanetSphere(int x,int y)43 DrawPlanetSphere (int x, int y)
44 {
45 STAMP s;
46 PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
47
48 s.origin.x = x;
49 s.origin.y = y;
50
51 BatchGraphics ();
52 s.frame = Orbit->SphereFrame;
53 DrawStamp (&s);
54 if (Orbit->ObjectFrame)
55 {
56 s.frame = Orbit->ObjectFrame;
57 DrawStamp (&s);
58 }
59 UnbatchGraphics ();
60 }
61
62 void
DrawDefaultPlanetSphere(void)63 DrawDefaultPlanetSphere (void)
64 {
65 CONTEXT oldContext;
66
67 oldContext = SetContext (PlanetContext);
68 DrawPlanetSphere (SIS_SCREEN_WIDTH / 2, PLANET_ORG_Y);
69 SetContext (oldContext);
70 }
71
72 void
InitSphereRotation(int direction,BOOLEAN shielded)73 InitSphereRotation (int direction, BOOLEAN shielded)
74 {
75 PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
76
77 rotDirection = direction;
78 rotPointIndex = 0;
79 throbShield = shielded && optWhichShield == OPT_3DO;
80
81 if (throbShield)
82 {
83 // ObjectFrame must contain the shield graphic
84 Orbit->WorkFrame = Orbit->ObjectFrame;
85 // We need a scratch frame so that we can apply throbbing
86 // to the shield, so create one
87 Orbit->ObjectFrame = CaptureDrawable (CreateDrawable (
88 WANT_PIXMAP | WANT_ALPHA,
89 GetFrameWidth (Orbit->ObjectFrame),
90 GetFrameHeight (Orbit->ObjectFrame), 2));
91 }
92
93 // Render the first sphere/shield frame
94 // Prepare will set the next one
95 rotFrameIndex = 1;
96 PrepareNextRotationFrame ();
97 }
98
99 void
UninitSphereRotation(void)100 UninitSphereRotation (void)
101 {
102 PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
103
104 if (Orbit->WorkFrame)
105 {
106 DestroyDrawable (ReleaseDrawable (Orbit->ObjectFrame));
107 Orbit->ObjectFrame = Orbit->WorkFrame;
108 Orbit->WorkFrame = NULL;
109 }
110 }
111
112 void
PrepareNextRotationFrame(void)113 PrepareNextRotationFrame (void)
114 {
115 PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
116
117 // Generate the next rotation frame
118 // We alternate between the frames because we do not call FlushGraphics()
119 // The frame we just drew may not have made it to the screen yet
120 rotFrameIndex ^= 1;
121
122 // Go to next point, taking care of wraparounds
123 rotPointIndex += rotDirection;
124 if (rotPointIndex < 0)
125 rotPointIndex = MAP_WIDTH - 1;
126 else if (rotPointIndex >= MAP_WIDTH)
127 rotPointIndex = 0;
128
129 // prepare the next sphere frame
130 Orbit->SphereFrame = SetAbsFrameIndex (Orbit->SphereFrame, rotFrameIndex);
131 RenderPlanetSphere (Orbit->SphereFrame, rotPointIndex, throbShield);
132
133 if (throbShield)
134 { // prepare the next shield throb frame
135 Orbit->ObjectFrame = SetAbsFrameIndex (Orbit->ObjectFrame,
136 rotFrameIndex);
137 SetShieldThrobEffect (Orbit->WorkFrame, rotPointIndex,
138 Orbit->ObjectFrame);
139 }
140 }
141
142 #define ZOOM_RATE 24
143 #define ZOOM_TIME (ONE_SECOND * 6 / 5)
144
145 // This takes care of zooming the planet sphere into place
146 // when entering orbit
147 void
ZoomInPlanetSphere(void)148 ZoomInPlanetSphere (void)
149 {
150 PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
151 const int base = GSCALE_IDENTITY;
152 int dx, dy;
153 int oldScale;
154 int oldMode;
155 int i;
156 int frameCount;
157 int zoomCorner;
158 RECT frameRect;
159 RECT repairRect;
160 TimeCount NextTime;
161
162 frameCount = ZOOM_TIME / (ONE_SECOND / ZOOM_RATE);
163
164 // Planet zoom in from a randomly chosen corner
165 zoomCorner = TFB_Random ();
166 dx = 1 - (zoomCorner & 1) * 2;
167 dy = 1 - (zoomCorner & 2);
168
169 if (Orbit->ObjectFrame)
170 GetFrameRect (Orbit->ObjectFrame, &frameRect);
171 else
172 GetFrameRect (Orbit->SphereFrame, &frameRect);
173 repairRect = frameRect;
174
175 for (i = 0; i <= frameCount; ++i)
176 {
177 double scale;
178 POINT pt;
179
180 NextTime = GetTimeCounter () + (ONE_SECOND / ZOOM_RATE);
181
182 // Use 1 + e^-2 - e^(-2x / frameCount)) function to get a decelerating
183 // zoom like the one 3DO does (supposedly)
184 if (i < frameCount)
185 scale = 1.134 - exp (-2.0 * i / frameCount);
186 else
187 scale = 1.0; // final frame
188
189 // start from beyond the screen
190 pt.x = SIS_SCREEN_WIDTH / 2 + (int) (dx * (1.0 - scale)
191 * (SIS_SCREEN_WIDTH * 6 / 10) + 0.5);
192 pt.y = PLANET_ORG_Y + (int) (dy * (1.0 - scale)
193 * (SCAN_SCREEN_HEIGHT * 6 / 10) + 0.5);
194
195 SetContext (PlanetContext);
196
197 BatchGraphics ();
198 if (i > 0)
199 RepairBackRect (&repairRect);
200
201 oldMode = SetGraphicScaleMode (TFB_SCALE_BILINEAR);
202 oldScale = SetGraphicScale ((int)(base * scale + 0.5));
203 DrawPlanetSphere (pt.x, pt.y);
204 SetGraphicScale (oldScale);
205 SetGraphicScaleMode (oldMode);
206
207 UnbatchGraphics ();
208
209 repairRect.corner.x = pt.x + frameRect.corner.x;
210 repairRect.corner.y = pt.y + frameRect.corner.y;
211
212 PrepareNextRotationFrame ();
213
214 SleepThreadUntil (NextTime);
215 }
216 }
217
218 void
RotatePlanetSphere(BOOLEAN keepRate)219 RotatePlanetSphere (BOOLEAN keepRate)
220 {
221 static TimeCount NextTime;
222 TimeCount Now = GetTimeCounter ();
223
224 if (keepRate && Now < NextTime)
225 return; // not time yet
226
227 NextTime = Now + PLANET_ROTATION_RATE;
228 DrawDefaultPlanetSphere ();
229
230 PrepareNextRotationFrame ();
231 }
232
233 static void
renderTintFrame(Color tintColor)234 renderTintFrame (Color tintColor)
235 {
236 PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
237 CONTEXT oldContext;
238 DrawMode mode, oldMode;
239 STAMP s;
240 RECT r;
241
242 oldContext = SetContext (OffScreenContext);
243 SetContextFGFrame (Orbit->TintFrame);
244 SetContextClipRect (NULL);
245 // get the rect of the whole context (or our frame really)
246 GetContextClipRect (&r);
247
248 // copy the topo frame to the tint frame
249 s.origin.x = 0;
250 s.origin.y = 0;
251 s.frame = pSolarSysState->TopoFrame;
252 DrawStamp (&s);
253
254 // apply the tint
255 #ifdef USE_ADDITIVE_SCAN_BLIT
256 mode = MAKE_DRAW_MODE (DRAW_ADDITIVE, DRAW_FACTOR_1 / 2);
257 #else
258 mode = MAKE_DRAW_MODE (DRAW_ALPHA, DRAW_FACTOR_1 / 2);
259 #endif
260 oldMode = SetContextDrawMode (mode);
261 SetContextForeGroundColor (tintColor);
262 DrawFilledRectangle (&r);
263 SetContextDrawMode (oldMode);
264
265 SetContext (oldContext);
266 }
267
268 // tintColor.a is ignored
269 void
DrawPlanet(int tintY,Color tintColor)270 DrawPlanet (int tintY, Color tintColor)
271 {
272 STAMP s;
273 PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
274
275 s.origin.x = 0;
276 s.origin.y = 0;
277
278 BatchGraphics ();
279 if (sameColor (tintColor, BLACK_COLOR))
280 { // no tint -- just draw the surface
281 s.frame = pSolarSysState->TopoFrame;
282 DrawStamp (&s);
283 }
284 else
285 { // apply different scan type tints
286 FRAME tintFrame = Orbit->TintFrame;
287 int height = GetFrameHeight (tintFrame);
288
289 if (!sameColor (tintColor, Orbit->TintColor))
290 {
291 renderTintFrame (tintColor);
292 Orbit->TintColor = tintColor;
293 }
294
295 if (tintY < height - 1)
296 { // untinted piece showing, draw regular topo
297 s.frame = pSolarSysState->TopoFrame;
298 DrawStamp (&s);
299 }
300
301 if (tintY >= 0)
302 { // tinted piece showing, draw tinted piece
303 RECT oldClipRect;
304 RECT clipRect;
305
306 // adjust cliprect to confine the tint
307 GetContextClipRect (&oldClipRect);
308 clipRect = oldClipRect;
309 clipRect.extent.height = tintY + 1;
310 SetContextClipRect (&clipRect);
311 s.frame = tintFrame;
312 DrawStamp (&s);
313 SetContextClipRect (&oldClipRect);
314 }
315 }
316 UnbatchGraphics ();
317 }
318
319