diff --git a/index.html b/index.html new file mode 100644 index 0000000..3ad9605 --- /dev/null +++ b/index.html @@ -0,0 +1,44 @@ + + + Texture viewer + + + +
+ Heretic | + Doom | + Doom II +
+
+
+ + + + diff --git a/texture.js b/texture.js new file mode 100644 index 0000000..70bed2d --- /dev/null +++ b/texture.js @@ -0,0 +1,217 @@ + +EMPTY_RE = /^\s*$/; +COMMENT_RE = /^\s*;/; +TEXTURE_RE = /^\s*([A-Za-z0-9_-]+)\s*(-?[0-9]+)\s*(-?[0-9]+)/; +PATCH_RE = /^\s*\*\s*([A-Za-z0-9_-]+)\s*(-?[0-9]+)\s*(-?[0-9]+)/; + +class PatchesStore { + constructor(rootPath, extension) { + this.patches = {}; + this.rootPath = rootPath; + this.extension = extension; + this.waitingLoad = 0; + + // Make a container for holding patch images + this.container = document.createElement("div"); + this.container.style.display = "none"; + document.body.appendChild(this.container); + } + + loadPatch(name) { + name = name.toLowerCase(); + if (name in this.patches) { + return; + } + var filename = this.rootPath + "/" + name + this.extension; + var img = document.createElement("img"); + img.setAttribute("src", filename); + this.waitingLoad++; + var ps = this; + img.onload = function() { + console.log("Patch loaded: " + name); + ps.patchLoaded(); + } + this.container.append(img); + this.patches[name] = img; + } + + getPatch(name) { + name = name.toLowerCase(); + if (name in this.patches) { + return this.patches[name]; + } + throw "Patch not in loaded set: " + name; + } + + patchLoaded() { + this.waitingLoad--; + if (this.waitingLoad == 0 && this.onload != null) { + this.onload(); + } + } + + loadAllPatches(textures, callback) { + var ps = this; + ps.onload = function() { + ps.onload = null; + callback(); + } + var i, j; + for (i = 0; i < textures.length; ++i) { + var tx = textures[i]; + for (j = 0; j < tx.patches.length; ++j) { + this.loadPatch(tx.patches[j].name); + } + } + } +} + +class Patch { + constructor(name, x, y) { + this.name = name; + this.x = x; + this.y = y; + } + + toString() { + return "* " + this.name + " " + this.x + " " + this.y; + } +} + +class Texture { + constructor(name, width, height) { + this.name = name; + this.width = width; + this.height = height; + this.patches = []; + } + + addPatch(p) { + this.patches.push(p); + } + + toString() { + var hdr = this.name + " " + this.width + " " + this.height; + return hdr + "\n" + this.patches.join("\n"); + } + + drawTexture(ps) { + var canvas = document.createElement("canvas"); + canvas.setAttribute("width", this.width); + canvas.setAttribute("height", this.height); + var ctx = canvas.getContext("2d"); + var i; + for (i = 0; i < this.patches.length; ++i) { + var patch = this.patches[i]; + var img = ps.getPatch(patch.name); + ctx.drawImage(img, patch.x, patch.y); + } + return canvas; + } +} + +function parseTextures(config) { + var lines = config.split("\n"); + var i; + var textures = [], tx = null; + for (i = 0; i < lines.length; ++i) { + var line = lines[i]; + console.log(line); + if (COMMENT_RE.exec(line)) { + continue; + } + var m = TEXTURE_RE.exec(line); + if (m) { + var w = parseInt(m[2]); + var h = parseInt(m[3]); + tx = new Texture(m[1], w, h); + textures.push(tx); + continue; + } + var m = PATCH_RE.exec(line); + if (m) { + var x = parseInt(m[2]); + var y = parseInt(m[3]); + tx.addPatch(new Patch(m[1], x, y)); + continue; + } + if (!EMPTY_RE.exec(line)) { + throw "Parse error in texture file: " + line; + } + } + return textures; +} + +function loadTexturesFile(url, callback) { + console.log("load textures from " + url); + var url; + var xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.onreadystatechange = function() { + console.log("response loading " + xhr.responseURL + ": " + + xhr.status); + if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) { + var textures = parseTextures(xhr.responseText); + console.log("loaded " + textures.length + + " textures from file: " + xhr.responseURL); + callback(textures); + } + } + xhr.send(); +} + +function loadAllTextures(urls, callback) { + var waitingTextures = urls.length; + var allTextures = []; + var i; + for (i = 0; i < urls.length; ++i) { + loadTexturesFile(urls[i], function(textures) { + allTextures = allTextures.concat(textures); + --waitingTextures; + if (waitingTextures == 0) { + callback(allTextures); + } + }); + } +} + +function renderAllTextures(element, game) { + var ps = new PatchesStore(game + "/patches", ".png"); + urls = [game + "/textures/texture1.txt"]; + if (game != "doom2") { + urls.push(game + "/textures/texture2.txt"); + } + loadAllTextures(urls, function(textures) { + ps.loadAllPatches(textures, function() { + element.innerHTML = ""; + var i; + for (i = 0; i < textures.length; ++i) { + var tx = textures[i]; + var canvas = tx.drawTexture(ps); + //element.appendChild(document.createTextNode(tx.name)); + element.appendChild(canvas); + //element.appendChild(document.createElement("br")); + } + }); + }); +} + +function getGame() { + var u = new URL(window.location.href); + var game = u.searchParams.get("game"); + if (game == null) { + return "doom1"; + } + return game; +} + +window.onload = function() { + console.log("page loaded"); + var el = document.getElementById("texture-list"); + el.innerHTML = "Loading..."; + document.body.appendChild(el); + var game = getGame(); + console.log("rendering for " + game); + renderAllTextures(el, game); +} +