-
-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathrender_3D.asm
More file actions
319 lines (299 loc) · 7.48 KB
/
render_3D.asm
File metadata and controls
319 lines (299 loc) · 7.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
;
; Title: 3D Modelling Functions
; Author: Dean Belfield
; Created: 20/08/2025
; Last Updated: 30/11/2025
;
; Modinfo:
; 25/11/2025: Optimised project3D, rotate8_3D
; 28/11/2025: Optimised renderModel
; 30/11/2025: Added buffer parameter to drawObject
;
SECTION KERNEL_CODE
INCLUDE "globals.inc"
EXTERN rotate8_3D ; In maths.asm
EXTERN rotate16_3D ; In maths.asm
EXTERN project3D ; In maths.asm
EXTERN windingOrder ; In maths.asm
EXTERN negHL ; In maths.asm
EXTERN scratchpad ; In ram.inc
EXTERN shape_buffer ; In render.asm
EXTERN triangleL2C ; In clipping.asm
EXTERN triangleL2CF ; In clipping.asm
PUBLIC _cam_pos
PUBLIC _cam_theta
_cam_pos: DS 6,0 ; Point16_3D cam_pos
_cam_theta: DS 3,0 ; Angle_3D cam_theta;
; extern void rotateModel(Point16 * buffer, Point16_3D p, Angle_3D a, Model_3D * m) __z88dk_callee;
; This is an optimised version of this C routine
;
; for(i=0; i<m->numVertices; i++) {
; Point8_3D v = (*m->vertices)[i];
; Point8_3D r = rotate8_3D(v, a);
; *buffer++ = project3D(&p, &r);
; }
;
PUBLIC _rotateModel, rotateModel
_rotateModel: POP BC ; The return address
POP HL: LD (R7),HL ; R0: Pointer to Point16 buffer
POP HL: LD (R1),HL ; R1: p.x
POP HL: LD (R2),HL ; R2: p.y
POP HL: LD (R3),HL ; R3: p.z
POP DE ; E: theta.x, D: theta.y
DEC SP ; Correct the stack address for single byte
POP AF
LD L,A ; L: theta.z
POP IY ; IY: Pointer to Model_3D object
PUSH BC ; Restore the return address
PUSH IX
CALL rotateModel
POP IX
RET
;
; Sort out the angles for rotate
; R7: Pointer to the Point16 buffer for the rotated points
; IY: Pointer to the Model_3D structure
;
; The model origin
; R1: p.x
; R2: p.y
; R3: p.z
;
; The model rotation
; D: theta.x
; E: theta.y
; L: theta.z
;
rotateModel: LD A,L
LD (@M1+1),DE ; Self-mod the angles
LD (@M2+1),A
;
LD B,(IY+0) ; Fetch number of vertices from the model
LD L,(IY+2) ; Fetch pointer to the vertices
LD H,(IY+3)
LD IX,(R7) ; IX: Pointer to the Point16 buffer for the rotated points
;
; First get the number of vertices to plot
;
@L1: PUSH BC ; Push loop counter
;
; Read a vertice in from the model
;
LD A,(HL) ; A: v.x
INC HL
LD B,(HL) ; B: v.y
INC HL
LD C,(HL) ; C: v.z
INC HL
PUSH HL
LD H,A ; H: v.x
;
; Apply rotation
;
@M1: LD DE,0 ; theta.x, theta.y
@M2: LD L,0 ; theta.z
CALL rotate8_3D ; Do the rotation, Point8_3D result in (IY)
CALL project3D ; Do the projection, Point16 result in (IY)
;
; Store the transformed point in the buffer
;
LD (IX+0),E ; Store the rotated point
LD (IX+1),D
LD (IX+2),L
LD (IX+3),H
LD BC,4
ADD IX,BC
;
; Loop
;
POP HL ; Pop pointer to vertices
POP BC ; Pop loop counter
DJNZ @L1
RET
; extern void drawObject(Point16 * buffer, Object_3D * o, uint8_t renderMode) __z88dk_callee;
; This is an optimised version of this C routine
;
; Point16_3D u_pos = {
; o->pos.x - cam_pos.x,
; o->pos.y - cam_pos.y,
; o->pos.z - cam_pos.z,
; };
; u_pos = rotate16_3D(u_pos, cam_theta);
; Angle_3D u_ang = {
; cam_theta.x - o->theta.x,
; cam_theta.y - o->theta.y,
; cam_theta.z - o->theta.z,
; };
; if(u_pos.z >= 200 && abs(u_pos.x) < u_pos.z && abs(u_pos.y) < u_pos.z ) {
; rotateModel(&point_t[0], u_pos, u_ang, o->model);
; renderModel(&point_t[0], o->model, renderMode);
; }
;
PUBLIC _drawObject, drawObject
_drawObject: POP BC ; The return address
POP HL: LD (R7),HL ; Pointer to the vector buffer
POP IY ; Pointer to the model data
DEC SP
POP AF ; A: renderMode
PUSH BC ; Restore the return address
PUSH IX
CALL drawObject
POP IX
RET
; Draw an object in world space
; R7: Pointer to the translated points buffer
; IY: Pointer to an Object_3D structure
; A: Render mode (0: wireframe, 1: filled)
;
; + 0: Flags
; + 1: Pointer to the objects movement routine
; + 3: Pointer to the objects Model_3D data
; + 5: Point16_3D world position of object (6 bytes)
; + 11: Angle3D object rotation (3 bytes)
;
drawObject: PUSH AF ; Store mode for later
LD L,(IY+5) ; p.x
LD H,(IY+6)
LD BC,(_cam_pos+0)
OR A
SBC HL,BC
LD (R1),HL ; R1: p.x - cam_pos.x
LD L,(IY+7) ; p.y
LD H,(IY+8)
LD BC,(_cam_pos+2)
OR A
SBC HL,BC
LD (R2),HL ; R2: p.y - cam_pos.y
;
LD L,(IY+9) ; p.z
LD H,(IY+10)
LD BC,(_cam_pos+4)
OR A
SBC HL,BC
LD (R3),HL ; R3: p.z - cam_pos.z
;
; Rotate the model around the camera
;
LD BC,(_cam_theta) ; C: cam_theta.x, B: cam_theta.y
LD A,(_cam_theta+2); A: cam_theta.z
CALL rotate16_3D
;
; Only rotate and render if u_pos.z >= 200 && abs(u_pos.x) < u_pos.z && abs(u_pos.y) < u_pos.z
;
LD HL,(R3) ; HL: p.z
BIT 7,H ; If negative
RET NZ ; Then just do nothing
LD BC,200 ; If positive, and
CMP_HL BC ; less than 200, then
RET C ; do nothing
;
LD BC,(R3) ; BC: p.z
LD HL,(R1) ; HL: p.x
BIT 7,H
CALL NZ,negHL ; HL: ABS(HL)
CMP_HL BC
RET NC ; Return if ABS(p.x) < p.z
;
LD HL,(R2) ; HL: p.y
BIT 7,H
CALL NZ,negHL ; HL: ABS(HL)
CMP_HL BC
RET NC ; Return if ABS(p.y) < p.z
;
; Rotate the object respective to the camera
;
LD A,(_cam_theta+0)
SUB (IY+11)
LD D,A ; D: cam_theta.x - theta.x
;
LD A,(_cam_theta+1)
SUB (IY+12)
LD E,A ; E: cam_theta.y - theta.y
;
LD A,(_cam_theta+2)
SUB (IY+13)
LD L,A ; L: cam_theta.z - theta.z
;
;
; Rotate and project the object to screen space and render
;
LD C,(IY+3)
LD A,(IY+4)
LD IYL,C ; IY: Pointer to the Model_3D object
LD IYH,A
CALL rotateModel
POP AF ; A: Render mode
JR renderModel
; extern void renderModel(Point16 * buffer, Model_3D * m, uint8_t mode) __z88dk_callee;
; This is an optimised version of this C routine
;
; for(i=0; i<m->numFaces; i++) {
; Vertice_3D * v = &(*m->faces)[i];
; Point16 p1 = buffer[v->p1];
; Point16 p2 = buffer[v->p2];
; Point16 p3 = buffer[v->p3];
; if(windingOrder(p1,p2,p3)) {
; if(mode == 1) {
; triangleL2CF(p1,p2,p3,v->colour);
; }
; else {
; triangleL2C(p1,p2,p3,0xFF);
; }
; }
; }
;
PUBLIC _renderModel, renderModel
_renderModel: POP BC ; The return address
POP HL: LD (R7),HL ; Pointer to the vector buffer
POP IY ; Pointer to the model data
DEC SP ; Correct the stack address for single byte
POP AF ; A: mode
PUSH BC ; Restore the return address
; IY: Pointer to the Model_3D object
; R7: Pointer to the translated points buffer
; A: Mode (0: wireframe, 1: filled)
;
renderModel: LD B,(IY+1) ; B: Number of faces
LD L,(IY+4) ; HL: Pointer to the face data
LD H,(IY+5)
LD (@M3+1),A ; Mode
;
; Fetch all the face data (four bytes)
;
@L1: EXX
LD DE,R0 ; DE: Pointer to the variable storage for the point data
EXX
REPT 3
LD A,(HL): INC HL
EXX
LD HL,(R7) ; HL: Pointer to the translated point buffer
ADD HL,A ; Effectively multiply the face number by 4 by adding it to the
ADD HL,A ; pointer 4 times
ADD HL,A
ADD HL,A
LDI ; Copy the point data to DE
LDI
LDI
LDI
EXX
ENDR
LD A,(HL) ; The face colour
EX AF,AF
INC HL
EXX
CALL windingOrder ; Do the backface culling calculation
JR Z,@M1 ; This face is culled, so skip
@M3: LD A,0 ; A: mode (0 = wireframe, 1 = filled)
OR A
JR Z, @M2 ; Not 1, so jump to wireframe version
EX AF,AF
CALL triangleL2CF ; Only draw triangles that are facing us
@M1: EXX
DJNZ @L1
RET
;
@M2: DEC A ; Always do wireframe in colour #FF
CALL triangleL2C
EXX
DJNZ @L1
RET