1 /** 2 * Compositor object. 3 * 4 * @author Andrew J. Baker 5 */ 6 7 /** @namespace Steppe */ 8 var Steppe = (function(Steppe) { 9 /** @class */ 10 Steppe.Compositor = function(undefined) { 11 var _heightmap = [], 12 _outOfBoundsHeightmap = [], 13 _textureArray = []; 14 15 return { 16 /** 17 * Add a texture to the texture-array. 18 * 19 * @param {number} height The 'height' at which to apply the 20 * texture. 21 * @param {Image} textureImage The image to use as a texture. 22 * @return {Compositor} This (fluent interface). 23 */ 24 addTexture: function(height, textureImage) { 25 if ( !(textureImage instanceof HTMLImageElement)) { 26 throw('Invalid textureImage: not an instance of ' + 27 'HTMLImageElement'); 28 } 29 30 if (height < 0 || height > 255) { 31 throw('Invalid height; must be in the range 0..255'); 32 } 33 34 if (textureImage.width != 256 || 35 textureImage.height != 256) { 36 throw('Invalid texture dimensions; must be 256x256'); 37 } 38 39 var textureCanvas = document.createElement('canvas'); 40 textureCanvas.width = textureImage.width; 41 textureCanvas.height = textureImage.height; 42 43 var textureContext = textureCanvas.getContext('2d'); 44 45 textureContext.drawImage(textureImage, 0, 0, 46 textureCanvas.width, textureCanvas.height); 47 48 _textureArray[height] = { 49 data: textureContext.getImageData(0, 0, 50 textureCanvas.width, 51 textureCanvas.height).data, 52 height: textureCanvas.height, 53 width: textureCanvas.width 54 }; 55 56 return this; 57 }, 58 59 /** 60 * Create a composite texturemap from the heightmap and 61 * texture-array images. 62 * 63 * @param {HTMLCanvasElement} texturemapCanvas The texturemap 64 * canvas to which the heightmap and 65 * texture-array images are 66 * composited. 67 */ 68 composite: function(texturemapCanvas) { 69 if ( !(texturemapCanvas instanceof HTMLCanvasElement)) { 70 throw('Invalid texturemapCanvas: not an instance of ' + 71 'HTMLCanvasElement'); 72 } 73 74 var texturemapContext = texturemapCanvas.getContext('2d'); 75 76 var textureCanvas = document.createElement('canvas'); 77 textureCanvas.width = 256; 78 textureCanvas.height = 256; 79 80 var textureContext = textureCanvas.getContext('2d'); 81 82 if (_textureArray[255] === undefined) { 83 throw('No texture added at height 255; unable to ' + 84 'composite'); 85 } 86 for (var i = 254; i >= 0; --i) { 87 if (_textureArray[i] === undefined) { 88 _textureArray[i] = _textureArray[i + 1]; 89 } 90 } 91 92 for (var y = 0; y < 1024; ++y) { 93 for (var x = 0; x < 1024; ++x) { 94 var height = _heightmap[(y << 10) + x]; 95 96 var index = ((y & 255) << 10) + ((x & 255) << 2); 97 98 texturemapContext.fillStyle = 'rgb(' + 99 _textureArray[height].data[index] + ',' + 100 _textureArray[height].data[index + 1] + ',' + 101 _textureArray[height].data[index + 2] + ')'; 102 103 texturemapContext.fillRect(x, y, 1, 1); 104 } 105 } 106 }, 107 108 /** 109 * Get the heightmap as an array. 110 * 111 * @return {array} The heightmap canvas converted to an array. 112 */ 113 getHeightmap: function() { 114 return _heightmap; 115 }, 116 117 /** 118 * Get the out-of-bounds heightmap as an array. 119 * 120 * @return {array} The out-of-bounds heightmap canvas converted to 121 * an array. 122 */ 123 getOutOfBoundsHeightmap: function() { 124 return _outOfBoundsHeightmap; 125 }, 126 127 /** 128 * Put a mask (a 2.5D sprite's heightmap). 129 * 130 * @param {HTMLImageElement} mask The 2.5D sprite's heightmap; 131 * should contain a greyscale 132 * image. 133 * @param {number} x The x-ordinate. 134 * @param {number} y The y-ordinate. 135 * @param {number} scaleFactor The scale factor to multiply each 136 * value in the mask by. 137 * @return {Compositor} This (fluent interface). 138 */ 139 putMask: function(mask, x, y, scaleFactor) { 140 var maskCanvas = document.createElement('canvas'); 141 maskCanvas.width = mask.width; 142 maskCanvas.height = mask.height; 143 144 var maskContext = maskCanvas.getContext('2d'); 145 146 maskContext.drawImage(mask, 0, 0); 147 148 var data = maskContext.getImageData(0, 0, maskCanvas.width, 149 maskCanvas.height).data; 150 151 for (var y2 = 0; y2 < maskCanvas.height; ++y2) { 152 for (var x2 = 0; x2 < maskCanvas.width; ++x2) { 153 var index = y2 * maskCanvas.width + x2; 154 155 if (data[index * 4 + 3]) { 156 var index = ((y2 + y) << 10) + (x2 + x); 157 158 _heightmap[index] = 192 + 159 data[(y2 * maskCanvas.width + x2) * 4] * 160 scaleFactor; 161 } 162 } 163 } 164 165 return this; 166 }, 167 168 /** 169 * Set the heightmap to use for compositing [and 170 * out-of-bounds]. 171 * 172 * @param {HTMLCanvasElement} heightmapCanvas The heightmap 173 * canvas; should 174 * contain a 175 * greyscale image. 176 * @return {Compositor} This (fluent interface). 177 */ 178 setHeightmap: function(heightmapCanvas) { 179 var data = heightmapCanvas.getContext('2d') 180 .getImageData(0, 0, 1024, 1024).data; 181 182 for (var y = 0; y < 1024; ++y) { 183 for (var x = 0; x < 1024; ++x) { 184 var index = (y << 10) + x; 185 186 _heightmap[index] = data[index << 2]; 187 _outOfBoundsHeightmap[index] = _heightmap[index]; 188 } 189 } 190 191 return this; 192 } 193 }; 194 }; 195 196 return Steppe; 197 } (Steppe || { }) ); 198