diff --git a/public/index.html b/public/index.html
index c85a323..f32da9e 100644
--- a/public/index.html
+++ b/public/index.html
@@ -18,13 +18,29 @@
.wizard {
position: absolute;
- padding: 25px;
- width: 100%;
- bottom: 0;
+ width: calc(100% - 50px);
+ left: 25px;
+ bottom: 25px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
+ background: rgba(255, 255, 255, 0.2);
+ }
+
+ @media (max-width:600px) {
+ body {
+ font-size: 1.3em;
+ }
+
+ input, select {
+ font-size: 1em;
+ }
+
+ button {
+ width: 5em;
+ height: 3em;
+ }
}
diff --git a/src/HeadRenderer.js b/src/HeadRenderer.js
index 904f7e5..3365ad2 100644
--- a/src/HeadRenderer.js
+++ b/src/HeadRenderer.js
@@ -17,8 +17,8 @@ export class HeadRenderer {
this.renderer.setSize(W, H);
document.body.appendChild(this.renderer.domElement);
- this.camera = new THREE.PerspectiveCamera(70, W / H, 1, 2000);
- this.camera.position.z = 600;
+ this.camera = new THREE.PerspectiveCamera(70, W / H, 1, 200);
+ this.camera.position.z = 60;
this.scene = new THREE.Scene();
this.scene.background = new Color(0xffffff);
diff --git a/src/Shape.js b/src/Shape.js
index df4498b..0e5a1c8 100644
--- a/src/Shape.js
+++ b/src/Shape.js
@@ -48,7 +48,7 @@ export class Shape {
const params = {
color: 0xffffff,
transparent: true,
- alphaTest: 0.5,
+ alphaTest: 0.2,
};
if (this.textureInfo) {
let Loader;
@@ -129,7 +129,7 @@ export class Shape {
geometry.translate(this.translation[0], this.translation[1], this.translation[2]);
// Scale up a bit
- geometry.scale(20, 20, 20);
+ geometry.scale(2, 2, 2);
// Rotate the face so it faces us \o/
geometry.rotateX(290 * (Math.PI / 180));
diff --git a/src/ShapeCollection.js b/src/ShapeCollection.js
index b3fbac9..83a959e 100644
--- a/src/ShapeCollection.js
+++ b/src/ShapeCollection.js
@@ -23,4 +23,6 @@ export class ShapeCollection {
return this.group
}
-}
\ No newline at end of file
+}
+
+window.ShapeCollection = ShapeCollection;
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index d5d6f3f..9ecc01a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -25,7 +25,8 @@ window.addEventListener("load", async () => {
part1.innerHTML = "";
part2.innerHTML = "";
- const indexed = {};
+ const indexed = window.indexed = {};
+ const byName = window.byName = {};
const tree = {};
const shapeCache = {};
@@ -33,30 +34,22 @@ window.addEventListener("load", async () => {
headRenderer.expose();
for (let mesh of allMeshes) {
- let [_, x, race, gender, part, nr = undefined] = mesh.name.split('_');
+ let [_, __, race, gender, part, nr = undefined] = mesh.name.split('_');
race = race.replace(new RegExp("\\s+"), ' ');
- if (race.toLowerCase() === "khajiit") {
- console.log(part, mesh.name, nr);
- }
- if (x !== 'N') {
- continue;
+ if (race === 'DarkElf') {
+ race = 'Dark Elf'
}
- if (nr === undefined) {
+ if (nr === undefined && !['head', 'hair'].includes(part.toLowerCase())) {
[_, part, nr] = part.match(/(head|hair)(\d+)?/i) || [];
- console.log(part);
-
- if (!nr) {
- continue;
- }
}
let option = document.createElement("option");
option.textContent = mesh.name;
option.value = mesh.id = uuidv4();
indexed[mesh.id] = mesh;
-
+ byName[mesh.name] = mesh.id;
if (mesh.name.toLowerCase().indexOf("hair") !== -1) {
part1.appendChild(option);
@@ -89,7 +82,7 @@ window.addEventListener("load", async () => {
setRace(race.value);
});
- function setRace(name) {
+ function setRace(name, propagate = true) {
race.value = name;
gender.innerHTML = "";
for (let genderX in tree[name]) {
@@ -99,7 +92,9 @@ window.addEventListener("load", async () => {
gender.appendChild(option);
}
- setGender(gender.value)
+ if (propagate) {
+ setGender(gender.value)
+ }
}
gender.addEventListener("change", () => {
@@ -112,19 +107,66 @@ window.addEventListener("load", async () => {
setHead(tree[race.value][name]['head'][0]);
}
- setRace("Dark Elf");
+ // Collect hash query before setting defaults
+ let q = collectHashQuery();
+
+ // Dark Elf / M is what Morrowind starts you with
+ setRace("Dark Elf", false);
setGender("M");
+ updateFromHash(q);
+
+ function collectHashQuery() {
+ return location.hash.substr(1).split('&').reduce((c, x) => {
+ let [name, value = ""] = x.split('=', 2).map(y => decodeURIComponent(y));
+ c[name] = value;
+ return c;
+ }, {});
+ }
+
+ function updateFromHash(q) {
+ if (!q) {
+ q = collectHashQuery();
+ }
+
+ if (byName[q['head']] && q['head'].toLowerCase().includes('head')) {
+ setHead(byName[q['head']]);
+ } else {
+ if (q['head']) {
+ console.log("No head found with name: ", q['head']);
+ }
+ }
+
+ if (byName[q['hair']] && q['hair'].toLowerCase().includes('hair')) {
+ setHair(byName[q['hair']]);
+ } else {
+ if (q['hair']) {
+ console.log("No hair found with name: ", q['hair']);
+ }
+ }
+ }
+
+ updateFromHash();
+
+ window.addEventListener('hashchange', () => {
+ updateFromHash();
+ });
+
function getShape(name) {
return new ShapeCollection(indexed[name], "blob/textures/");
}
+ function updateHash() {
+ location.hash = `head=${encodeURIComponent(indexed[part2.value].name)}&hair=${encodeURIComponent(indexed[part1.value].name)}`;
+ }
+
function setHair(id) {
part1.value = id;
if (shape1 !== null) {
headRenderer.remove(shape1);
}
headRenderer.add(shape1 = getShape(id));
+ updateHash();
}
part1.addEventListener("change", () => {
@@ -147,6 +189,7 @@ window.addEventListener("load", async () => {
headRenderer.remove(shape2);
}
headRenderer.add(shape2 = getShape(id));
+ updateHash();
}
part2.addEventListener("change", () => {