mirror of
https://github.com/schibo/1964js.git
synced 2025-04-02 10:52:54 -04:00
340 lines
13 KiB
CoffeeScript
340 lines
13 KiB
CoffeeScript
###1964js - JavaScript/HTML5 port of 1964 - N64 emulator
|
|
Copyright (C) 2012 Joel Middendorf
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.###
|
|
|
|
useExternalTextures = false #for loading community texture packs
|
|
|
|
C1964jsRenderer = (settings, glx, webGL) ->
|
|
gl = glx
|
|
@canvas = undefined
|
|
|
|
texrectVertexPositionBuffer = gl.createBuffer()
|
|
texrectVertexTextureCoordBuffer = gl.createBuffer()
|
|
texrectVertexIndexBuffer = gl.createBuffer()
|
|
texrectVertexIndices = [0, 1, 2, 0, 2, 3]
|
|
gl.bindBuffer gl.ELEMENT_ARRAY_BUFFER, texrectVertexIndexBuffer
|
|
gl.bufferData gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(texrectVertexIndices), gl.STATIC_DRAW
|
|
texrectVertexIndexBuffer.itemSize = 1
|
|
texrectVertexIndexBuffer.numItems = 6
|
|
@colorsTexture0 = gl.createTexture()
|
|
|
|
@videoHLE = undefined
|
|
fivetoeight = [0x00,0x08,0x10,0x18,0x21,0x29,0x31,0x39,0x42,0x4A,0x52,0x5A,0x63,0x6B,0x73,0x7B,0x84,0x8C,0x94,0x9C,0xA5,0xAD,0xB5,0xBD,0xC6,0xCE,0xD6,0xDE,0xE7,0xEF,0xF7,0xFF]
|
|
fourtoeight = [0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff]
|
|
threetoeight = [
|
|
0x00, # 000 -> 00 00 00 00
|
|
0x24, # 001 -> 00 10 01 00
|
|
0x49, # 010 -> 01 00 10 01
|
|
0x6d, # 011 -> 01 10 11 01
|
|
0x92, # 100 -> 10 01 00 10
|
|
0xb6, # 101 -> 10 11 01 10
|
|
0xdb, # 110 -> 11 01 10 11
|
|
0xff # 111 -> 11 11 11 11
|
|
]
|
|
onetoeight = [
|
|
0x00, # 0 -> 00 00 00 00
|
|
0xff # 1 -> 11 11 11 11
|
|
]
|
|
|
|
@textureCache = new Object()
|
|
|
|
@texRect = (xl, yl, xh, yh, s, t, dsdx, dtdy, tile, tmem, videoHLE, isFillRect) ->
|
|
|
|
tileWidth = ((tile.lrs >> 2) + 1) - tile.uls
|
|
tileHeight = ((tile.lrt >> 2) + 1) - tile.ult
|
|
|
|
@videoHLE = videoHLE
|
|
|
|
nextPow2Width = videoHLE.pow2roundup tileWidth
|
|
nextPow2Height = videoHLE.pow2roundup tileHeight
|
|
|
|
widthscale = tileWidth / nextPow2Width
|
|
heightscale = tileHeight / nextPow2Height
|
|
|
|
rectWidth = xh-xl
|
|
rectHeight = yh-yl
|
|
|
|
#console.log "Rectsize:"+rectWidth+"x"+rectHeight+" S:"+s+"("+dsdx+") T:"+t+"("+dtdy
|
|
|
|
sl = s / tileHeight
|
|
tl = t / tileWidth
|
|
sh = (s + (rectWidth*dsdx)) / tileWidth * widthscale
|
|
th = (t + (rectHeight*dtdy)) / tileHeight * heightscale
|
|
|
|
xTrans = 160
|
|
yTrans = 120
|
|
xl = (xl-xTrans)/xTrans
|
|
xh = (xh-xTrans)/xTrans
|
|
yl = -(yl-yTrans)/yTrans
|
|
yh = -(yh-yTrans)/yTrans
|
|
|
|
initQuad xl, yl, xh, yh, sl, tl, sh, th, videoHLE
|
|
@draw tile, tmem, videoHLE, nextPow2Width, nextPow2Height, tileWidth, tileHeight, isFillRect
|
|
return
|
|
|
|
@formatTexture = (tile, tmem, videoHLE, isFillRect) ->
|
|
@videoHLE = videoHLE
|
|
|
|
return undefined if tile.lrs is undefined or tile.lrt is undefined or tile.uls is undefined or tile.ult is undefined
|
|
|
|
tileWidth = ((tile.lrs >> 2) + 1) - tile.uls
|
|
tileHeight = ((tile.lrt >> 2) + 1) - tile.ult
|
|
|
|
|
|
nextPow2Width = @videoHLE.pow2roundup tileWidth
|
|
nextPow2Height = @videoHLE.pow2roundup tileHeight
|
|
|
|
textureSize = nextPow2Width * nextPow2Height * 4
|
|
#hacky texture cache unique id (want to see how fast we currently are)
|
|
|
|
if tileWidth is undefined or tileHeight is undefined
|
|
e = Error
|
|
e.message = "Undefined tile dimensions"
|
|
throw e
|
|
|
|
@useTextureCache = false #change to true to try texture cache
|
|
if @useTextureCache is true
|
|
randomPixel = nextPow2Width * nextPow2Height
|
|
textureId = (tmem[randomPixel]>>>0) << 24 | (tmem[randomPixel+nextPow2Width+1]>>>0) << 16 | (tmem[randomPixel+nextPow2Width*2+1]>>>0) << 8 | tmem[randomPixel+nextPow2Width*3+1]>>>0
|
|
return @textureCache[textureId] if @textureCache[textureId]?
|
|
|
|
if (nextPow2Width isnt tileWidth) or (nextPow2Height isnt tileHeight)
|
|
# handle non-power of 2 textures
|
|
if @canvas is undefined
|
|
@canvas = document.getElementById('Canvas');
|
|
@canvas.width = tileWidth
|
|
@canvas.height = tileHeight
|
|
context = @canvas.getContext('2d')
|
|
imageData = context.createImageData(nextPow2Width, nextPow2Height)
|
|
texture = imageData.data
|
|
else
|
|
texture = new Uint8Array(textureSize)
|
|
|
|
dstRowOffset = 0
|
|
dstRowStride = nextPow2Width * 4
|
|
srcRowStride = tile.line<<3
|
|
srcRowOffset = tile.tmem<<3
|
|
|
|
if videoHLE.cycleType is 3 or isFillRect is true
|
|
# no need to copy`
|
|
else
|
|
if @useTextureCache is true
|
|
@textureCache[textureId] = texture
|
|
switch tile.fmt
|
|
when consts.TXT_FMT_RGBA # rgba
|
|
switch tile.siz
|
|
when consts.TXT_SIZE_16b # rgba5551
|
|
j=0
|
|
while j < tileHeight
|
|
i=0
|
|
srcOffset = srcRowOffset
|
|
dstOffset = dstRowOffset
|
|
while i < tileWidth
|
|
color16 = tmem[srcOffset]<<8 | tmem[srcOffset+1]
|
|
texture[dstOffset] = fivetoeight[color16 >> 11 & 0x1F]
|
|
texture[dstOffset + 1] = fivetoeight[color16 >> 6 & 0x1F]
|
|
texture[dstOffset + 2] = fivetoeight[color16 >> 1 & 0x1F]
|
|
texture[dstOffset + 3] = if ((color16 & 0x01) is 0) then 0x00 else 0xFF
|
|
i++
|
|
srcOffset += 2
|
|
dstOffset += 4
|
|
j++
|
|
srcRowOffset += srcRowStride
|
|
dstRowOffset += dstRowStride
|
|
else
|
|
console.error "TODO: tile format " + tile.fmt + ", tile.size:" + tile.siz
|
|
when consts.TXT_FMT_IA # ia
|
|
switch tile.siz
|
|
when consts.TXT_SIZE_8b # ia8
|
|
j=0
|
|
while j < tileHeight
|
|
i=0
|
|
srcOffset = srcRowOffset
|
|
dstOffset = dstRowOffset
|
|
while i < tileWidth
|
|
b = tmem[srcOffset]
|
|
I = fourtoeight[(b >>> 4)& 0x0F]
|
|
a = fourtoeight[b & 0x0F]
|
|
texture[dstOffset] = I
|
|
texture[dstOffset + 1] = I
|
|
texture[dstOffset + 2] = I
|
|
texture[dstOffset + 3] = a
|
|
i++
|
|
srcOffset += 1
|
|
dstOffset += 4
|
|
j++
|
|
srcRowOffset += srcRowStride
|
|
dstRowOffset += dstRowStride
|
|
when consts.TXT_SIZE_4b # ia4
|
|
j=0
|
|
while j < tileHeight
|
|
i=0
|
|
srcOffset = srcRowOffset
|
|
dstOffset = dstRowOffset
|
|
while i < tileWidth
|
|
b = tmem[srcOffset]
|
|
I0 = threetoeight[(b & 0xe0)>>>5]
|
|
a0 = onetoeight[(b & 0x10)>>>4]
|
|
I1 = threetoeight[(b & 0x0e)>>>1]
|
|
a1 = onetoeight[(b & 0x01)>>>0]
|
|
texture[dstOffset] = I0
|
|
texture[dstOffset + 1] = I0
|
|
texture[dstOffset + 2] = I0
|
|
texture[dstOffset + 3] = a0
|
|
texture[dstOffset + 4] = I1
|
|
texture[dstOffset + 5] = I1
|
|
texture[dstOffset + 6] = I1
|
|
texture[dstOffset + 7] = a1
|
|
i+=2
|
|
srcOffset += 1
|
|
dstOffset += 8
|
|
if tileWidth & 1
|
|
I0 = threetoeight[(b & 0xe0)>>>5]
|
|
a0 = onetoeight[(b & 0x10)>>>4]
|
|
texture[dstOffset] = I0
|
|
texture[dstOffset + 1] = I0
|
|
texture[dstOffset + 2] = I0
|
|
texture[dstOffset + 3] = a0
|
|
i+=1
|
|
srcOffset += 1
|
|
dstOffset += 4
|
|
j++
|
|
srcRowOffset += srcRowStride
|
|
dstRowOffset += dstRowStride
|
|
when consts.TXT_SIZE_16b # ia16
|
|
j=0
|
|
while j < tileHeight
|
|
i=0
|
|
srcOffset = srcRowOffset
|
|
dstOffset = dstRowOffset
|
|
while i < tileWidth
|
|
I = tmem[srcOffset]
|
|
a = tmem[srcOffset+1]
|
|
texture[dstOffset] = I
|
|
texture[dstOffset + 1] = I
|
|
texture[dstOffset + 2] = I
|
|
texture[dstOffset + 3] = a
|
|
i++
|
|
srcOffset += 2
|
|
dstOffset += 4
|
|
j++
|
|
srcRowOffset += srcRowStride
|
|
dstRowOffset += dstRowStride
|
|
else
|
|
console.error "TODO: tile format " + tile.fmt + ", tile.size" + tile.siz
|
|
else
|
|
console.error "TODO: tile format " + tile.fmt + ", tile.size" + tile.siz
|
|
|
|
# if @useTextureCache is true
|
|
# return @textureCache[textureId]
|
|
if (nextPow2Width isnt tileWidth) or (nextPow2Height isnt tileHeight)
|
|
context.putImageData(imageData, 0, 0);
|
|
return @canvas
|
|
return texture
|
|
|
|
initQuad = (xl, yl, xh, yh, sl, tl, sh, th, videoHLE) ->
|
|
vertices = [xh, yh, 0.0, xh, yl, 0.0, xl, yl, 0.0, xl, yh, 0.0]
|
|
gl.bindBuffer gl.ARRAY_BUFFER, texrectVertexPositionBuffer
|
|
gl.bufferData gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW
|
|
texrectVertexPositionBuffer.itemSize = 3
|
|
texrectVertexPositionBuffer.numItems = 4
|
|
|
|
textureCoords = [sh, th, sh, tl, sl, tl, sl, th]
|
|
gl.bindBuffer gl.ARRAY_BUFFER, texrectVertexTextureCoordBuffer
|
|
gl.bufferData gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW
|
|
texrectVertexTextureCoordBuffer.itemSize = 2
|
|
texrectVertexTextureCoordBuffer.numItems = 4
|
|
return
|
|
|
|
@draw = (tile, tmem, videoHLE, nextPow2Width, nextPow2Height, tileWidth, tileHeight, isFillRect) ->
|
|
videoHLE.setBlendFunc()
|
|
# gl.useProgram webGL.shaderProgram
|
|
|
|
gl.enableVertexAttribArray webGL.shaderProgram.vertexPositionAttribute
|
|
gl.bindBuffer gl.ARRAY_BUFFER, texrectVertexPositionBuffer
|
|
gl.vertexAttribPointer webGL.shaderProgram.vertexPositionAttribute, texrectVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0
|
|
|
|
gl.enableVertexAttribArray webGL.shaderProgram.textureCoordAttribute
|
|
gl.bindBuffer gl.ARRAY_BUFFER, texrectVertexTextureCoordBuffer
|
|
gl.vertexAttribPointer webGL.shaderProgram.textureCoordAttribute, texrectVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0
|
|
|
|
#console.log "Binding Texture Size: "+tileWidth+" x "+tileHeight+" -> "+canvaswidth+" x "+canvasheight
|
|
|
|
textureData = @formatTexture(tile, tmem, videoHLE, isFillRect)
|
|
if textureData isnt undefined
|
|
gl.activeTexture(gl.TEXTURE0)
|
|
gl.bindTexture(gl.TEXTURE_2D, @colorsTexture0)
|
|
if textureData instanceof HTMLElement
|
|
#it's a canvas
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureData)
|
|
else
|
|
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, nextPow2Width, nextPow2Height, 0, gl.RGBA, gl.UNSIGNED_BYTE, textureData)
|
|
|
|
|
|
wrapS = gl.REPEAT
|
|
wrapT = gl.REPEAT
|
|
if ((tile.cms is consts.RDP_TXT_CLAMP) or (tile.masks is 0))
|
|
wrapS = gl.CLAMP_TO_EDGE
|
|
else if tile.cms is consts.RDP_TXT_MIRROR
|
|
wrapS = gl.MIRRORED_REPEAT
|
|
if ((tile.cmt is consts.RDP_TXT_CLAMP) or (tile.maskt is 0))
|
|
wrapT = gl.CLAMP_TO_EDGE
|
|
else if tile.cmt is consts.RDP_TXT_MIRROR
|
|
wrapT = gl.MIRRORED_REPEAT
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS)
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT)
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
|
gl.uniform1i webGL.shaderProgram.samplerUniform, @colorsTexture0
|
|
|
|
if videoHLE.primColor.length > 0
|
|
gl.uniform4fv webGL.shaderProgram.uPrimColor, videoHLE.primColor
|
|
|
|
if videoHLE.fillColor.length > 0
|
|
gl.uniform4fv webGL.shaderProgram.uFillColor, videoHLE.fillColor
|
|
|
|
if videoHLE.blendColor.length > 0
|
|
gl.uniform4fv webGL.shaderProgram.uBlendColor, videoHLE.blendColor
|
|
|
|
if videoHLE.envColor.length > 0
|
|
gl.uniform4fv webGL.shaderProgram.uEnvColor, videoHLE.envColor
|
|
|
|
|
|
if isFillRect is true
|
|
cycleType = 3
|
|
else
|
|
cycleType = videoHLE.cycleType
|
|
gl.uniform1i webGL.shaderProgram.cycleType, cycleType
|
|
|
|
gl.bindBuffer gl.ELEMENT_ARRAY_BUFFER, texrectVertexIndexBuffer
|
|
webGL.setMatrixUniforms webGL.shaderProgram
|
|
# webGL.setCombineUniforms videoHLE, webGL.shaderProgram
|
|
|
|
if settings.wireframe is true
|
|
gl.drawElements gl.LINE_LOOP, texrectVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0
|
|
else
|
|
gl.drawElements gl.TRIANGLES, texrectVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0
|
|
|
|
texrectVertexTextureCoordBuffer.numItems = 0
|
|
texrectVertexPositionBuffer.numItems = 0
|
|
return
|
|
|
|
return this
|
|
#hack global space until we export classes properly
|
|
#node.js uses exports; browser uses this (window)
|
|
root = exports ? this
|
|
root.C1964jsRenderer = C1964jsRenderer
|