diff --git a/fractal-terrain-generation/.gitignore b/fractal-terrain-generation/.gitignore index 893afd5..9ea7034 100644 --- a/fractal-terrain-generation/.gitignore +++ b/fractal-terrain-generation/.gitignore @@ -1 +1,2 @@ dist/js +dist/index.html diff --git a/fractal-terrain-generation/README.md b/fractal-terrain-generation/README.md index f37e6fb..bbea32b 100644 --- a/fractal-terrain-generation/README.md +++ b/fractal-terrain-generation/README.md @@ -2,15 +2,14 @@ Demo originally created for talk given by Ryland Goldstein at _Serverless conf 2018_ in San Francisco. -1. Replace in `dist/index.html` with your account number. -1. Export `BINARIS_ACCOUNT_NUMBER` to your environment -1. Run the following command - `export FRACTAL_ENDPOINT=https://run-sandbox.binaris.com/v2/run/${BINARIS_ACCOUNT_NUMBER}/public_fractal` -1. Run `npm install` -1. Run `bn deploy public_fractal` -1. Run `npm run build` -1. Run `bn deploy public_servePage` +1. If you haven't run `bn login` before, do so +1. Run `npm run install_deps` +1. Run `npm run deploy` 1. Navigate to the generated Binaris function URL for `servePage` in your browser For even faster generation consider upgrading to the Binaris paid tier. + +If you made changes to the frontend (anything in `src`) simply run `npm run deploy_frontend` + +For backend changes run `npm run deploy_backend` \ No newline at end of file diff --git a/fractal-terrain-generation/builder.js b/fractal-terrain-generation/builder.js new file mode 100644 index 0000000..0fdd973 --- /dev/null +++ b/fractal-terrain-generation/builder.js @@ -0,0 +1,53 @@ +const fse = require('fs-extra'); +const path = require('path'); + +const YMLUtil = require('binaris/lib/binarisYML'); + +const { getAccountId, getAPIKey, getRealm } = require('binaris/lib/userConf'); +const { forceRealm } = require('binaris/sdk'); + +const backendYAMLPath = path.join(__dirname, 'fractal_backend'); +const servingYAMLPath = path.join(__dirname, 'dist'); + +async function getServingFuncName() { + const binarisConf = await YMLUtil.loadBinarisConf(servingYAMLPath); + return YMLUtil.getFuncName(binarisConf); +} + +async function getBackendFuncName() { + const binarisConf = await YMLUtil.loadBinarisConf(backendYAMLPath); + return YMLUtil.getFuncName(binarisConf); +} + +async function getPublicPath(accountID, endpoint) { + const savedEndpoint = process.env.BINARIS_INVOKE_ENDPOINT; + process.env.BINARIS_INVOKE_ENDPOINT = endpoint; + const { getInvokeUrl } = require('binaris/sdk/url'); + const servingName = await getServingFuncName(); + const invokeURL = await getInvokeUrl(accountID, servingName); + process.env.BINARIS_INVOKE_ENDPOINT = savedEndpoint; + return invokeURL; +} + +async function getFractalURL(accountID) { + const { getInvokeUrl } = require('binaris/sdk/url'); + const backendName = await getBackendFuncName(); + return getInvokeUrl(accountID, backendName); +} + +async function prebuild() { + const realm = await getRealm(); + if (realm) { + forceRealm(realm); + } + const accountID = await getAccountId(undefined); + const FRACTAL_ENDPOINT = await getFractalURL(accountID); + const PUBLIC_PATH = (await getPublicPath(accountID, ' ')).slice(8); + return { + FRACTAL_ENDPOINT, + PUBLIC_PATH, + BINARIS_ACCOUNT_ID: accountID, + }; +} + +module.exports = { prebuild }; diff --git a/fractal-terrain-generation/binaris.yml b/fractal-terrain-generation/dist/binaris.yml similarity index 57% rename from fractal-terrain-generation/binaris.yml rename to fractal-terrain-generation/dist/binaris.yml index 47f2aad..c6619b1 100644 --- a/fractal-terrain-generation/binaris.yml +++ b/fractal-terrain-generation/dist/binaris.yml @@ -1,8 +1,4 @@ functions: - public_fractal: - file: fractal.js - entrypoint: handler - runtime: node8 public_servePage: file: servePage.js entrypoint: handler diff --git a/fractal-terrain-generation/dist/index.html b/fractal-terrain-generation/dist/index.html.dot similarity index 98% rename from fractal-terrain-generation/dist/index.html rename to fractal-terrain-generation/dist/index.html.dot index 58b6e79..059ba17 100644 --- a/fractal-terrain-generation/dist/index.html +++ b/fractal-terrain-generation/dist/index.html.dot @@ -196,6 +196,6 @@
Paused
- + diff --git a/fractal-terrain-generation/dist/package.json b/fractal-terrain-generation/dist/package.json new file mode 100644 index 0000000..04c23d3 --- /dev/null +++ b/fractal-terrain-generation/dist/package.json @@ -0,0 +1,16 @@ +{ + "name": "fractal-frontend", + "version": "1.0.0", + "description": "", + "main": "servePage.js", + "scripts": {}, + "keywords": [], + "author": "Ryland Goldstein", + "license": "MIT", + "dependencies": { + "dot": "^1.1.2", + "mime-types": "^2.1.21", + "mz": "^2.7.0", + "path": "^0.12.7" + } +} diff --git a/fractal-terrain-generation/dist/resources/brown_red.png b/fractal-terrain-generation/dist/resources/brown_red.png new file mode 100644 index 0000000..47522eb Binary files /dev/null and b/fractal-terrain-generation/dist/resources/brown_red.png differ diff --git a/fractal-terrain-generation/dist/resources/dark_blue.png b/fractal-terrain-generation/dist/resources/dark_blue.png new file mode 100644 index 0000000..4f00a56 Binary files /dev/null and b/fractal-terrain-generation/dist/resources/dark_blue.png differ diff --git a/fractal-terrain-generation/dist/resources/dark_red.png b/fractal-terrain-generation/dist/resources/dark_red.png new file mode 100644 index 0000000..6564aba Binary files /dev/null and b/fractal-terrain-generation/dist/resources/dark_red.png differ diff --git a/fractal-terrain-generation/dist/resources/light_blue.png b/fractal-terrain-generation/dist/resources/light_blue.png new file mode 100644 index 0000000..20036c8 Binary files /dev/null and b/fractal-terrain-generation/dist/resources/light_blue.png differ diff --git a/fractal-terrain-generation/dist/resources/lime_green.png b/fractal-terrain-generation/dist/resources/lime_green.png new file mode 100644 index 0000000..e410e98 Binary files /dev/null and b/fractal-terrain-generation/dist/resources/lime_green.png differ diff --git a/fractal-terrain-generation/dist/resources/orange.png b/fractal-terrain-generation/dist/resources/orange.png new file mode 100644 index 0000000..2371d1c Binary files /dev/null and b/fractal-terrain-generation/dist/resources/orange.png differ diff --git a/fractal-terrain-generation/dist/resources/yellow.png b/fractal-terrain-generation/dist/resources/yellow.png new file mode 100644 index 0000000..48dfac6 Binary files /dev/null and b/fractal-terrain-generation/dist/resources/yellow.png differ diff --git a/fractal-terrain-generation/servePage.js b/fractal-terrain-generation/dist/servePage.js similarity index 54% rename from fractal-terrain-generation/servePage.js rename to fractal-terrain-generation/dist/servePage.js index 890aef6..d1f8f25 100644 --- a/fractal-terrain-generation/servePage.js +++ b/fractal-terrain-generation/dist/servePage.js @@ -1,10 +1,12 @@ /* eslint-disable no-console */ - const fs = require('mz/fs'); const mime = require('mime-types'); const path = require('path'); +const dot = require('dot'); -const prefix = './dist'; +const prefix = '.'; +const binarisAccountId = process.env.BINARIS_ACCOUNT_ID; +const dots = dot.process(prefix); exports.handler = async (body, ctx) => { let resourcePath = ctx.request.path; @@ -15,14 +17,21 @@ exports.handler = async (body, ctx) => { resourcePath = '/index.html'; } - const webpage = await fs.readFile(`${prefix}${resourcePath}`); + let webPage; + const dotName = resourcePath.substr(1, resourcePath.indexOf('.') - 1); + if (dots[dotName]) { + webpage = dots[dotName]({ binarisAccountId }); + } else { + webpage = await fs.readFile(`${prefix}${resourcePath}`); + } const resourceType = mime.contentType(path.extname(resourcePath)); return new ctx.HTTPResponse({ statusCode: 200, headers: { 'Content-Type': resourceType, - 'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type, Accept', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept', }, body: webpage, }); diff --git a/fractal-terrain-generation/fractal.js b/fractal-terrain-generation/fractal.js deleted file mode 100644 index 24dc5f7..0000000 --- a/fractal-terrain-generation/fractal.js +++ /dev/null @@ -1,65 +0,0 @@ -/* eslint-disable no-console,no-bitwise */ -const noiseGen = require('./function_lib/noiseGen'); -const simplify = require('./function_lib/simplify'); - -exports.handler = async (body, ctx) => { - const { size, xPos, zPos, downscale, heightFactor } = ctx.request.query; - - const genStartTime = process.hrtime(); - - const { blockCount, data, maxHeight } = noiseGen( - parseInt(xPos, 10), parseInt(zPos, 10), size, - downscale, heightFactor - ); - const { verts, indices, normals, tex } = simplify( - data, size, maxHeight, size, - parseInt(xPos, 10), parseInt(zPos, 10) - ); - - // how many elements make up the lookup table - const tableElements = 4; - - const mergedData = new Int16Array(verts.length + indices.length - + normals.length + tex.length + tableElements); - - // this is an unfortunate bit of logic required to make sure - // that our buffers length value doesn't get truncated/overflowed - // in the lookup table. - function split32BitValue(value) { - return [value & 0xFFFF, (value >> 16) & 0xFFFF]; - } - const splitVertsLength = split32BitValue(verts.length); - const splitTexLength = split32BitValue(tex.length); - mergedData[0] = splitVertsLength[0]; - mergedData[1] = splitVertsLength[1]; - mergedData[2] = splitTexLength[0]; - mergedData[3] = splitTexLength[1]; - - mergedData.set(verts, tableElements); - mergedData.set(normals, tableElements + verts.length); - mergedData.set(indices, verts.length + tableElements + normals.length + tex.length); - - const buffer = Buffer.from(mergedData.buffer); - console.log(`buffer size is ${buffer.length / 1000}`); - - const genTime = process.hrtime(genStartTime); - const genDataTimeStr = (genTime[0] * 1000) + (genTime[1] / 1000000); - const customHeaders = { - 'X-Gen-Data-Blockcount': blockCount, - 'X-Gen-Data-Max-Height': maxHeight, - 'X-Gen-Data-Payload-Bytes': buffer.length, - 'X-Gen-Data-Time-Running-MS': genDataTimeStr, - }; - - return new ctx.HTTPResponse({ - statusCode: 200, - headers: { - 'Access-Control-Expose-Headers': Object.keys(customHeaders).join(', '), - 'Content-Type': 'application/octet-stream', - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'Origin X-Requested-With, Content-Type, Accept', - ...customHeaders, - }, - body: buffer, - }); -}; diff --git a/fractal-terrain-generation/fractal_backend/binaris.yml b/fractal-terrain-generation/fractal_backend/binaris.yml new file mode 100644 index 0000000..ba18240 --- /dev/null +++ b/fractal-terrain-generation/fractal_backend/binaris.yml @@ -0,0 +1,5 @@ +functions: + public_fractal: + file: fractal.js + entrypoint: handler + runtime: node8 diff --git a/fractal-terrain-generation/fractal_backend/fractal.js b/fractal-terrain-generation/fractal_backend/fractal.js new file mode 100644 index 0000000..049550e --- /dev/null +++ b/fractal-terrain-generation/fractal_backend/fractal.js @@ -0,0 +1,99 @@ +/* eslint-disable no-console,no-bitwise */ +const noiseGen = require('./noiseGen'); +const simplify = require('./simplify'); + +function makeHeaders(blockCount, maxHeight, + payloadBytes, genTime) { + const statusHeaders = { + 'X-Gen-Data-Blockcount': blockCount, + 'X-Gen-Data-Max-Height': maxHeight, + 'X-Gen-Data-Payload-Bytes': payloadBytes, + 'X-Gen-Data-Time-Running-MS': genTime, + }; + const customHeaders = { + 'Access-Control-Expose-Headers': Object.keys(statusHeaders).join(', '), + 'Content-Type': 'application/octet-stream', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'Origin X-Requested-With, Content-Type, Accept', + ... statusHeaders, + }; + return customHeaders; +} + +exports.handler = async (body, ctx) => { + const { + downscale, + heightFactor, + numTex, + size, + xPos, + yPos, + zPos, + } = ctx.request.query; + + let respBody; + let headers; + let statusCode = 500; + const genStartTime = process.hrtime(); + + const { blockCount, data, maxHeight } = noiseGen( + parseInt(xPos, 10), parseInt(yPos, 10), + parseInt(zPos, 10), parseInt(size, 10), + numTex, downscale, parseInt(heightFactor, 10) + ); + if (blockCount === 0) { + const failedGenTime = process.hrtime(genStartTime); + const genStr = (failedGenTime[0] * 1000) + (failedGenTime[1] / 1000000); + headers = makeHeaders(blockCount, maxHeight, 0, genStr); + respBody = new Buffer(2); + statusCode = 200; + } else { + const { indices, mats, normals, tex, verts } = simplify( + data, size, size, size, + parseInt(xPos, 10), parseInt(yPos, 10), + parseInt(zPos, 10), heightFactor, numTex, + ); + + // how many elements make up the lookup table + const tableElements = 4; + + const mergedData = new Int16Array(verts.length + indices.length + + normals.length + tex.length + mats.length + tableElements); + + // this is an unfortunate bit of logic required to make sure + // that our buffers length value doesn't get truncated/overflowed + // in the lookup table. + function split32BitValue(value) { + return [value & 0xFFFF, (value >> 16) & 0xFFFF]; + } + const splitVertsLength = split32BitValue(verts.length); + const splitTexLength = split32BitValue(tex.length); + mergedData[0] = splitVertsLength[0]; + mergedData[1] = splitVertsLength[1]; + mergedData[2] = splitTexLength[0]; + mergedData[3] = splitTexLength[1]; + + mergedData.set(verts, tableElements); + mergedData.set(normals, tableElements + verts.length); + mergedData.set(tex, tableElements + verts.length + normals.length); + mergedData.set(mats, tableElements + verts.length + normals.length + tex.length); + mergedData.set(indices, verts.length + tableElements + normals.length + tex.length + mats.length); + + respBody = Buffer.from(mergedData.buffer); + console.log(`buffer size is ${respBody.length / 1000}`); + + const genTime = process.hrtime(genStartTime); + const genDataTimeStr = (genTime[0] * 1000) + (genTime[1] / 1000000); + headers = makeHeaders(blockCount, maxHeight, respBody.length, genDataTimeStr); + statusCode = 200; + } + if (ctx.request.query.express_server) { + ctx.set(headers); + return ctx.send(respBody); + } + return new ctx.HTTPResponse({ + statusCode, + headers, + body: respBody, + }); +}; diff --git a/fractal-terrain-generation/function_lib/noiseGen.js b/fractal-terrain-generation/fractal_backend/noiseGen.js similarity index 64% rename from fractal-terrain-generation/function_lib/noiseGen.js rename to fractal-terrain-generation/fractal_backend/noiseGen.js index f2f007d..a6e3c17 100644 --- a/fractal-terrain-generation/function_lib/noiseGen.js +++ b/fractal-terrain-generation/fractal_backend/noiseGen.js @@ -2,6 +2,16 @@ const SimplexNoise = require('simplex-noise'); const simplex = new SimplexNoise('default'); +function getMat(currHeight, maxHeight, numColors) { + const heightIncr = maxHeight / numColors; + for (let i = 0; i < numColors; i += 1) { + if (currHeight <= i * heightIncr) { + return i; + } + } + return numColors - 1; +} + /** * Create a volumetric density field representing the state * of a given region of terrain. @@ -14,7 +24,7 @@ const simplex = new SimplexNoise('default'); * @param {number} scaleHeight - sets the defacto maxHeight of the volume * @return {object} - generated data and metadata */ -function createDensities(cX, cZ, size, downscale = 1000, scaleHeight = 50) { +function createDensities(cX, cY, cZ, size, numColors, downscale = 1000, scaleHeight = 50) { let maxHeight = -1; let minHeight = 10000; let blockCount = 0; @@ -42,20 +52,34 @@ function createDensities(cX, cZ, size, downscale = 1000, scaleHeight = 50) { } } } - // if the calculated max height isn't divisible by - // the volume size granularity, we need to modify it - // to be an increment of size. - if (maxHeight % size !== 0) { - maxHeight = Math.ceil(maxHeight / size) * size; + + if ((minHeight) > (cY + size)) { + return { + blockCount: 0, + data: [], + maxHeight, + minHeight, + }; } - const densities = new Int8Array(size * size * maxHeight).fill(1); + + let maxRelevantHeight = maxHeight; + if (maxHeight > (cY + size)) { + maxRelevantHeight = size; + } else { + maxRelevantHeight = maxHeight - cY; + } + + const densities = new Int8Array(size * size * size).fill(1); let currIdx = -1; for (let i = 0; i < size; i += 1) { - for (let j = 0; j < maxHeight; j += 1) { + for (let j = 0; j < size; j += 1) { for (let k = 0; k < size; k += 1) { - currIdx = i + (j * size) + (k * maxHeight * size); - densities[currIdx] = (j <= heights[i + (k * size)]) ? 1 : 0; - blockCount += densities[currIdx]; + currIdx = i + (j * size) + (k * size * size); + densities[currIdx] = ((j + cY) <= heights[i + (k * size)]) ? 1 : 0; + if (densities[currIdx] !== 0) { + densities[currIdx] = getMat(j + cY, scaleHeight, numColors); + blockCount += 1; + } } } } diff --git a/fractal-terrain-generation/fractal_backend/package-lock.json b/fractal-terrain-generation/fractal_backend/package-lock.json new file mode 100644 index 0000000..7b99ba1 --- /dev/null +++ b/fractal-terrain-generation/fractal_backend/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "fractal_backend", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "simplex-noise": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/simplex-noise/-/simplex-noise-2.4.0.tgz", + "integrity": "sha512-OjyDWm/QZjVbMrPxDVi9b2as+SeNn9EBXlrWVRlFW+TSyWMSXouDryXkQN0vf5YP+QZKobrmkvx1eQYPLtuqfw==" + } + } +} diff --git a/fractal-terrain-generation/fractal_backend/package.json b/fractal-terrain-generation/fractal_backend/package.json new file mode 100644 index 0000000..aa54513 --- /dev/null +++ b/fractal-terrain-generation/fractal_backend/package.json @@ -0,0 +1,14 @@ +{ + "name": "fractal_backend", + "version": "1.0.0", + "description": "Binaris fractal backend function", + "main": "fractal.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Ryland Goldstein", + "license": "ISC", + "dependencies": { + "simplex-noise": "^2.4.0" + } +} diff --git a/fractal-terrain-generation/function_lib/simplify.js b/fractal-terrain-generation/fractal_backend/simplify.js similarity index 92% rename from fractal-terrain-generation/function_lib/simplify.js rename to fractal-terrain-generation/fractal_backend/simplify.js index a1047b1..5d8dd22 100644 --- a/fractal-terrain-generation/function_lib/simplify.js +++ b/fractal-terrain-generation/fractal_backend/simplify.js @@ -8,7 +8,6 @@ const BOTTOM = 5; // Credit to https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/ for // the theory and code structure for this algorithm. - class Vector3 { constructor(x, y, z) { this.x = x; @@ -67,9 +66,10 @@ class BlockFace { * @param {number} ySize - volumes size in Y dimension * @param {number} zSize - volumes size in Z dimension * @param {number} xPos - x coordinate offset of geometry + * @param {number} yPos - y coordinate offset of geometry * @param {number} zPos - z coordinate offset of geometry */ -function simplify(volume, xSize, ySize, zSize, xPos, zPos) { +function simplify(volume, xSize, ySize, zSize, xPos, yPos, zPos, heightFactor, numColors) { const nX = [0, 0, xSize - 1, 0, 0, 0]; const nY = [0, 0, 0, 0, ySize - 1, 0]; @@ -79,8 +79,13 @@ function simplify(volume, xSize, ySize, zSize, xPos, zPos) { const cZ = [1, -1, 0, 0, 0, 0]; const faces = new Array(xSize * ySize * zSize); - for (let i = 0; i < volume.length; i += 1) { - faces[i] = new BlockFace(volume[i]); + for (let x0 = 0; x0 < xSize; x0++) { + for (let y0 = 0; y0 < ySize; y0++) { + for (let z0 = 0; z0 < zSize; z0++) { + const sharedIDX = x0 + (y0 * xSize) + (z0 * xSize * ySize); + faces[sharedIDX] = new BlockFace(volume[sharedIDX]); + } + } } function getBlockData(x, y, z) { @@ -118,6 +123,7 @@ function simplify(volume, xSize, ySize, zSize, xPos, zPos) { const indices = []; let normals = []; const tex = []; + const mats = []; let indexExtend = 0; const x = [0, 0, 0]; @@ -240,20 +246,24 @@ function simplify(volume, xSize, ySize, zSize, xPos, zPos) { verts.push(x[0] + xPos); // v0 - verts.push(x[1]); + verts.push(x[1] + yPos); verts.push(x[2] + zPos); + mats.push(mask[n].type); verts.push(x[0] + du[0] + xPos); // v1 - verts.push(x[1] + du[1]); + verts.push(x[1] + du[1] + yPos); verts.push(x[2] + du[2] + zPos); + mats.push(mask[n].type); verts.push(x[0] + dv[0] + xPos); // v2 - verts.push(x[1] + dv[1]); + verts.push(x[1] + dv[1] + yPos); verts.push(x[2] + dv[2] + zPos); + mats.push(mask[n].type); verts.push(x[0] + du[0] + dv[0] + xPos); // v3 - verts.push(x[1] + du[1] + dv[1]); + verts.push(x[1] + du[1] + dv[1] + yPos); verts.push(x[2] + du[2] + dv[2] + zPos); + mats.push(mask[n].type); tex.push(g); tex.push(0); @@ -341,10 +351,11 @@ function simplify(volume, xSize, ySize, zSize, xPos, zPos) { calcNormals(); return { - verts, - tex, - normals, indices, + mats, + normals, + tex, + verts, }; } diff --git a/fractal-terrain-generation/localServer.js b/fractal-terrain-generation/localServer.js new file mode 100644 index 0000000..fb2924a --- /dev/null +++ b/fractal-terrain-generation/localServer.js @@ -0,0 +1,16 @@ +const express = require('express'); + +const fractal = require('./fractal_backend/fractal'); +const app = express(); + +async function handleReq(req, res) { + if (req && req.query) { + req.query.express_server = true; + res.request = { query: req.query }; + } + return await fractal.handler(req.body, res); +} + +app.get('/v2/*', handleReq); + +app.listen(3000, () => console.log('Fractal server running at localhost:3000/v2/*')); diff --git a/fractal-terrain-generation/package.json b/fractal-terrain-generation/package.json index e973222..f10785c 100644 --- a/fractal-terrain-generation/package.json +++ b/fractal-terrain-generation/package.json @@ -4,21 +4,31 @@ "description": "", "main": "webpack.config.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "webpack-dev-server", "build": "webpack --config webpack.prod.config.js", - "lint": "./node_modules/.bin/eslint -c .eslintrc.js src/ function_lib/" + "deploy": "npm run deploy_backend && npm run deploy_frontend", + "deploy_backend": "cd fractal_backend && bn deploy public_fractal", + "deploy_frontend": "npm run build && cd dist && bn deploy public_servePage", + "install_deps": "npm install && npm run install_frontend && npm run install_backend", + "install_backend": "cd fractal_backend && npm install", + "install_frontend": "cd dist && npm install", + "lint": "./node_modules/.bin/eslint -c .eslintrc.js src/ function_lib/", + "local_server": "forever --watch --watchDirectory ./fractal_backend ./localServer.js", + "start": "webpack-dev-server", + "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "Ryland Goldstein", "license": "MIT", "dependencies": { + "binaris": "^6.0.6", "dat.gui": "^0.7.2", + "dot": "^1.1.2", + "forever": "^0.15.3", + "fs-extra": "^7.0.1", "loglevel": "^1.6.1", "mime-types": "^2.1.21", "mz": "^2.7.0", "path": "^0.12.7", - "simplex-noise": "^2.4.0", "superagent": "^3.8.3", "three": "^0.94.0" }, @@ -26,15 +36,16 @@ "babel-core": "^6.24.1", "babel-loader": "^7.0.0", "babel-plugin-transform-async-to-generator": "^6.24.1", - "babel-polyfill": "^6.26.0", - "babel-preset-es2016": "^6.24.1", "babel-plugin-transform-runtime": "^6.23.0", + "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.7.0", + "babel-preset-es2016": "^6.24.1", "browser-sync": "^2.18.8", "browser-sync-webpack-plugin": "^1.1.4", "eslint": "^3.14.1", "eslint-config-airbnb-base": "^11.0.1", "eslint-plugin-import": "^2.12.0", + "express": "^4.16.4", "glslify-loader": "^1.0.2", "raw-loader": "^0.5.1", "webpack": "^2.5.1", diff --git a/fractal-terrain-generation/src/gen.worker.js b/fractal-terrain-generation/src/gen.worker.js index 0bbfc63..46b1f77 100644 --- a/fractal-terrain-generation/src/gen.worker.js +++ b/fractal-terrain-generation/src/gen.worker.js @@ -25,9 +25,10 @@ const timeMSBetweenRetries = 20; * @param {Number} numRetries - # of invocation attempts before failing explicitly * @param {Number} endpoint - which of the function endpoints to use (if remote gen) */ -async function genData(ID, size, xPos, zPos, downscale, - heightFactor, endpoint, numRetries = 1) { +async function genData(ID, size, xPos, yPos, zPos, + downscale, heightFactor, numTex, endpoint, numRetries = 1) { const scaledX = xPos * size; + const scaledY = yPos * size; const scaledZ = zPos * size; // TODO(Ry): add exponential backoff // For now we simply retry based on the specified number @@ -38,10 +39,12 @@ async function genData(ID, size, xPos, zPos, downscale, const { body, headers } = await request .get(endpoint) .query({ - size, downscale, heightFactor, + numTex, + size, xPos: scaledX, + yPos: scaledY, zPos: scaledZ, }) .responseType('arraybuffer'); @@ -51,6 +54,7 @@ async function genData(ID, size, xPos, zPos, downscale, type: GEN_SUCCESS, size, xPos, + yPos, zPos, heightFactor, maxHeight: headers['x-gen-data-max-height'], @@ -68,6 +72,7 @@ async function genData(ID, size, xPos, zPos, downscale, type: GEN_FAILURE, size, xPos, + yPos, zPos, data: err.message, }); @@ -85,14 +90,16 @@ async function genData(ID, size, xPos, zPos, downscale, */ thisWorker.addEventListener('message', async (e) => { const { + downscale, + endpoint, + heightFactor, ID, + numRetries, + numTex, size, xPos, + yPos, zPos, - endpoint, - numRetries, - downscale, - heightFactor, } = e.data; - genData(ID, size, xPos, zPos, downscale, heightFactor, endpoint, numRetries); + genData(ID, size, xPos, yPos, zPos, downscale, heightFactor, numTex, endpoint, numRetries); }); diff --git a/fractal-terrain-generation/src/main.js b/fractal-terrain-generation/src/main.js index 8d98d2b..5775c39 100644 --- a/fractal-terrain-generation/src/main.js +++ b/fractal-terrain-generation/src/main.js @@ -3,11 +3,15 @@ import * as THREE from 'three'; import * as dat from 'dat.gui'; import * as log from 'loglevel'; +import VertShader from './shaders/simple_shader.vert'; +import FragShader from './shaders/simple_shader.frag'; + import { Game } from './game'; import TileWorld from './worldGen'; import { WorkerPool } from './workerPool'; +import loadTextures from './textureLoader'; -const rootEndpoint = `${process.env.FRACTAL_ENDPOINT}/generate`; +const rootEndpoint = process.env.FRACTAL_ENDPOINT; const defaultTileSize = 4; const defaultTileRadius = 8; @@ -27,6 +31,16 @@ const guiOptions = { const currPos = { x: 0, z: 0 }; +const textureMap = { + lightBlueTex: 'light_blue.png', + redBrownTex: 'brown_red.png', + yellowTex: 'yellow.png', + darkBlueTex: 'dark_blue.png', + orangeTex: 'orange.png', + limeTex: 'lime_green.png', + redTex: 'dark_red.png', +}; + const mats = [ new THREE.MeshNormalMaterial({ side: THREE.FrontSide, @@ -127,7 +141,7 @@ function setupGUIMenus(gui, world, game) { } }); - const numFunctionsEle = gui.add(fullOptions, 'numFunctions').min(1).max(5).step(1); + const numFunctionsEle = gui.add(fullOptions, 'numFunctions').min(1).max(7).step(1); numFunctionsEle.onChange(async (value) => { if (value === world.maxEndpoints) { log.info('value is unchanged'); @@ -140,7 +154,7 @@ function setupGUIMenus(gui, world, game) { // enable/disable wireframe rendering const wireframeEle = gui.add(fullOptions, 'wireframe'); wireframeEle.onChange(() => { - world.updateMaterials(); + world.updateMaterial(); }); // choose from accepted values @@ -179,7 +193,13 @@ function createGUI(gui) { * * @return {object} - the fully configured game instance */ -function setupDemo() { +async function setupDemo() { + const maxTexLoadTries = 5; + const texLoadRetryDelay = 3; + // kick start texture loading to avoid initial delay + const inLoading = loadTextures(textureMap, + maxTexLoadTries, texLoadRetryDelay); + const gui = new dat.gui.GUI(); const savedConfig = createGUI(gui); @@ -188,13 +208,31 @@ function setupDemo() { tileRadius, skyboxColor } = savedConfig; const pool = new WorkerPool(numWorkers, 1); + const texArray = await inLoading; + const uniforms = THREE.UniformsUtils.merge([ + THREE.UniformsLib['lights'], { + textures: { + type: 'tv', + value: texArray, + }, + }, + ]); + + const terrainMaterial = new THREE.ShaderMaterial({ + uniforms, + lights: true, + vertexShader: VertShader, + fragmentShader: FragShader, + }); + // currently a hack which is used to "fake" concurrency const downScale = 80; const heightFactor = (2 ** tileSize) * 0.8; + const maxHeight = 1; - const world = new TileWorld(game, pool, mats, - tileRadius, 2 ** tileSize, downScale, heightFactor, - 0, 0, rootEndpoint, 1000, numFunctions); + const world = new TileWorld(game, pool, terrainMaterial, + texArray.length, tileRadius, maxHeight, 2 ** tileSize, downScale, + heightFactor, 0, 0, rootEndpoint, 1000, numFunctions); // ADD GENS PER SECOND game.setBackgroundColor(new THREE.Color(skyboxColor)); @@ -224,7 +262,7 @@ function setupDemo() { function onDocumentKeyDown(event) { const keyCode = event.which; if (keyCode === 77) { - world.updateMaterials(); + world.updateMaterial(); } if (keyCode === 82) { world.drain(); @@ -237,6 +275,6 @@ function setupDemo() { return game; } -const game = setupDemo(); - -game.animate(); +Promise.resolve(setupDemo()).then((game) => { + game.animate(); +}); diff --git a/fractal-terrain-generation/src/shaders/internal_compute_shader.frag b/fractal-terrain-generation/src/shaders/internal_compute_shader.frag new file mode 100644 index 0000000..473c8dd --- /dev/null +++ b/fractal-terrain-generation/src/shaders/internal_compute_shader.frag @@ -0,0 +1,36 @@ +precision highp float; + +varying vec2 vUv; +varying vec3 vNormal; +varying vec3 vPosition; + +uniform vec3 directionalLightColor[1]; +uniform vec3 directionalLightDirection[1]; +uniform sampler2D textures[7]; + +int whichTex() { + float heightIncr = 50.0 / 7.0; + + for (int i = 0; i < 7; i += 1) { + if (vPosition.y <= float(i) * heightIncr) { + return i; + } + } + return 6; +} + +void main() { + vec4 lights = vec4(0.0, 0.0, 0.0, 1.0); + vec4 ambient = vec4(0.4, 0.4, 0.4, 1.0); + vec3 lightVector = normalize(vPosition - vec3(0, 50, 0)); + lights.rgb += clamp(dot(-vec3(0, -1, 0), vNormal), 0.0, 1.0) * vec3(2,2,2); + + int textureIndex = whichTex(); + + for (int k = 0; k < 7; ++k) { + if (textureIndex == k) { + vec4 lerp = texture2D(textures[k], vUv); + gl_FragColor = (lerp * lights) + (lerp * ambient); + } + } +} diff --git a/fractal-terrain-generation/src/shaders/internal_compute_shader.vert b/fractal-terrain-generation/src/shaders/internal_compute_shader.vert new file mode 100644 index 0000000..256321f --- /dev/null +++ b/fractal-terrain-generation/src/shaders/internal_compute_shader.vert @@ -0,0 +1,15 @@ +varying vec3 worldPosition; + +varying vec2 vUv; +varying vec3 vNormal; +varying vec3 vPosition; +varying float vTextureIdx; + +void main() { + vUv = uv; + vNormal = (modelMatrix * vec4(normal, 0.0)).xyz; + vPosition = position; + vec4 mPosition = modelMatrix * vec4(position, 1.0 ); + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + worldPosition = mPosition.xyz; +} diff --git a/fractal-terrain-generation/src/shaders/shader.frag b/fractal-terrain-generation/src/shaders/shader.frag deleted file mode 100644 index bbca28c..0000000 --- a/fractal-terrain-generation/src/shaders/shader.frag +++ /dev/null @@ -1,9 +0,0 @@ -uniform vec3 topColor; -uniform vec3 bottomColor; -uniform float offset; -uniform float exponent; -varying vec3 worldPosition; -void main() { - float h = normalize( worldPosition + offset ).y; - gl_FragColor = vec4( mix( bottomColor, topColor, max( pow( h, exponent ), 0.0 ) ), 1.0 ); -} diff --git a/fractal-terrain-generation/src/shaders/shader.vert b/fractal-terrain-generation/src/shaders/shader.vert deleted file mode 100644 index b003441..0000000 --- a/fractal-terrain-generation/src/shaders/shader.vert +++ /dev/null @@ -1,6 +0,0 @@ -varying vec3 worldPosition; -void main() { - vec4 mPosition = modelMatrix * vec4( position, 1.0 ); - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - worldPosition = mPosition.xyz; -} diff --git a/fractal-terrain-generation/src/shaders/simple_shader.frag b/fractal-terrain-generation/src/shaders/simple_shader.frag new file mode 100644 index 0000000..6a105b0 --- /dev/null +++ b/fractal-terrain-generation/src/shaders/simple_shader.frag @@ -0,0 +1,25 @@ +precision highp float; + +varying vec2 vUv; +varying vec3 vNormal; +varying vec3 vPosition; +varying float vTextureIdx; + +uniform vec3 directionalLightColor[1]; +uniform vec3 directionalLightDirection[1]; +uniform sampler2D textures[7]; + +void main() { + vec4 lights = vec4(0.0, 0.0, 0.0, 1.0); + vec4 ambient = vec4(0.4, 0.4, 0.4, 1.0); + vec3 lightVector = normalize(vPosition - vec3(0, 50, 0)); + lights.rgb += clamp(dot(-vec3(0, -1, 0), vNormal), 0.0, 1.0) * vec3(2,2,2); + + for (int k = 0; k < 7; ++k) { + if (vTextureIdx - float(k) <= 0.1) { + vec4 lowTex = texture2D(textures[k], vUv); + gl_FragColor = (lowTex * lights) + (lowTex * ambient); + break; + } + } +} diff --git a/fractal-terrain-generation/src/shaders/simple_shader.vert b/fractal-terrain-generation/src/shaders/simple_shader.vert new file mode 100644 index 0000000..09c1066 --- /dev/null +++ b/fractal-terrain-generation/src/shaders/simple_shader.vert @@ -0,0 +1,18 @@ +attribute float textureIdx; + +varying vec3 worldPosition; + +varying vec2 vUv; +varying vec3 vNormal; +varying vec3 vPosition; +varying float vTextureIdx; + +void main() { + vTextureIdx = textureIdx; + vUv = uv; + vNormal = (modelMatrix * vec4(normal, 0.0)).xyz; + vPosition = position; + vec4 mPosition = modelMatrix * vec4(position, 1.0 ); + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + worldPosition = position; +} diff --git a/fractal-terrain-generation/src/textureLoader.js b/fractal-terrain-generation/src/textureLoader.js new file mode 100644 index 0000000..c3ea67d --- /dev/null +++ b/fractal-terrain-generation/src/textureLoader.js @@ -0,0 +1,49 @@ +import * as THREE from 'three'; +import * as log from 'loglevel'; + +import msleep from './msleep'; + +const loader = new THREE.TextureLoader(); + +const modifiedRemoteEndpoint = process.env.FRACTAL_ENDPOINT.replace('fractal', 'servePage'); +const resEndpoint = `${process.env.FRACTAL_RESOURCE_ENDPOINT || modifiedRemoteEndpoint}/resources`; + +/** + * Needed as of now because of our rate limiting. + */ +async function loadUntilSuccess(texURL, maxRetries, retryDelay) { + for (let i = 0; i < maxRetries; i += 1) { + try { + return await (new Promise((resolve, reject) => { + function onLoad(tex) { + return resolve(tex); + } + function onError(err) { + return reject(err); + } + loader.load(texURL, onLoad, undefined, onError); + })); + } catch (err) { + log.debug(`err loading texture ${texURL}, ${err}`); + await msleep(retryDelay); + } + } + throw new Error(`Unable to load texture ${texURL}`); +} + +module.exports = async function loadTextures(texMap, maxRetries, retryDelay) { + const texArray = []; + const textureKeys = Object.keys(texMap); + for (let i = 0; i < textureKeys.length; i += 1) { + const texKey = textureKeys[i]; + const texURL = `${resEndpoint}/${texMap[texKey]}`; + texArray.push(await loadUntilSuccess(texURL, maxRetries)); + } + + texArray.forEach((tex) => { + tex.wrapS = tex.wrapT = THREE.RepeatWrapping; + tex.repeat.set(2, 2); + }); + + return texArray; +} diff --git a/fractal-terrain-generation/src/tile.js b/fractal-terrain-generation/src/tile.js index b9dc3ab..a114169 100644 --- a/fractal-terrain-generation/src/tile.js +++ b/fractal-terrain-generation/src/tile.js @@ -1,8 +1,9 @@ class Tile { - constructor(xPos, zPos) { + constructor(xPos, yPos, zPos) { this.xPos = xPos; + this.yPos = yPos; this.zPos = zPos; - this.key = `${this.xPos},${this.zPos}`; + this.key = `${this.xPos},${this.yPos},${this.zPos}`; this.mesh = undefined; this.stale = false; this.generated = false; @@ -22,6 +23,7 @@ class Tile { if (verbose) { const tileData = JSON.stringify({ x: this.xPos, + y: this.yPos, z: this.zPos, hasMesh: this.mesh !== undefined, generating: this.generating, @@ -29,12 +31,12 @@ class Tile { }); return `Tile info ${tileData}`; } - return `Tile @ x=${this.xPos} z=${this.zPos}`; + return `Tile @ x=${this.xPos} y=${this.yPos} z=${this.zPos}`; } } -function tileKey(x, z) { - return `${x},${z}`; +function tileKey(x, y, z) { + return `${x},${y},${z}`; } // TODO(Ry): change tileKey method of usage diff --git a/fractal-terrain-generation/src/workerPool.js b/fractal-terrain-generation/src/workerPool.js index 240538f..ec451c2 100644 --- a/fractal-terrain-generation/src/workerPool.js +++ b/fractal-terrain-generation/src/workerPool.js @@ -94,7 +94,7 @@ class WorkerPool { this.workers.forEach(worker => (worker.onmessage = msgEventFunction)); } - changeNumWorkers(numWorkers) { + async changeNumWorkers(numWorkers) { if (this.adjusting) return false; if (numWorkers === this.numWorkers) return true; @@ -108,6 +108,8 @@ class WorkerPool { addedWorker.onmessage = this.onMessage; this.workers.push(addedWorker); this.workersAvailable.push(0); + // TODO(Ry): remove this once concurrent loading is fixed:w + await msleep(400); } this.numWorkers = numWorkers; this.adjusting = false; diff --git a/fractal-terrain-generation/src/worldGen.js b/fractal-terrain-generation/src/worldGen.js index bdc4cbe..286b89b 100644 --- a/fractal-terrain-generation/src/worldGen.js +++ b/fractal-terrain-generation/src/worldGen.js @@ -8,15 +8,15 @@ import msleep from './msleep'; import { GEN_SUCCESS } from './sharedTypes'; - // Allow for buffers longer than their type can express -function createGeomFromBuffer(rawData, xPos, zPos, sizeScalar) { +function createGeomFromBuffer(rawData, xPos, yPos, zPos, sizeScalar) { log.debug(`generating geom from buffer @pos x "${xPos}" z "${zPos}"`); const buffGeom = new THREE.BufferGeometry(); // eslint-disable-next-line no-bitwise const numVerts = rawData[0] + (rawData[1] << 16); // eslint-disable-next-line no-bitwise const numTex = rawData[2] + (rawData[3] << 16); + const numMats = numVerts / 3; log.trace(`#verts "${numVerts}" #tex "${numTex}"`); const bytesPerEle = 2; const initOffset = bytesPerEle * 4; @@ -24,29 +24,32 @@ function createGeomFromBuffer(rawData, xPos, zPos, sizeScalar) { const vertView = new Int16Array(rawData.buffer, initOffset, numVerts); const normView = new Int16Array(rawData.buffer, initOffset + vertOff, numVerts); const texView = new Int16Array(rawData.buffer, initOffset + (2 * vertOff), numTex); + const matsView = new Uint16Array(rawData.buffer, initOffset + (2 * vertOff) + (bytesPerEle * numTex), numMats); const indexView = new Uint16Array(rawData.buffer, - initOffset + (2 * vertOff) + (bytesPerEle * numTex)); + initOffset + (2 * vertOff) + (bytesPerEle * numTex) + (numMats * bytesPerEle)); buffGeom.addAttribute('position', new THREE.Int16BufferAttribute(vertView, 3)); buffGeom.addAttribute('normal', new THREE.Float32BufferAttribute(normView, 3, true)); // TODO(Ry): Get UV coordinates working and enable this buffGeom.addAttribute('uv', new THREE.Int16BufferAttribute(texView, 2)); + buffGeom.addAttribute('textureIdx', new THREE.Int16BufferAttribute(matsView, 1)); buffGeom.setIndex(new THREE.Uint32BufferAttribute(indexView, 1)); buffGeom.scale(sizeScalar, sizeScalar, sizeScalar); return buffGeom; } class TileWorld { - constructor(game, workerPool, materials, radius, - tileSize, downScale, heightFactor, startX, + constructor(game, workerPool, material, numTex, radius, + maxHeight, tileSize, downScale, heightFactor, startX, startZ, rootEndpoint, maxGeomGen = 1000, maxEndpoints = 1) { this.game = game; this.workerPool = workerPool; - this.materials = materials; - this.currMaterial = 0; + this.material = material; + this.numTex = numTex; this.tileMap = {}; this.currX = startX; this.currZ = startZ; + this.maxHeight = maxHeight; this.radius = radius; this.tileSize = tileSize; this.downScale = downScale; @@ -82,9 +85,9 @@ class TileWorld { */ this.workerPool.setWorkersMessageEvent((e) => { this.workerPool.releaseWorker(new WorkerHandle(e.data.ID)); - const currTile = this.tileMap[tileKey(e.data.xPos, e.data.zPos)]; + const currTile = this.tileMap[tileKey(e.data.xPos, e.data.yPos, e.data.zPos)]; if (!currTile) { - log.warn(`tile ${tileKey(e.data.xPos, e.data.zPos)} not valid`); + log.warn(`tile ${tileKey(e.data.xPos, e.data.yPos, e.data.zPos)} not valid`); } if (e.data.type === GEN_SUCCESS) { const timeToGen = performance.now() - currTile.genTime; @@ -94,15 +97,15 @@ class TileWorld { this.runningTime += timeToGen; this.totalGenTime += timeToGen; this.totalGens += 1; - log.trace(`finished generation for tile at x "${e.data.xPos}" z "${e.data.zPos}"`); + log.trace(`finished generation for tile at x "${e.data.xPos}" y "${e.data.yPos}" z "${e.data.zPos}"`); // now that our worker has finished its task we can pass the raw // geometry data to the GPU for rendering this.processRawGeom(currTile, e.data); } else { - const { xPos, zPos, data } = e.data; + const { xPos, yPos, zPos, data } = e.data; this.inGen -= 1; currTile.generating = false; - log.error(`Tile at x "${xPos}" z "${zPos}" failed to generate`); + log.error(`Tile at x "${xPos}" y "${yPos}" z "${zPos}" failed to generate`); log.error(data); } }); @@ -120,7 +123,7 @@ class TileWorld { const endpoint = this.currEndpoint % this.maxEndpoints; const endpointString = endpoint > 0 ? `${endpoint - 1}` : ''; this.currEndpoint += 1; - return `${this.rootEndpoint}${endpointString}`; + return `${this.rootEndpoint}${endpointString}/generate`; } /** @@ -200,13 +203,15 @@ class TileWorld { let newZ; let newLoc; for (let i = -this.radius; i < this.radius; i += 1) { - for (let j = -this.radius; j < this.radius; j += 1) { - newX = i + this.currX; - newZ = j + this.currZ; - newLoc = tileKey(newX, newZ); - if (!this.tileMap[newLoc]) { - this.tileMap[newLoc] = new Tile(newX, newZ); - tilesToGen.push(this.tileMap[newLoc]); + for (let j = 0; j < this.maxHeight; j += 1) { + for (let k = -this.radius; k < this.radius; k += 1) { + newX = i + this.currX; + newZ = k + this.currZ; + newLoc = tileKey(newX, j, newZ); + if (!this.tileMap[newLoc]) { + this.tileMap[newLoc] = new Tile(newX, j, newZ); + tilesToGen.push(this.tileMap[newLoc]); + } } } } @@ -214,7 +219,7 @@ class TileWorld { tilesToGen.forEach(async (tile) => { this.inGen += 1; tile.generating = true; - this.workerGenTile(tile, this.getAndIncrementEndpoint()); + this.workerGenTile(tile); }); this.updating = false; } @@ -225,7 +230,7 @@ class TileWorld { * @param {object} tile - tile instance to generate data for * @param {number} endpoint - numerical endpoint to use for generation */ - async workerGenTile(tile, endpoint) { + async workerGenTile(tile) { log.trace(`Passing tile gen task to worker using endpoint ${endpoint} for ${tile.describe()}`); const handle = await this.workerPool.getAvailableWorker(); // if we weren't able to get a valid worker or the world is @@ -235,21 +240,29 @@ class TileWorld { tile.generating = false; return; } - + const endpoint = this.getAndIncrementEndpoint() tile.genTime = performance.now(); handle.worker.postMessage({ ID: handle.ID, size: this.tileSize, downscale: this.downScale, - heightFactor: this.heightFactor, + heightFactor: (this.maxHeight * this.tileSize), numRetries: 5, + numTex: this.numTex, xPos: tile.xPos, + yPos: tile.yPos, zPos: tile.zPos, endpoint, }); } async processRawGeom(tile, workerData) { + if (parseInt(workerData.blockCount, 10) === 0) { + tile.generated = true; + tile.generating = false; + this.inGen -= 1; + return; + } // since this may be one of the few operations // on the main thread, care needs to be taken // so we avoid blocking @@ -258,9 +271,9 @@ class TileWorld { } this.inGeomGen += 1; const tileGeom = createGeomFromBuffer(workerData.data, - tile.xPos, tile.zPos, this.sizeScalar); + tile.xPos, tile.yPos, tile.zPos, this.sizeScalar); if (!tile.stale) { - const tileMesh = new THREE.Mesh(tileGeom, this.getCurrentMaterial()); + const tileMesh = new THREE.Mesh(tileGeom, this.material); tileMesh.name = tile.key; log.trace(`adding mesh ${tileMesh.name} to scene`); this.game.addMesh(tileMesh); @@ -272,21 +285,8 @@ class TileWorld { tile.generating = false; } - getCurrentMaterial() { - const mat = this.materials[this.currMaterial % this.materials.length]; - return mat; - } - - updateMaterials() { - this.currMaterial += 1; - const mat = this.materials[this.currMaterial % this.materials.length]; - Object.keys(this.tileMap).forEach((keyForTile) => { - const tileObj = this.game.getObject(keyForTile); - if (tileObj) { - tileObj.material = mat; - tileObj.material.needsUpdate = true; - } - }); + updateMaterial() { + this.material.wireframe = !this.material.wireframe; } avgGenTime() { diff --git a/fractal-terrain-generation/webpack.config.js b/fractal-terrain-generation/webpack.config.js index ef3de68..616b9ab 100644 --- a/fractal-terrain-generation/webpack.config.js +++ b/fractal-terrain-generation/webpack.config.js @@ -1,12 +1,19 @@ const webpack = require('webpack'); const path = require('path'); const BrowserSyncPlugin = require('browser-sync-webpack-plugin'); +const fs = require('mz/fs'); +const dot = require('dot'); -module.exports = { +const { prebuild } = require('./builder'); +const localResourceEndpoint = 'http://localhost:3001'; + +module.exports = async () => { + const { FRACTAL_ENDPOINT, PUBLIC_PATH, BINARIS_ACCOUNT_ID } = await prebuild(); + return { entry: [ 'babel-polyfill', './src/gen.worker.js', './src/main.js' ], output: { path: path.resolve(__dirname, 'dist/js'), - publicPath: `/v2/run/${process.env.BINARIS_ACCOUNT_NUMBER}/public_servePage/js/`, + publicPath: `${PUBLIC_PATH}/js`, filename: 'three.bundle.js' }, module: { @@ -24,8 +31,7 @@ module.exports = { options: { presets: ['env'] } - } - }, + } }, { test: /\.(glsl|frag|vert)$/, loader: 'raw-loader', @@ -50,7 +56,7 @@ module.exports = { proxy: 'http://localhost:8080/', files: [ { - match: ['**/*.html'], + match: ['**/*.html*'], fn: event => { if (event === 'change') { const bs = require('browser-sync').get( @@ -70,15 +76,25 @@ module.exports = { { 'process.env': { - 'FRACTAL_ENDPOINT': JSON.stringify(process.env.FRACTAL_ENDPOINT), + FRACTAL_ENDPOINT: JSON.stringify(FRACTAL_ENDPOINT), + FRACTAL_RESOURCE_ENDPOINT: JSON.stringify(localResourceEndpoint), } } ), ], devServer: { - contentBase: path.resolve(__dirname, 'dist'), - publicPath: `/v2/run/${process.env.BINARIS_ACCOUNT_NUMBER}/public_servePage/js/`, + before: function(app, server) { + app.get(['/', '/index.html'], async (req, res) => { + const file = await fs.readFile(__dirname + '/dist/index.html.dot', 'utf8'); + const template = dot.template(file); + res.set('content-type', 'text/html'); + res.send(template({ binarisAccountId: BINARIS_ACCOUNT_ID })); + }); + }, + contentBase: [path.resolve(__dirname, 'dist'), path.resolve(__dirname, 'dist/resources')], + publicPath: `${PUBLIC_PATH}/js`, }, watch: true, devtool: 'cheap-eval-source-map' + }; } diff --git a/fractal-terrain-generation/webpack.prod.config.js b/fractal-terrain-generation/webpack.prod.config.js index 8ec3d9b..89f5fc2 100644 --- a/fractal-terrain-generation/webpack.prod.config.js +++ b/fractal-terrain-generation/webpack.prod.config.js @@ -2,11 +2,15 @@ const webpack = require('webpack'); const path = require('path'); const BrowserSyncPlugin = require('browser-sync-webpack-plugin'); -module.exports = { +const { prebuild } = require('./builder'); + +module.exports = async () => { + const { FRACTAL_ENDPOINT, PUBLIC_PATH } = await prebuild(); + return { entry: [ 'babel-polyfill', './src/gen.worker.js', './src/main.js' ], output: { path: path.resolve(__dirname, 'dist/js'), - publicPath: `/v2/run/${process.env.BINARIS_ACCOUNT_NUMBER}/public_servePage/js/`, + publicPath: `${PUBLIC_PATH}/js/`, filename: 'three.bundle.js' }, module: { @@ -47,9 +51,11 @@ module.exports = { { 'process.env': { - 'FRACTAL_ENDPOINT': JSON.stringify(process.env.FRACTAL_ENDPOINT), + FRACTAL_ENDPOINT: JSON.stringify(FRACTAL_ENDPOINT), } } ), ], + }; } +