mirror of
https://github.com/lscharen/iigs-game-engine.git
synced 2024-11-25 00:30:48 +00:00
Add workable overlay support to the scanline renderer
This commit is contained in:
parent
6832c7f405
commit
5697737a93
10
src/Defs.s
10
src/Defs.s
@ -182,13 +182,6 @@ OVERLAY_SOLID equ $8000 ; Overlay covers the scan line and is fu
|
||||
OVERLAY_ABOVE equ $0000 ; Overlay is drawn above scanline sprites
|
||||
OVERLAY_BELOW equ $4000 ; Overlay is drawn below scanline sprites
|
||||
|
||||
OVERLAY_BASE equ 2 ; 2 bytes for the number of overlays
|
||||
OVERLAY_REC_SIZE equ 10 ; Size of an overlay record (10 bytes)
|
||||
OVERLAY_FLAGS equ {OVERLAY_BASE+0}
|
||||
OVERLAY_TOP equ {OVERLAY_BASE+2}
|
||||
OVERLAY_BOTTOM equ {OVERLAY_BASE+4}
|
||||
OVERLAY_PROC equ {OVERLAY_BASE+6}
|
||||
|
||||
; DirtyBits definitions
|
||||
DIRTY_BIT_BG0_X equ $0001
|
||||
DIRTY_BIT_BG0_Y equ $0002
|
||||
@ -228,6 +221,7 @@ TILE_CTRL_MASK equ $7E00
|
||||
; TILE_PROC_MASK equ $7800 ; Select tile proc for rendering
|
||||
|
||||
; Sprite constants
|
||||
SPRITE_OVERLAY equ $8000 ; This is an overlay record. Stored as a sprite for render ordering purposes
|
||||
SPRITE_COMPILED equ $4000 ; This is a compiled sprite (SPRITE_DISP points to a routine in the compiled cache bank)
|
||||
SPRITE_HIDE equ $2000 ; Do not render the sprite
|
||||
SPRITE_16X16 equ $1800 ; 16 pixels wide x 16 pixels tall
|
||||
@ -283,7 +277,7 @@ VBuffArray EXT
|
||||
_stamp_step EXT
|
||||
VBuffVertTableSelect EXT
|
||||
VBuffHorzTableSelect EXT
|
||||
Overlays EXT
|
||||
; Overlays EXT
|
||||
BG1YCache EXT
|
||||
ScalingTables EXT
|
||||
|
||||
|
153
src/Render.s
153
src/Render.s
@ -71,7 +71,7 @@ _Render
|
||||
; The code fields are locked in now and ready to be rendered. See if there is an overlay or any
|
||||
; other reason to render with shadowing off. Otherwise, just do things quickly.
|
||||
|
||||
lda Overlays
|
||||
lda Overlays+OVERLAY_ID
|
||||
beq :no_ovrly
|
||||
|
||||
jsr _ShadowOff
|
||||
@ -82,6 +82,7 @@ _Render
|
||||
|
||||
ldx Overlays+OVERLAY_TOP ; Blit the full virtual buffer to the screen
|
||||
ldy Overlays+OVERLAY_BOTTOM
|
||||
iny
|
||||
jsr _BltRange
|
||||
|
||||
; Turn shadowing back on
|
||||
@ -93,6 +94,7 @@ _Render
|
||||
jsr _DoOverlay
|
||||
|
||||
ldx Overlays+OVERLAY_BOTTOM
|
||||
inx
|
||||
cpx ScreenHeight
|
||||
beq :done
|
||||
ldy ScreenHeight
|
||||
@ -343,14 +345,15 @@ _RenderWithShadowing
|
||||
jsr _BuildShadowList ; Create the rages based on the sorted sprite y-values
|
||||
; jsr _MergeOverlay ; Add the overlay range into the shadow list (treat it as a sprite)
|
||||
|
||||
jsr _ComplementList ; Create the complement to identify non-sprite / non-overlay scanlines
|
||||
; jsr _ComplementList ; Create the complement to identify non-sprite / non-overlay scanlines
|
||||
|
||||
jsr _ShadowOff ; Turn off shadowing and draw all the scanlines with sprites on them
|
||||
jsr _DrawShadowList
|
||||
jsr _DrawDirectSprites ; Draw the sprites directly to the Bank $01 graphics buffer (skipping the render-to-tile step)
|
||||
|
||||
jsr _ShadowOn ; Turn shadowing back on
|
||||
jsr _DrawComplementList ; Alternate drawing scanlines and PEI slam to expose the full fram
|
||||
; jsr _DrawComplementList ; Alternate drawing scanlines and PEI slam to expose the full fram
|
||||
jsr _DrawFinalPass
|
||||
|
||||
;
|
||||
; The objects that need to be reasoned about are
|
||||
@ -598,13 +601,16 @@ _DrawShadowList
|
||||
|
||||
rts
|
||||
|
||||
; Run through the list of sprites that are not IS_OFFSCREEN and draw them directly to the graphics screen. We can use
|
||||
; Run through the list of sprites that are not IS_OFFSCREEN and not OVERLAYS and draw them directly to the graphics screen. We can use
|
||||
; compiled sprites here, with limitations.
|
||||
_DrawDirectSprites
|
||||
ldx _SortedHead
|
||||
bmi :empty
|
||||
|
||||
:loop
|
||||
lda _Sprites+SPRITE_ID,x
|
||||
bit #SPRITE_OVERLAY
|
||||
bne :next
|
||||
lda _Sprites+SPRITE_STATUS,x
|
||||
bit #SPRITE_STATUS_HIDDEN
|
||||
bne :next
|
||||
@ -622,7 +628,144 @@ _DrawDirectSprites
|
||||
:empty
|
||||
rts
|
||||
|
||||
; Run through the complement list and alternate between calling _PEISlam and _BltRange to show to full screen
|
||||
; Run through the sorted list and perform a final render the jumps between calling _PEISlam for shadowed lines,
|
||||
; _BltRange for clean backgrounds and Overlays as needed.
|
||||
;
|
||||
; The trick here is to merge runs of shared render types.
|
||||
;
|
||||
; Loop invariant: X-register is the current object index, Y-register is the next object index
|
||||
;
|
||||
; TODO: This does not yet handle the case of a narrow overlay in the middle of a sprite. The second half of the sprite will not be exposed
|
||||
; by a PEISlam.
|
||||
;
|
||||
; e.g. |--- Overlay ---|
|
||||
; |-------------- Sprite ----------------|
|
||||
;
|
||||
; Output Should be |-- PEI --||--- Overlay ---||--- PEI --|
|
||||
; But currently is |-- PEI --||--- Overlay ---|
|
||||
|
||||
_DrawFinalPass
|
||||
:curr_top equ tmp0
|
||||
:curr_bottom equ tmp1
|
||||
:curr_type equ tmp2
|
||||
|
||||
ldx _SortedHead
|
||||
bmi :empty
|
||||
|
||||
lda _Sprites+SPRITE_CLIP_TOP,x ; Load the first object's top edge
|
||||
sta :curr_top
|
||||
beq :loop ; If it's at the top edge of the screen, proceed. Othrewise _BltRange the top range
|
||||
|
||||
ldx #0
|
||||
tay
|
||||
jsr _BltRange
|
||||
ldx _SortedHead ; Reload the register
|
||||
|
||||
:loop
|
||||
lda _Sprites+SPRITE_ID,x ; Save the type of the current segment. Do this first because it can be skipped
|
||||
and #SPRITE_OVERLAY ; when merging ranges of the same type
|
||||
sta :curr_type
|
||||
|
||||
lda _Sprites+SPRITE_CLIP_TOP,x
|
||||
sta :curr_top
|
||||
lda _Sprites+SPRITE_CLIP_BOTTOM,x ; Optimistically set the end of the segment to the bottom of this object
|
||||
inc ; Clip values are on the scanline, so add one to make it a proper interval
|
||||
|
||||
:update
|
||||
sta :curr_bottom
|
||||
|
||||
:skip
|
||||
ldy _Sprites+SORTED_NEXT,x ; If there another object in the list?
|
||||
bmi :no_more ; If not, we can finish up
|
||||
|
||||
lda :curr_bottom ; If the bottom of the current object is _less than_ the top of the next
|
||||
cmp _Sprites+SPRITE_CLIP_TOP,y ; sprite, then there is a gap and we can draw the current object and a
|
||||
bcc :advance ; _BltRange up to the next one
|
||||
|
||||
; Here, we've established that there is another object segment that starts at or within the bounds of the current
|
||||
; object. If they are of the same type, then we can merge them and look at the next object in the list; treating
|
||||
; the merges range as a larger, single object range.
|
||||
;
|
||||
; If they are different, then clip the current object range to the top of the next one, render the current object
|
||||
; range and then take the new object as the current one.
|
||||
;
|
||||
; If the first object extends past the second, we are going to miss the remainder of that object. We really need a
|
||||
; stack to put it on so that it can eventually be processed later.
|
||||
|
||||
lda _Sprites+SPRITE_ID,y
|
||||
and #SPRITE_OVERLAY
|
||||
cmp :curr_type
|
||||
bne :no_merge
|
||||
|
||||
tyx ; Move the next index into the current
|
||||
lda _Sprites+SPRITE_CLIP_BOTTOM,y ; Get the bottom value of the next sprite.
|
||||
inc
|
||||
cmp :curr_bottom ; If it extends the segment then replace the bottom value, otherwise skip. In
|
||||
bcc :skip ; either case, the type and top value remain the same
|
||||
bra :update
|
||||
|
||||
; This is a simpler version of the 'advance' below. In this case there are overlapping ranges, so we just need to draw a
|
||||
; clipped version of the top range and then restart the loop with the next range.
|
||||
:no_merge
|
||||
lda _Sprites+SPRITE_CLIP_TOP,y ; Get the top of the next segment
|
||||
sta :curr_bottom ; Use it as the bottom of the current segment
|
||||
phy ; Save the next index...
|
||||
jsr :PEIOrOverlay ; Draw the current segment type
|
||||
plx ; ...and restore as the current
|
||||
bra :loop ; Start again
|
||||
|
||||
:advance
|
||||
phy
|
||||
jsr :PEIOrOverlay ; Draw the appropriate filler
|
||||
lda 1,s
|
||||
tax
|
||||
ldy _Sprites+SPRITE_CLIP_TOP,x ; Draw the background in between
|
||||
ldx :curr_bottom
|
||||
jsr _BltRange
|
||||
plx
|
||||
bra :loop
|
||||
|
||||
; List is empty, so just do one big _BltRange with a tail call
|
||||
:empty
|
||||
ldx #0
|
||||
:no_more2
|
||||
ldy ScreenHeight
|
||||
jmp _BltRange
|
||||
|
||||
; Found the end of the list. Draw current object and then blit the rest of the screen
|
||||
:no_more
|
||||
jsr :PEIOrOverlay
|
||||
ldx :curr_bottom
|
||||
cpx ScreenHeight
|
||||
bcc :no_more2
|
||||
rts
|
||||
|
||||
; Help to select between calling an Overlay or PEISlam routine
|
||||
:PEIOrOverlay
|
||||
lda :curr_type
|
||||
bne :overlay
|
||||
|
||||
ldx :curr_top
|
||||
ldy :curr_bottom
|
||||
jmp _PEISlam
|
||||
:overlay
|
||||
lda _Sprites+OVERLAY_PROC,x
|
||||
stal :disp+1
|
||||
lda _Sprites+OVERLAY_PROC+1,x
|
||||
stal :disp+2
|
||||
|
||||
lda ScreenY0 ; pass the address of the first line of the overlay
|
||||
clc
|
||||
adc _Sprites+OVERLAY_TOP,x
|
||||
asl
|
||||
tax
|
||||
lda ScreenAddr,x
|
||||
clc
|
||||
adc ScreenX0
|
||||
:disp jsl $000000
|
||||
rts
|
||||
|
||||
|
||||
_DrawComplementList
|
||||
|
||||
ldx #0
|
||||
|
42
src/Tool.s
42
src/Tool.s
@ -698,6 +698,11 @@ _TSStartScript
|
||||
|
||||
_TSExit #0;#6
|
||||
; SetOverlay(top, bottom, proc)
|
||||
;
|
||||
; Overlays are handled as quasi-sprites. They need to be included in the y-sorted list of "stuff", but they are not drawn like
|
||||
; sprites. As such, they set a special flag in the SPRITE_ID field which allows them to be ignored for other purposes. Also,
|
||||
; they are not added into the "normal" sprite range on 0 - 15, but are stored in locations 16 and up to further seggregate them from
|
||||
; the rest of the system. A lot of the SPRITE_* locations are repurposed for Overlay-specific information.
|
||||
_TSSetOverlay
|
||||
:proc equ FirstParam+0
|
||||
:bottom equ FirstParam+4
|
||||
@ -705,9 +710,40 @@ _TSSetOverlay
|
||||
|
||||
_TSEntry
|
||||
|
||||
lda #1
|
||||
sta Overlays
|
||||
stz Overlays+OVERLAY_FLAGS
|
||||
ldx #0 ; Always just use the first spot
|
||||
lda #SPRITE_OVERLAY
|
||||
sta Overlays+OVERLAY_ID,x
|
||||
stz Overlays+OVERLAY_FLAGS,x
|
||||
|
||||
lda :top,s
|
||||
sta Overlays+OVERLAY_TOP,x
|
||||
lda :bottom,s
|
||||
dec
|
||||
sta Overlays+OVERLAY_BOTTOM,x
|
||||
sec
|
||||
sbc :top,s
|
||||
inc
|
||||
sta Overlays+OVERLAY_HEIGHT,x
|
||||
|
||||
lda :proc,s
|
||||
sta Overlays+OVERLAY_PROC,x
|
||||
lda :proc+2,s
|
||||
sta Overlays+OVERLAY_PROC+2,x
|
||||
|
||||
ldx #{MAX_SPRITES+0}*2 ; Adjust to call the generic routings
|
||||
jsr _InsertSprite
|
||||
|
||||
_TSExit #0;#8
|
||||
|
||||
_TSUpdateOverlay
|
||||
:proc equ FirstParam+0
|
||||
:bottom equ FirstParam+4
|
||||
:top equ FirstParam+6
|
||||
|
||||
_TSEntry
|
||||
|
||||
ldx #0
|
||||
stz Overlays+OVERLAY_FLAGS,x
|
||||
lda :top,s
|
||||
sta Overlays+OVERLAY_TOP
|
||||
lda :bottom,s
|
||||
|
@ -27,7 +27,7 @@ DirtyTiles ENT
|
||||
|
||||
ds \,$00 ; pad to the next page boundary
|
||||
_Sprites ENT
|
||||
ds SPRITE_REC_SIZE*MAX_SPRITES
|
||||
ds SPRITE_REC_SIZE*MAX_ELEMENTS
|
||||
|
||||
;-------------------------------------------------------------------------------------
|
||||
;
|
||||
@ -389,9 +389,10 @@ OldOneSecVec ENT
|
||||
ds 4
|
||||
Timers ENT
|
||||
ds TIMER_REC_SIZE*MAX_TIMERS
|
||||
Overlays ENT
|
||||
dw 0 ; count
|
||||
ds 10 ; only support one for now (flags, start_line, end_line, function call)
|
||||
|
||||
;Overlays ENT
|
||||
; dw 0 ; count
|
||||
; ds 10 ; only support one for now (flags, start_line, end_line, function call)
|
||||
|
||||
; From the IIgs ref
|
||||
DefaultPalette ENT
|
||||
@ -513,17 +514,17 @@ _SortedHead ENT
|
||||
_ShadowListCount ENT
|
||||
ds 2
|
||||
_ShadowListTop ENT
|
||||
ds {2*{MAX_SPRITES+1}} ; space for all of the sprites + overlay range
|
||||
ds {2*{MAX_ELEMENTS}} ; space for all of the sprites + overlay range
|
||||
_ShadowListBottom ENT
|
||||
ds {2*{MAX_SPRITES+1}}
|
||||
ds {2*{MAX_ELEMENTS}}
|
||||
|
||||
; Complement of the Shadow List. Can have one more segment than that list
|
||||
_DirectListCount ENT
|
||||
ds 2
|
||||
_DirectListTop ENT
|
||||
ds {2*{MAX_SPRITES+2}}
|
||||
ds {2*{MAX_ELEMENTS+1}}
|
||||
_DirectListBottom ENT
|
||||
ds {2*{MAX_SPRITES+2}}
|
||||
ds {2*{MAX_ELEMENTS+1}}
|
||||
|
||||
|
||||
; Steps to the different sprite stamps
|
||||
|
@ -28,6 +28,9 @@ TILE_STORE_NUM equ 12 ; Need this many parallel arra
|
||||
MAX_SPRITES equ 16
|
||||
SPRITE_REC_SIZE equ 42
|
||||
|
||||
MAX_OVERLAYS equ 2
|
||||
MAX_ELEMENTS equ {MAX_SPRITES+MAX_OVERLAYS}
|
||||
|
||||
; Mark each sprite as ADDED, UPDATED, MOVED, REMOVED depending on the actions applied to it
|
||||
; on this frame. Quick note, the same Sprite ID cannot be removed and added in the same frame.
|
||||
; A REMOVED sprite if removed from the sprite list during the Render call, so it's ID is not
|
||||
@ -41,28 +44,39 @@ SPRITE_STATUS_REMOVED equ $0008 ; Sprite has been removed.
|
||||
SPRITE_STATUS_HIDDEN equ $0010 ; Sprite is in a hidden state
|
||||
|
||||
; These values are set by the user
|
||||
SPRITE_STATUS equ {MAX_SPRITES*0}
|
||||
SPRITE_ID equ {MAX_SPRITES*2}
|
||||
SPRITE_X equ {MAX_SPRITES*4}
|
||||
SPRITE_Y equ {MAX_SPRITES*6}
|
||||
VBUFF_ADDR equ {MAX_SPRITES*8} ; Base address of the sprite's stamp in the data/mask banks
|
||||
SPRITE_STATUS equ {MAX_ELEMENTS*0}
|
||||
SPRITE_ID equ {MAX_ELEMENTS*2}
|
||||
SPRITE_X equ {MAX_ELEMENTS*4}
|
||||
SPRITE_Y equ {MAX_ELEMENTS*6}
|
||||
VBUFF_ADDR equ {MAX_ELEMENTS*8} ; Base address of the sprite's stamp in the data/mask banks
|
||||
|
||||
; These values are cached / calculated during the rendering process
|
||||
TS_LOOKUP_INDEX equ {MAX_SPRITES*10} ; The index from the TileStoreLookup table that corresponds to the top-left corner of the sprite
|
||||
TS_COVERAGE_SIZE equ {MAX_SPRITES*12} ; Representation of how many TileStore tiles (NxM) are covered by this sprite
|
||||
SPRITE_DISP equ {MAX_SPRITES*14} ; Cached address of the specific stamp based on sprite flags
|
||||
SPRITE_CLIP_LEFT equ {MAX_SPRITES*16}
|
||||
SPRITE_CLIP_RIGHT equ {MAX_SPRITES*18}
|
||||
SPRITE_CLIP_TOP equ {MAX_SPRITES*20}
|
||||
SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*22}
|
||||
IS_OFF_SCREEN equ {MAX_SPRITES*24}
|
||||
SPRITE_WIDTH equ {MAX_SPRITES*26}
|
||||
SPRITE_HEIGHT equ {MAX_SPRITES*28}
|
||||
SPRITE_CLIP_WIDTH equ {MAX_SPRITES*30}
|
||||
SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*32}
|
||||
TS_VBUFF_BASE equ {MAX_SPRITES*34} ; Finalized VBUFF address based on the sprite position and tile offsets
|
||||
SORTED_PREV equ {MAX_SPRITES*36} ; Doubly-Linked List that maintains the sprites in sorted order based on SPRITE_Y
|
||||
SORTED_NEXT equ {MAX_SPRITES*38}
|
||||
TS_LOOKUP_INDEX equ {MAX_ELEMENTS*10} ; The index from the TileStoreLookup table that corresponds to the top-left corner of the sprite
|
||||
TS_COVERAGE_SIZE equ {MAX_ELEMENTS*12} ; Representation of how many TileStore tiles (NxM) are covered by this sprite
|
||||
SPRITE_DISP equ {MAX_ELEMENTS*14} ; Cached address of the specific stamp based on sprite flags
|
||||
SPRITE_CLIP_LEFT equ {MAX_ELEMENTS*16}
|
||||
SPRITE_CLIP_RIGHT equ {MAX_ELEMENTS*18}
|
||||
SPRITE_CLIP_TOP equ {MAX_ELEMENTS*20}
|
||||
SPRITE_CLIP_BOTTOM equ {MAX_ELEMENTS*22}
|
||||
IS_OFF_SCREEN equ {MAX_ELEMENTS*24}
|
||||
SPRITE_WIDTH equ {MAX_ELEMENTS*26}
|
||||
SPRITE_HEIGHT equ {MAX_ELEMENTS*28}
|
||||
SPRITE_CLIP_WIDTH equ {MAX_ELEMENTS*30}
|
||||
SPRITE_CLIP_HEIGHT equ {MAX_ELEMENTS*32}
|
||||
TS_VBUFF_BASE equ {MAX_ELEMENTS*34} ; Finalized VBUFF address based on the sprite position and tile offsets
|
||||
SORTED_PREV equ {MAX_ELEMENTS*36} ; Doubly-Linked List that maintains the sprites in sorted order based on SPRITE_Y
|
||||
SORTED_NEXT equ {MAX_ELEMENTS*38}
|
||||
|
||||
; The Overlays are part of the _Sprites memory space and come after the maximum number of sprites
|
||||
Overlays equ {_Sprites+{MAX_SPRITES*2}}
|
||||
|
||||
; Aliases of SPRITE_* memory locations that are used for Overlay info when the SPRITE_OVERLAY bit is set on SPRITE_ID
|
||||
OVERLAY_ID equ SPRITE_ID
|
||||
OVERLAY_FLAGS equ SPRITE_STATUS
|
||||
OVERLAY_TOP equ SPRITE_CLIP_TOP ; This is important because SPRITE_CLIP_TOP is used for sorting
|
||||
OVERLAY_BOTTOM equ SPRITE_CLIP_BOTTOM
|
||||
OVERLAY_HEIGHT equ SPRITE_HEIGHT
|
||||
OVERLAY_PROC equ VBUFF_ADDR
|
||||
|
||||
; 52 rows by 82 columns + 2 extra rows and columns for sprite sizes
|
||||
;
|
||||
|
Loading…
Reference in New Issue
Block a user