1// MIT License
2
3// Copyright (c) 2020 Vadim Grigoruk @nesbox // grigoruk@gmail.com
4
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23typedef struct
24{
25 float x, y, u, v;
26} TexVertDep;
27
28static struct
29{
30 s16 Left[TIC80_HEIGHT];
31 s16 Right[TIC80_HEIGHT];
32 s32 ULeft[TIC80_HEIGHT];
33 s32 VLeft[TIC80_HEIGHT];
34} SidesBufferDep;
35
36static void setSideTexPixel(s32 x, s32 y, float u, float v)
37{
38 s32 yy = y;
39 if (yy >= 0 && yy < TIC80_HEIGHT)
40 {
41 if (x < SidesBufferDep.Left[yy])
42 {
43 SidesBufferDep.Left[yy] = x;
44 SidesBufferDep.ULeft[yy] = (s32)(u * 65536.0f);
45 SidesBufferDep.VLeft[yy] = (s32)(v * 65536.0f);
46 }
47 if (x > SidesBufferDep.Right[yy])
48 {
49 SidesBufferDep.Right[yy] = x;
50 }
51 }
52}
53
54static void ticTexLine(tic_mem* memory, TexVertDep* v0, TexVertDep* v1)
55{
56 TexVertDep* top = v0;
57 TexVertDep* bot = v1;
58
59 if (bot->y < top->y)
60 {
61 top = v1;
62 bot = v0;
63 }
64
65 float dy = bot->y - top->y;
66 float step_x = (bot->x - top->x);
67 float step_u = (bot->u - top->u);
68 float step_v = (bot->v - top->v);
69
70 if ((s32)dy != 0)
71 {
72 step_x /= dy;
73 step_u /= dy;
74 step_v /= dy;
75 }
76
77 float x = top->x;
78 float y = top->y;
79 float u = top->u;
80 float v = top->v;
81
82 if (y < .0f)
83 {
84 y = .0f - y;
85
86 x += step_x * y;
87 u += step_u * y;
88 v += step_v * y;
89
90 y = .0f;
91 }
92
93 s32 botY = (s32)bot->y;
94 if (botY > TIC80_HEIGHT)
95 botY = TIC80_HEIGHT;
96
97 for (; y < botY; ++y)
98 {
99 setSideTexPixel((s32)x, (s32)y, u, v);
100 x += step_x;
101 u += step_u;
102 v += step_v;
103 }
104}
105
106void drawTexturedTriangleDep(tic_core* core, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, bool use_map, u8* colors, s32 count)
107{
108 tic_mem* memory = &core->memory;
109 tic_vram* vram = &memory->ram->vram;
110
111 u8* mapping = getPalette(memory, colors, count);
112 TexVertDep V0, V1, V2;
113
114 const u8* map = memory->ram->map.data;
115 tic_tilesheet sheet = getTileSheetFromSegment(memory, memory->ram->vram.blit.segment);
116
117 V0.x = x1; V0.y = y1; V0.u = u1; V0.v = v1;
118 V1.x = x2; V1.y = y2; V1.u = u2; V1.v = v2;
119 V2.x = x3; V2.y = y3; V2.u = u3; V2.v = v3;
120
121 // calculate the slope of the surface
122 // use floats here
123 float denom = (V0.x - V2.x) * (V1.y - V2.y) - (V1.x - V2.x) * (V0.y - V2.y);
124 if (denom == 0.0)
125 {
126 return;
127 }
128 float id = 1.0f / denom;
129 float dudx, dvdx;
130 // this is the UV slope across the surface
131 dudx = ((V0.u - V2.u) * (V1.y - V2.y) - (V1.u - V2.u) * (V0.y - V2.y)) * id;
132 dvdx = ((V0.v - V2.v) * (V1.y - V2.y) - (V1.v - V2.v) * (V0.y - V2.y)) * id;
133 // convert to fixed
134 s32 dudxs = (s32)(dudx * 65536.0f);
135 s32 dvdxs = (s32)(dvdx * 65536.0f);
136 // fill the buffer
137 for (s32 i = 0; i < COUNT_OF(SidesBuffer.Left); i++)
138 SidesBufferDep.Left[i] = TIC80_WIDTH, SidesBufferDep.Right[i] = -1;
139
140 // parse each line and decide where in the buffer to store them ( left or right )
141 ticTexLine(memory, &V0, &V1);
142 ticTexLine(memory, &V1, &V2);
143 ticTexLine(memory, &V2, &V0);
144
145 for (s32 y = 0; y < TIC80_HEIGHT; y++)
146 {
147 // if it's backwards skip it
148 s32 width = SidesBufferDep.Right[y] - SidesBufferDep.Left[y];
149 // if it's off top or bottom , skip this line
150 if ((y < core->state.clip.t) || (y > core->state.clip.b))
151 width = 0;
152 if (width > 0)
153 {
154 s32 u = SidesBufferDep.ULeft[y];
155 s32 v = SidesBufferDep.VLeft[y];
156 s32 left = SidesBufferDep.Left[y];
157 s32 right = SidesBufferDep.Right[y];
158 // check right edge, and CLAMP it
159 if (right > core->state.clip.r)
160 right = core->state.clip.r;
161 // check left edge and offset UV's if we are off the left
162 if (left < core->state.clip.l)
163 {
164 s32 dist = core->state.clip.l - SidesBufferDep.Left[y];
165 u += dudxs * dist;
166 v += dvdxs * dist;
167 left = core->state.clip.l;
168 }
169 // are we drawing from the map . ok then at least check before the inner loop
170 if (use_map == true)
171 {
172 for (s32 x = left; x < right; ++x)
173 {
174 enum { MapWidth = TIC_MAP_WIDTH * TIC_SPRITESIZE, MapHeight = TIC_MAP_HEIGHT * TIC_SPRITESIZE };
175 s32 iu = (u >> 16) % MapWidth;
176 s32 iv = (v >> 16) % MapHeight;
177
178 while (iu < 0) iu += MapWidth;
179 while (iv < 0) iv += MapHeight;
180
181 u8 tileindex = map[(iv >> 3) * TIC_MAP_WIDTH + (iu >> 3)];
182 tic_tileptr tile = tic_tilesheet_gettile(&sheet, tileindex, true);
183
184 u8 color = mapping[tic_tilesheet_gettilepix(&tile, iu & 7, iv & 7)];
185 if (color != TRANSPARENT_COLOR)
186 setPixel(core, x, y, color);
187 u += dudxs;
188 v += dvdxs;
189 }
190 }
191 else
192 {
193 // direct from tile ram
194 for (s32 x = left; x < right; ++x)
195 {
196 enum { SheetWidth = TIC_SPRITESHEET_SIZE, SheetHeight = TIC_SPRITESHEET_SIZE * TIC_SPRITE_BANKS };
197 s32 iu = (u >> 16) & (SheetWidth - 1);
198 s32 iv = (v >> 16) & (SheetHeight - 1);
199
200 u8 color = mapping[tic_tilesheet_getpix(&sheet, iu, iv)];
201 if (color != TRANSPARENT_COLOR)
202 setPixel(core, x, y, color);
203 u += dudxs;
204 v += dvdxs;
205 }
206 }
207 }
208 }
209}