You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
6.3 KiB
JavaScript
227 lines
6.3 KiB
JavaScript
import {HeadRenderer} from "./HeadRenderer";
|
|
import * as THREE from "three";
|
|
import {ShapeCollection} from "./ShapeCollection";
|
|
import {uuidv4} from "./util";
|
|
|
|
const headRenderer = window.headRenderer = new HeadRenderer();
|
|
|
|
const part1 = document.querySelector("#part_1");
|
|
let shape1 = null;
|
|
const part2 = document.querySelector("#part_2");
|
|
let shape2 = null;
|
|
|
|
const race = document.querySelector("#race");
|
|
const gender = document.querySelector("#gender");
|
|
|
|
const prevHair = document.querySelector('#prev_hair');
|
|
const nextHair = document.querySelector('#next_hair');
|
|
|
|
const prevHead = document.querySelector('#prev_head');
|
|
const nextHead = document.querySelector('#next_head');
|
|
|
|
const RACES = {
|
|
'darkelf': 'Dark Elf',
|
|
'khajiit': 'Khajiit',
|
|
'woodelf': 'Wood Elf',
|
|
'highelf': 'High Elf',
|
|
'redguard': 'Redguard',
|
|
'breton': 'Breton',
|
|
'argonian': 'Argonian',
|
|
'orc': 'Orc',
|
|
'imperial': 'Imperial',
|
|
'nord': 'Nord',
|
|
};
|
|
|
|
window.addEventListener("load", async () => {
|
|
const allMeshes = await (await fetch("blob/meshes.json?v=2")).json();
|
|
|
|
part1.innerHTML = "";
|
|
part2.innerHTML = "";
|
|
const indexed = window.indexed = {};
|
|
const byName = window.byName = {};
|
|
const tree = window.tree = {};
|
|
|
|
headRenderer.init();
|
|
headRenderer.expose();
|
|
|
|
for (let mesh of allMeshes) {
|
|
let [_, __, race = "object", gender = "X", part = "head", nr = undefined] = mesh.name.split('_');
|
|
race = race.replace(new RegExp("\\s+"), '');
|
|
race = RACES[race] || 'Object';
|
|
|
|
if (nr === undefined && !['head', 'hair'].includes(part.toLowerCase())) {
|
|
[_, part = "head", nr] = part.match(/(head|hair)(\d+)?/i) || [];
|
|
}
|
|
|
|
gender = gender.toUpperCase();
|
|
|
|
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);
|
|
}
|
|
|
|
if (mesh.name.toLowerCase().indexOf("head") !== -1) {
|
|
part2.appendChild(option);
|
|
}
|
|
|
|
if (!tree[race]) {
|
|
tree[race] = {};
|
|
}
|
|
|
|
if (!tree[race][gender]) {
|
|
tree[race][gender] = {'head': [], 'hair': []}
|
|
}
|
|
|
|
tree[race][gender][part.toLowerCase()].push(mesh.id);
|
|
}
|
|
|
|
race.innerHTML = "";
|
|
for (let raceX in tree) {
|
|
let option = document.createElement("option");
|
|
option.textContent = raceX;
|
|
option.value = raceX;
|
|
race.appendChild(option);
|
|
}
|
|
|
|
race.addEventListener('change', () => {
|
|
setRace(race.value);
|
|
});
|
|
|
|
function setRace(name, propagate = true) {
|
|
race.value = name;
|
|
gender.innerHTML = "";
|
|
for (let genderX in tree[name]) {
|
|
let option = document.createElement("option");
|
|
option.textContent = genderX;
|
|
option.value = genderX;
|
|
gender.appendChild(option);
|
|
}
|
|
|
|
if (propagate) {
|
|
setGender(gender.value)
|
|
}
|
|
}
|
|
|
|
gender.addEventListener("change", () => {
|
|
setGender(gender.value);
|
|
});
|
|
|
|
function setGender(name) {
|
|
gender.value = name;
|
|
setHair(tree[race.value][name]['hair'][0]);
|
|
setHead(tree[race.value][name]['head'][0]);
|
|
}
|
|
|
|
// 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.toLowerCase();
|
|
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/texture/");
|
|
}
|
|
|
|
function updateHash() {
|
|
location.hash = `head=${encodeURIComponent(indexed[part2.value].name)}&hair=${encodeURIComponent(indexed[part1.value].name)}`;
|
|
}
|
|
|
|
function setHair(id) {
|
|
if (!id) return;
|
|
|
|
part1.value = id;
|
|
if (shape1 !== null) {
|
|
headRenderer.remove(shape1);
|
|
}
|
|
headRenderer.add(shape1 = getShape(id));
|
|
updateHash();
|
|
}
|
|
|
|
part1.addEventListener("change", () => {
|
|
setHair(part1.value);
|
|
});
|
|
|
|
prevHair.addEventListener('click', () => {
|
|
let hairList = tree[race.value][gender.value]['hair'];
|
|
setHair(hairList[(hairList.length + (hairList.indexOf(part1.value) - 1)) % hairList.length]);
|
|
});
|
|
|
|
nextHair.addEventListener('click', () => {
|
|
let hairList = tree[race.value][gender.value]['hair'];
|
|
setHair(hairList[(hairList.indexOf(part1.value) + 1) % hairList.length]);
|
|
});
|
|
|
|
function setHead(id) {
|
|
if (!id) return;
|
|
|
|
part2.value = id;
|
|
if (shape2 !== null) {
|
|
headRenderer.remove(shape2);
|
|
}
|
|
headRenderer.add(shape2 = getShape(id));
|
|
updateHash();
|
|
}
|
|
|
|
part2.addEventListener("change", () => {
|
|
setHead(part2.value);
|
|
});
|
|
|
|
|
|
prevHead.addEventListener('click', () => {
|
|
let headList = tree[race.value][gender.value]['head'];
|
|
setHead(headList[(headList.length + (headList.indexOf(part2.value) - 1)) % headList.length]);
|
|
});
|
|
|
|
nextHead.addEventListener('click', () => {
|
|
let headList = tree[race.value][gender.value]['head'];
|
|
setHead(headList[(headList.indexOf(part2.value) + 1) % headList.length]);
|
|
});
|
|
|
|
headRenderer.draw();
|
|
});
|
|
|
|
window.THREE = THREE; |