mirror of
https://github.com/badvision/lawless-legends.git
synced 2024-12-27 10:29:40 +00:00
More work on sprite math in 6502.
This commit is contained in:
parent
53318fc2cd
commit
9f7d551fd0
@ -64,7 +64,7 @@ var options = 0;
|
||||
|
||||
var debugRay = null; /* Debugging info printed about this ray num, or null for none */
|
||||
|
||||
var debugSprite = 3; /* Debugging info printed about this sprite, or null for none */
|
||||
var debugSprite = 0; /* Debugging info printed about this sprite, or null for none */
|
||||
|
||||
var maxAngleNum = 16;
|
||||
|
||||
@ -110,6 +110,7 @@ var overlayText = "";
|
||||
|
||||
// Constants
|
||||
var wLog256;
|
||||
var wLog128;
|
||||
var wLogViewDist;
|
||||
|
||||
// Tables
|
||||
@ -261,6 +262,7 @@ function initCast()
|
||||
|
||||
// Sprite math constants
|
||||
wLog256 = log2_w_w(256);
|
||||
wLog128 = log2_w_w(128);
|
||||
wLogViewDist = log2_w_w(viewDist/8*256); // div by 8 to get to Apple II coords
|
||||
|
||||
// Sine table
|
||||
@ -354,6 +356,7 @@ function printPrecast() {
|
||||
console.log("");
|
||||
|
||||
console.log("wLog256: .word " + wordToHex(wLog256));
|
||||
console.log("wLog128: .word " + wordToHex(wLog128));
|
||||
console.log("wLogViewDist: .word " + wordToHex(wLogViewDist));
|
||||
console.log("");
|
||||
|
||||
@ -383,14 +386,20 @@ function calcZ(wLogHeight) {
|
||||
return ubyte(wLogHeight>>4);
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine uses integer math to perform the calculations originally
|
||||
* developed in floatRenderSprites(). To understand the math, you might
|
||||
* want to start by reading the float version first, then this, and then
|
||||
* finally the 6502 code that does these integer calculations.
|
||||
*/
|
||||
function intRenderSprites()
|
||||
{
|
||||
// Quantities that are the same for every sprite
|
||||
var bSgnSinT = (tbl_wLogSin[player.angleNum] & 0x8000) ? -1 : 1;
|
||||
var wLogSinT = (tbl_wLogSin[player.angleNum] & 0x7FFF);
|
||||
var wLogSinT = (tbl_wLogSin[player.angleNum] & 0x7FFF) - wLog256;
|
||||
var cosAngle = (player.angleNum - 4) & 15;
|
||||
var bSgnCosT = (tbl_wLogSin[cosAngle] & 0x8000) ? -1 : 1;
|
||||
var wLogCosT = (tbl_wLogSin[cosAngle] & 0x7FFF);
|
||||
var wLogCosT = (tbl_wLogSin[cosAngle] & 0x7FFF) - wLog256;
|
||||
|
||||
// Now process each sprite
|
||||
for (var i=0;i<allSprites.length;i++) {
|
||||
@ -411,8 +420,8 @@ function intRenderSprites()
|
||||
var bSgnDy = (dy < 0) ? -1 : 1;
|
||||
var wLogDy = log2_w_w(uword(Math.abs(dy)*256));
|
||||
|
||||
var wRx = bSgnDx*bSgnCosT*pow2_w_w(wLogDx + wLogCosT - wLog256) -
|
||||
bSgnDy*bSgnSinT*pow2_w_w(wLogDy + wLogSinT - wLog256);
|
||||
var wRx = bSgnDx*bSgnCosT*pow2_w_w(wLogDx + wLogCosT) -
|
||||
bSgnDy*bSgnSinT*pow2_w_w(wLogDy + wLogSinT);
|
||||
|
||||
// If sprite is behind the viewer, skip it.
|
||||
if (wRx < 0) {
|
||||
@ -423,55 +432,60 @@ function intRenderSprites()
|
||||
continue;
|
||||
}
|
||||
|
||||
var wRy = bSgnDx*bSgnSinT*pow2_w_w(wLogDx + wLogSinT - wLog256) +
|
||||
bSgnDy*bSgnCosT*pow2_w_w(wLogDy + wLogCosT - wLog256);
|
||||
var wRy = bSgnDx*bSgnSinT*pow2_w_w(wLogDx + wLogSinT) +
|
||||
bSgnDy*bSgnCosT*pow2_w_w(wLogDy + wLogCosT);
|
||||
|
||||
// Transform wRy to abs and sign
|
||||
var bSgnRy = 1;
|
||||
if (wRy < 0) {
|
||||
bSgnRy = -1;
|
||||
wRy = -wRy;
|
||||
}
|
||||
|
||||
// Calculate the distance
|
||||
var wLogSqRx = Math.max(0, (log2_w_w(Math.abs(wRx)) << 1) - wLog256);
|
||||
var wLogSqRy = Math.max(0, (log2_w_w(Math.abs(wRy)) << 1) - wLog256);
|
||||
var wLogSqRx = (log2_w_w(wRx) << 1) - wLog256;
|
||||
var wLogSqRy = (log2_w_w(wRy) << 1) - wLog256;
|
||||
var wSqDist = pow2_w_w(wLogSqRx) + pow2_w_w(wLogSqRy);
|
||||
var wLogDist = ((log2_w_w(wSqDist)-wLog256) >> 1) + wLog256;
|
||||
var wLogDist = (log2_w_w(wSqDist) + wLog256) >> 1;
|
||||
|
||||
// size of the sprite
|
||||
var wLogSize = wLogViewDist - wLogDist;
|
||||
var wSize = pow2_w_w(wLogSize);
|
||||
|
||||
// x-position on screen
|
||||
var bSgnRy = wRy < 0 ? -1 : 1;
|
||||
// The constant below is cheesy and based on empirical observation rather than understanding.
|
||||
// Sorry :/
|
||||
var wX = bSgnRy * pow2_w_w(log2_w_w(Math.abs(wRy)) - wLogDist + log2_w_w(252 / 8 / 0.44));
|
||||
var wX = bSgnRy * pow2_w_w(log2_w_w(wRy) - wLogDist + log2_w_w(252 / 8 / 0.44));
|
||||
if (sprite.index == debugSprite)
|
||||
console.log(" wRx/256=" + (wRx/256.0) + ", wRy/256=" + (wRy/256.0) + ", wSize=" + wSize + ", wX=" + wX);
|
||||
|
||||
// If no pixels on screen, skip it
|
||||
var spriteLeft = (screenWidth/8/2) + wX - (wSize/2);
|
||||
var spriteRight = spriteLeft + wSize;
|
||||
var spriteTop = ((screenHeight/8) - wSize) / 2;
|
||||
if (spriteRight < 0) {
|
||||
var wSpriteTop = 32 - (wSize >> 1);
|
||||
var wSpriteLeft = wX + wSpriteTop;
|
||||
if (wSpriteLeft < -wSize) {
|
||||
if (sprite.index == debugSprite)
|
||||
console.log(" off-screen to left.");
|
||||
console.log(" off-screen to left (wSpriteLeft=" + wSpriteLeft + ", -wSize=" + (-wSize) + ").");
|
||||
sprite.visible = false;
|
||||
sprite.img.style.display = "none";
|
||||
continue;
|
||||
}
|
||||
else if (spriteLeft > (screenWidth/8)) {
|
||||
else if (wSpriteLeft > 63) {
|
||||
if (sprite.index == debugSprite)
|
||||
console.log(" off-screen to right.");
|
||||
console.log(" off-screen to right (wSpriteLeft=" + wSpriteLeft + ", vs 63).");
|
||||
sprite.visible = false;
|
||||
sprite.img.style.display = "none";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Adjust from Apple II coordinates to PC coords (we render 8 pixels for each 1 Apple pix)
|
||||
spriteLeft *= 8;
|
||||
spriteTop *= 8;
|
||||
wSpriteLeft *= 8;
|
||||
wSpriteTop *= 8;
|
||||
wSize *= 8;
|
||||
|
||||
// Update the image with the calculated values
|
||||
sprite.visible = true;
|
||||
img.style.left = spriteLeft + "px";
|
||||
img.style.top = spriteTop+"px";
|
||||
img.style.left = wSpriteLeft + "px";
|
||||
img.style.top = wSpriteTop+"px";
|
||||
img.style.width = wSize + "px";
|
||||
img.style.height = wSize + "px";
|
||||
// The constant below is cheesy and I'm not sure why it's needed. But it seems to
|
||||
|
@ -28,6 +28,10 @@ DEBUG = 1 ; 1=some logging, 2=lots of logging
|
||||
MAX_SPRITES = 16 ; max # sprites visible at once
|
||||
NUM_COLS = 63
|
||||
|
||||
; Useful constants
|
||||
W_LOG_256 = $0800
|
||||
W_LOG_VIEW_DIST = $0E3F
|
||||
|
||||
; Variables
|
||||
backBuf: !byte 0 ; (value 0 or 1)
|
||||
frontBuf: !byte 0 ; (value 0 or 1)
|
||||
@ -575,26 +579,266 @@ spriteFu:
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
spriteCalc: !zone
|
||||
ldy #0
|
||||
lda spriteX
|
||||
lda #0 ; track sign bits
|
||||
sta .bSgnSinT
|
||||
sta .bSgnCosT
|
||||
sta .bSgnDx
|
||||
sta .bSgnDy
|
||||
sta .bSgnRy
|
||||
|
||||
; Look up sin of player direction, minus wLog256, and store it
|
||||
lda playerDir
|
||||
asl
|
||||
tay
|
||||
lda sinTbl,y
|
||||
sta .wLogSinT ; store lo byte
|
||||
lda sinTbl+1,y
|
||||
bpl + ; sign bit clear?
|
||||
and #$7F ; sign bit was set - mask it off
|
||||
inc .bSgnSinT ; and update sign flag
|
||||
+ sec
|
||||
sbc #8 ; subtract wLog256
|
||||
sta .wLogSinT+1 ; store hi byte
|
||||
|
||||
; Look up cos of player direction, minus wLog256, and store it
|
||||
lda playerDir
|
||||
sec
|
||||
sbc #4 ; cos(a) = sin(a - 90 degrees)
|
||||
and #$F ; wrap around
|
||||
asl
|
||||
tay
|
||||
lda sinTbl,y
|
||||
sta .wLogCosT ; store lo byte
|
||||
lda sinTbl+1,y
|
||||
bpl + ; sign bit clear?
|
||||
and #$7F ; sign bit was set - mask it off
|
||||
inc .bSgnCosT ; and update sign byte
|
||||
+ sec
|
||||
sbc #8 ; subtract wLog256
|
||||
sta .wLogCosT+1 ; store hi byte
|
||||
|
||||
; Calculate wDx = spriteX - playerX, as abs value and a sign bit
|
||||
lda spriteX ; calculate spriteX - playerX
|
||||
sec
|
||||
sbc playerX
|
||||
pha
|
||||
lda spriteX+1
|
||||
tay ; stash lo byte
|
||||
lda spriteX+1 ; work on hi byte
|
||||
sbc playerX+1
|
||||
bcs +
|
||||
eor #$FF
|
||||
tax ; put hi byte in X where we need it
|
||||
bpl + ; if positive, no inversion necessary
|
||||
inc .bSgnDx ; flip sign bit for output
|
||||
jsr .negYX ; negate to get absolute value
|
||||
+ tya ; lo byte in A where log2 wants it
|
||||
jsr log2_w_w ; wants A=lo, X=Hi
|
||||
sta .wLogDx
|
||||
stx .wLogDx+1
|
||||
|
||||
; Calculate wDy = spriteY - playerY, as abs value and a sign bit
|
||||
lda spriteY ; calculate spriteX - playerX
|
||||
sec
|
||||
sbc playerY
|
||||
tay ; stash lo byte
|
||||
lda spriteY+1 ; work on hi byte
|
||||
sbc playerY+1
|
||||
tax ; put hi byte in X where we need it
|
||||
bpl + ; if positive, no inversion necessary
|
||||
inc .bSgnDy ; flip sign bit for output
|
||||
jsr .negYX ; negate to get absolute value
|
||||
+ tya ; lo byte in A where log2 wants it
|
||||
jsr log2_w_w ; wants A=lo, X=Hi
|
||||
sta .wLogDy
|
||||
stx .wLogDy+1
|
||||
|
||||
; Calculate wRx = bSgnDx*bSgnCosT*pow2_w_w(wLogDx + wLogCosT) -
|
||||
; bSgnDy*bSgnSinT*pow2_w_w(wLogDy + wLogSinT)
|
||||
lda .wLogDx ; start with lo byte
|
||||
clc
|
||||
adc .wLogCosT
|
||||
tay ; put it in Y where pow2 wants it
|
||||
lda .wLogDx+1 ; now do hi byte
|
||||
adc .wLogCosT+1
|
||||
tax ; in X where pow2 wants it
|
||||
jsr pow2_w_w ; transform from log space to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
|
||||
tay ; set lo byte aside
|
||||
lda .bSgnDx
|
||||
eor .bSgnCosT ; multiply the two sign bits together
|
||||
beq + ; if result is clear, no negation
|
||||
jsr .negYX ; negate
|
||||
+ sty .wRx ; save partial result
|
||||
stx .wRx+1
|
||||
lda .wLogDy ; start with lo byte
|
||||
clc
|
||||
adc .wLogSinT
|
||||
tay ; put it in Y where pow2 wants it
|
||||
lda .wLogDy+1 ; now do hi byte
|
||||
adc .wLogSinT+1
|
||||
tax ; in X where pow2 wants it
|
||||
jsr pow2_w_w ; transform from log space to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
|
||||
tay ; set lo byte aside
|
||||
lda .bSgnDy
|
||||
eor .bSgnSinT ; multiply the two sign bits together
|
||||
eor #1 ; one extra inversion since we want to end up subtracting this
|
||||
beq + ; if result is clear, no negation
|
||||
jsr .negYX ; negate
|
||||
+ tya
|
||||
clc
|
||||
adc .wRx ; add to partial result
|
||||
sta .wRx
|
||||
txa
|
||||
adc .wRx+1 ; also hi byte
|
||||
sta .wRx+1
|
||||
|
||||
; if wRx is negative, it means sprite is behind viewer... we get out of school early.
|
||||
bpl +
|
||||
!if DEBUG { +prStr : !text "Sprite is behind viewer.",0 }
|
||||
rts
|
||||
|
||||
|
||||
; Calculate wRy = bSgnDx*bSgnSinT*pow2_w_w(wLogDx + wLogSinT) +
|
||||
; bSgnDy*bSgnCosT*pow2_w_w(wLogDy + wLogCosT);
|
||||
|
||||
+ lda .wLogDx ; start with lo byte
|
||||
clc
|
||||
adc .wLogSinT
|
||||
tay ; put it in Y where pow2 wants it
|
||||
lda .wLogDx+1 ; now do hi byte
|
||||
adc .wLogSinT+1
|
||||
tax ; in X where pow2 wants it
|
||||
jsr pow2_w_w ; transform from log space to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
|
||||
tay ; set lo byte aside
|
||||
lda .bSgnDx
|
||||
eor .bSgnSinT ; multiply the two sign bits together
|
||||
beq + ; if result is clear, no negation
|
||||
jsr .negYX ; negate
|
||||
+ sty .wRy ; save partial result
|
||||
stx .wRy+1
|
||||
lda .wLogDy ; start with lo byte
|
||||
clc
|
||||
adc .wLogCosT
|
||||
tay ; put it in Y where pow2 wants it
|
||||
lda .wLogDy+1 ; now do hi byte
|
||||
adc .wLogCosT+1
|
||||
tax ; in X where pow2 wants it
|
||||
jsr pow2_w_w ; transform from log space to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
|
||||
tay ; set lo byte aside
|
||||
lda .bSgnDy
|
||||
eor .bSgnCosT ; multiply the two sign bits together
|
||||
beq + ; if result is clear, no negation
|
||||
jsr .negYX ; negate
|
||||
+ tya
|
||||
clc
|
||||
adc .wRy ; add to partial result
|
||||
tay
|
||||
txa
|
||||
adc .wRy+1 ; also hi byte
|
||||
tax
|
||||
pla
|
||||
eor #$FF
|
||||
adc #1
|
||||
bcc ++
|
||||
bpl + ; if already positive, skip negation
|
||||
jsr .negYX ; negate to get abs value
|
||||
inc .bSgnRy ; and update sign bit
|
||||
+ sty .wRy ; save result (we may not actually need to do this, but it helps w/ debug)
|
||||
stx .wRy+1
|
||||
tya ; get lo byte where it needs to be for log2
|
||||
jsr log2_w_w ; calculate the log of wRy
|
||||
sta .wLogRy ; save it for later
|
||||
stx .wLogRy+1
|
||||
|
||||
; Calculate wLogSqRy = (log2_w_w(wRy) << 1) - wLog256;
|
||||
asl ; we already have it in register. Shift it up 1 bit
|
||||
sta .wLogSqRy ; save lo byte
|
||||
txa ; get hi byte
|
||||
rol ; shift up 1 bit, with carry from lo byte
|
||||
sec
|
||||
sbc #8 ; subtract wLog256 = $800
|
||||
sta .wLogSqRy+1
|
||||
|
||||
; Calculate wLogSqRx = (log2_w_w(wRx) << 1) - wLog256
|
||||
+ lda .wRx
|
||||
ldx .wRx+1
|
||||
jsr log2_w_w ; calculate log of wRx
|
||||
asl ; shift it up 1 bit
|
||||
sta .wLogSqRx ; save lo byte
|
||||
tay ; save it also in Y for upcoming pow2
|
||||
txa ; get hi byte
|
||||
rol ; shift up 1 bit, with carry from lo byte
|
||||
sec
|
||||
sbc #8 ; subtract wlog256 = $800
|
||||
sta .wLogSqRx+1
|
||||
|
||||
; Calculate wSqDist = pow2_w_w(wLogSqRx) + pow2_w_w(wLogSqRy)
|
||||
tax ; get lo byte where we need for pow2 (hi byte already in Y)
|
||||
jsr pow2_w_w ; convert back to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
|
||||
sta .wSqDist ; save partial result
|
||||
stx .wSqDist+1
|
||||
ldy .wLogSqRy ; get wLogSqRy into the right regs
|
||||
ldx .wLogSqRy+1
|
||||
jsr pow2_w_w ; convert it back to normal space also (in: Y=lo,X=hi, out: A=lo,X=hi)
|
||||
clc
|
||||
adc .wSqDist ; add to previous partial result (lo byte)
|
||||
sta .wSqDist ; save lo byte
|
||||
tay ; also stash aside
|
||||
txa
|
||||
adc .wSqDist+1 ; hi byte of partial
|
||||
sta .wSqDist+1 ; save hi byte
|
||||
|
||||
; Calculate wLogDist = (log2_w_w(wSqDist) + wLog256) >> 1
|
||||
txa ; hi byte in X
|
||||
tya ; lo byte in A
|
||||
jsr log2_w_w ; convert to log space
|
||||
tay ; set aside lo byte
|
||||
txa ; work on hi byte
|
||||
clc
|
||||
adc #8 ; add wLog256 = $800
|
||||
lsr ; shift right 1 bit -> carry
|
||||
sta .wLogDist+1
|
||||
tya ; finish off lo byte
|
||||
ror ; shift right with carry from hi byte
|
||||
sta .wLogDist
|
||||
|
||||
; Calculate wSize = pow2_w_w(wLogViewDist - wLogDist)
|
||||
lda #<W_LOG_VIEW_DIST ; lo byte of constant
|
||||
sec
|
||||
sbc wLogDist ; minus log dist
|
||||
tay ; lo byte where pow2 wants it
|
||||
lda #>W_LOG_VIEW_DIST ; hi byte of constant
|
||||
sbc wLogDist ; minus log dist
|
||||
tax ; hi byte where pow2 wants it
|
||||
jsr pow2_w_w ; get back from log space to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
|
||||
sta .wSize
|
||||
stx .wSize+1
|
||||
|
||||
; Calculate wX = bSgnRy * pow2_w_w(log2_w_w(wRy) - wLogDist + log2_w_w(252 / 8 / 0.44))
|
||||
; Note: log2_w_w(252 / 8 / 0.44) = $626
|
||||
lda .wLogRy ; calc wRy minus wLogDist, lo byte
|
||||
sec
|
||||
sbc .wLogDist
|
||||
tay ; stash lo byte temporarily
|
||||
lda .wLogRy+1 ; now work on hi byte
|
||||
sbc .wLogDist+1
|
||||
tax ; stash hi byte
|
||||
tya ; back to lo byte
|
||||
clc
|
||||
adc #$26 ; add lo byte of const log2_w_w(252 / 8 / 0.44)
|
||||
tay ; put it where pow2 wants it
|
||||
txa ; finish off hi byte
|
||||
adc #6 ; hi byte of const
|
||||
tax ; put it where pow2 wants it
|
||||
jsr pow2_w_w ; back to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
|
||||
sta .wX
|
||||
stx .wX+1
|
||||
|
||||
; TODO: finish this routine
|
||||
|
||||
.negYX: ; subroutine to negate value in Y=lo,X=hi.
|
||||
tya
|
||||
eor #$FF ; invert lo byte
|
||||
tay
|
||||
txa
|
||||
eor #$FF ; invert hi byte
|
||||
tax
|
||||
iny ; bump by 1 for true negation
|
||||
bne +
|
||||
inx
|
||||
+ pla
|
||||
++ sty .bSgn1
|
||||
jsr log2_w_w
|
||||
sta .wSum1
|
||||
stx .wSum1+1
|
||||
+ rts
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; Save a link in the linked column data, sorted according to its depth.
|
||||
@ -2750,10 +2994,6 @@ firstLink: !fill NUM_COLS
|
||||
mapSpriteL !fill MAX_SPRITES
|
||||
mapSpriteH !fill MAX_SPRITES
|
||||
|
||||
; Useful constants
|
||||
wLog256: !word $0800
|
||||
wLogViewDist: !word $0E3F
|
||||
|
||||
; Movement amounts when walking at each angle
|
||||
; Each entry consists of an X bump and a Y bump, in 8.8 fixed point
|
||||
walkDirs:
|
||||
|
Loading…
Reference in New Issue
Block a user