###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