1 /*
2 BStone: A Source port of
3 Blake Stone: Aliens of Gold and Blake Stone: Planet Strike
4 
5 Copyright (c) 1992-2013 Apogee Entertainment, LLC
6 Copyright (c) 2013-2015 Boris I. Bendovsky (bibendovsky@hotmail.com)
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the
20 Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23 
24 
25 #include "3d_def.h"
26 #include "bstone_fixed_point.h"
27 
28 
29 enum class ShapeDrawMode {
30     simple,
31     shaded,
32     player_weapon,
33 }; // ShapeDrawMode
34 
35 
36 extern const uint8_t* shadingtable;
37 extern const uint8_t* lightsource;
38 
39 
40 #define CLOAKED_SHAPES (1)
41 
42 
43 /*
44 =============================================================================
45 
46  GLOBALS
47 
48 =============================================================================
49 */
50 
51 int maxscale;
52 int maxscaleshl2;
53 int centery;
54 
55 int normalshade;
56 int normalshade_div = 1;
57 int shade_max = 1;
58 
59 int16_t nsd_table[] = { 1, 6, 3, 4, 1, 2 };
60 int16_t sm_table[] = { 36, 51, 62, 63, 18, 52 };
61 uint16_t* linecmds;
62 
63 
SetupScaling(int maxscaleheight)64 void SetupScaling(
65     int maxscaleheight)
66 {
67     maxscaleheight /= 2; // one scaler every two pixels
68 
69     maxscale = maxscaleheight - 1;
70     maxscaleshl2 = maxscale * 4;
71     normalshade = (3 * maxscale) / (4 * normalshade_div);
72     centery = viewheight / 2;
73 }
74 
75 
76 extern bool useBounceOffset;
77 
78 int bounceOffset = 0;
79 
generic_scale_shape(const int xcenter,const int shapenum,const int ref_height,const int8_t lighting,const ShapeDrawMode draw_mode)80 void generic_scale_shape(
81     const int xcenter,
82     const int shapenum,
83     const int ref_height,
84     const int8_t lighting,
85     const ShapeDrawMode draw_mode)
86 {
87     const auto is_player_weapon = (draw_mode == ShapeDrawMode::player_weapon);
88 
89     if (!is_player_weapon)
90     {
91         const auto ref_half_height = ref_height / 2;
92 
93         if (ref_half_height == 0)
94         {
95             return;
96         }
97 
98         if (ref_half_height > ::maxscaleshl2)
99         {
100             return;
101         }
102     }
103 
104     const auto height =
105         is_player_weapon ?
106         (::vga_height * ref_height) / ::vga_ref_height :
107         ref_height / 4;
108 
109     if (height == 0)
110     {
111         return;
112     }
113 
114 
115     constexpr auto side = bstone::Sprite::side;
116 
117     constexpr int mid_bob = 6;
118     constexpr auto bob_start = 6;
119 
120     const auto use_bobbing = is_player_weapon && useBounceOffset;
121 
122     const auto bounce_offset_n =
123         use_bobbing ?
124             bounceOffset / 0x10000 :
125             0;
126 
127     const auto bob_offset =
128         use_bobbing ?
129         (::vga_height * (bob_start + mid_bob - bounce_offset_n)) / ::vga_ref_height :
130         0;
131 
132 
133     const auto sprite_ptr = ::vid_sprite_cache.cache(
134         shapenum);
135 
136     const auto sprite_width = sprite_ptr->get_width();
137     const auto sprite_height = sprite_ptr->get_height();
138 
139     const auto half_height = height / 2;
140 
141     const auto offset_x =
142         is_player_weapon ?
143             (::viewwidth - height) / 2 :
144             xcenter - half_height;
145 
146     const auto offset_y =
147         is_player_weapon ?
148             ::vga_3d_view_bottom - height + bob_offset :
149             ::vga_3d_view_top + ::centery - half_height;
150 
151     const auto left = sprite_ptr->get_left();
152     auto x1 = offset_x + ((left * height) / side);
153 
154     if (x1 >= ::viewwidth)
155     {
156         return;
157     }
158 
159     auto x2 = x1 + ((sprite_width * height) / side);
160 
161     const auto top = sprite_ptr->get_top();
162     auto y1 = offset_y + ((top * height) / side);
163 
164     if (y1 >= ::vga_3d_view_bottom)
165     {
166         return;
167     }
168 
169     auto y2 = y1 + ((sprite_height * height) / side);
170 
171 
172     const auto tx_delta = bstone::FixedPoint{side, 0} / height;
173 
174     auto tx_column = bstone::FixedPoint{};
175 
176     if (x1 < 0)
177     {
178         tx_column += tx_delta * (-x1);
179         x1 = 0;
180     }
181 
182     if (x2 >= ::viewwidth)
183     {
184         x2 = ::viewwidth;
185     }
186 
187     if (x2 <= x1)
188     {
189         return;
190     }
191 
192     auto tx_row_begin = bstone::FixedPoint{};
193 
194     if (y1 < ::vga_3d_view_top)
195     {
196         tx_row_begin += tx_delta * (::vga_3d_view_top - y1);
197         y1 = ::vga_3d_view_top;
198     }
199 
200     if (y2 >= ::vga_3d_view_bottom)
201     {
202         y2 = ::vga_3d_view_bottom - 1;
203     }
204 
205     if (y2 <= y1)
206     {
207         return;
208     }
209 
210     const uint8_t* shading = nullptr;
211 
212     if (draw_mode == ShapeDrawMode::shaded)
213     {
214         auto i = shade_max - (63 * ref_height / (::normalshade * 8)) + lighting;
215 
216         if (i < 0)
217         {
218             i = 0;
219         }
220         else if (i > 63)
221         {
222             i = 63;
223         }
224 
225         // BBi Don't shade cloaked shape
226         if (::cloaked_shape)
227         {
228             i = 0;
229         }
230 
231         shading = &::lightsource[i * 256];
232     }
233 
234 
235     for (int x = x1; x < x2; ++x)
236     {
237         if (!is_player_weapon && ::wallheight[x] > ref_height)
238         {
239             tx_column += tx_delta;
240             continue;
241         }
242 
243         const auto column_index = tx_column.get_int();
244         const auto column = sprite_ptr->get_column(column_index);
245         auto tx_row = tx_row_begin;
246 
247         for (int y = y1; y < y2; ++y)
248         {
249             const auto row_index = tx_row.get_int();
250             const auto sprite_color = column[row_index];
251 
252             if (sprite_color < 0)
253             {
254                 tx_row += tx_delta;
255                 continue;
256             }
257 
258             const auto pixel_offset = ::vl_get_offset(0, x, y);
259             auto color_index = static_cast<uint8_t>(sprite_color);
260 
261             if (draw_mode == ShapeDrawMode::shaded)
262             {
263 #if CLOAKED_SHAPES
264                 if (::cloaked_shape)
265                 {
266                     color_index = shading[0x1000 | ::vga_memory[pixel_offset]];
267                 }
268                 else
269 #endif
270                 {
271                     color_index = shading[color_index];
272                 }
273             }
274 
275             ::vga_memory[pixel_offset] = color_index;
276 
277             tx_row += tx_delta;
278         }
279 
280         tx_column += tx_delta;
281     }
282 }
283 
284 /*
285 =======================
286 =
287 = ScaleLSShape with Light sourcing
288 =
289 = Draws a compiled shape at [scale] pixels high
290 =
291 = each vertical line of the shape has a pointer to segment data:
292 =       end of segment pixel*2 (0 terminates line) used to patch rtl in scaler
293 =       top of virtual line with segment in proper place
294 =       start of segment pixel*2, used to jsl into compiled scaler
295 =       <repeat>
296 =
297 = Setup for call
298 = --------------
299 = GC_MODE read mode 1, write mode 2
300 = GC_COLORDONTCARE  set to 0, so all reads from video memory return 0xff
301 = GC_INDEX pointing at GC_BITMASK
302 =
303 =======================
304 */
ScaleLSShape(int xcenter,int shapenum,int height,int8_t lighting)305 void ScaleLSShape(
306     int xcenter,
307     int shapenum,
308     int height,
309     int8_t lighting)
310 {
311     generic_scale_shape(
312         xcenter,
313         shapenum,
314         height,
315         lighting,
316         ShapeDrawMode::shaded);
317 }
318 
319 /*
320 =======================
321 =
322 = ScaleShape
323 =
324 = Draws a compiled shape at [scale] pixels high
325 =
326 = each vertical line of the shape has a pointer to segment data:
327 =       end of segment pixel*2 (0 terminates line) used to patch rtl in scaler
328 =       top of virtual line with segment in proper place
329 =       start of segment pixel*2, used to jsl into compiled scaler
330 =       <repeat>
331 =
332 = Setup for call
333 = --------------
334 = GC_MODE read mode 1, write mode 2
335 = GC_COLORDONTCARE  set to 0, so all reads from video memory return 0xff
336 = GC_INDEX pointing at GC_BITMASK
337 =
338 =======================
339 */
ScaleShape(int xcenter,int shapenum,int height)340 void ScaleShape(
341     int xcenter,
342     int shapenum,
343     int height)
344 {
345     generic_scale_shape(
346         xcenter,
347         shapenum,
348         height,
349         0,
350         ShapeDrawMode::simple);
351 }
352 
353 // BBi
scale_player_weapon(const int sprite_id,const int height)354 void scale_player_weapon(
355     const int sprite_id,
356     const int height)
357 {
358     generic_scale_shape(
359         0,
360         sprite_id,
361         height,
362         0,
363         ShapeDrawMode::player_weapon);
364 }
365 // BBi
366