"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Prefab = exports.parseRotation = exports.Model = exports.Material = void 0;
const fs = require("fs");
const gl_matrix_1 = require("gl-matrix");
const math_1 = require("./math");
const utils_1 = require("./utils");
const importModels = () => {
    const modelDirns = `${utils_1.appDir}app/assets/models`;
    const modelDir = `${modelDirns}/`;
    const files = fs.readdirSync(modelDirns);
    const mtlFiles = new Map();
    const objFiles = [];
    if (!files) {
        console.error('Falsy model files:', files);
    }
    for (let i = 0; i < files.length; i++) {
        const f = files[i];
        if (f.endsWith('.obj')) {
            objFiles.push(fs.readFileSync(modelDir + f, 'utf8'));
        }
        else if (f.endsWith('.mtl')) {
            mtlFiles.set(f, fs.readFileSync(modelDir + f, 'utf8'));
        }
    }
    return { mtlFiles, objFiles };
};
const { mtlFiles, objFiles } = importModels();
const materials = new Map();
class Material {
    constructor() {
        this.ambientColor = gl_matrix_1.vec4.fromValues(1, 1, 1, 1);
        this.diffuseColor = gl_matrix_1.vec4.fromValues(0, 0, 0, 1);
        this.specularColor = gl_matrix_1.vec4.fromValues(0, 0, 0, 1);
        this.illumination = 2;
        this.name = '';
    }
    clone(name) {
        const m = new Material();
        m.ambientColor = gl_matrix_1.vec4.clone(this.ambientColor);
        m.diffuseColor = gl_matrix_1.vec4.clone(this.diffuseColor);
        m.specularColor = gl_matrix_1.vec4.clone(this.specularColor);
        m.illumination = this.illumination;
        m.name = name;
        return m;
    }
    static loadMtl(filename, file) {
        const matMap = new Map();
        let lnl = -1;
        let nnl = file.indexOf('\n');
        const m = new Material();
        materials.set(filename, matMap);
        while (nnl !== -1) {
            const line = file.substring(lnl, nnl).trim();
            const args = line.split(' ');
            switch (args[0]) {
                case 'newmtl':
                    m.name = args[1];
                    matMap.set(args[1], m);
                    break;
                case 'Ns':
                    break;
                case 'Ka':
                    if (args.length < 5) {
                        args.push('1');
                    }
                    gl_matrix_1.vec4.set(m.ambientColor, parseFloat(args[1]), parseFloat(args[2]), parseFloat(args[3]), parseFloat(args[4]));
                    break;
                case 'Kd':
                    if (args.length < 5) {
                        args.push('1');
                    }
                    gl_matrix_1.vec4.set(m.diffuseColor, parseFloat(args[1]), parseFloat(args[2]), parseFloat(args[3]), parseFloat(args[4]));
                    break;
                case 'Ks':
                    if (args.length < 5) {
                        args.push('1');
                    }
                    gl_matrix_1.vec4.set(m.specularColor, parseFloat(args[1]), parseFloat(args[2]), parseFloat(args[3]), parseFloat(args[4]));
                    break;
                case 'Ke':
                    break;
                case 'Ni':
                    break;
                case 'd':
                    break;
                case 'illum':
                    m.illumination = parseFloat(args[1]);
                    break;
                case '':
                    break;
                case '#':
                    break;
                default:
                    console.warn('unrecognized material data', args);
                    break;
            }
            lnl = nnl + 1;
            nnl = file.indexOf('\n', lnl);
        }
        return m;
    }
}
exports.Material = Material;
const models = new Map();
class Model {
    constructor() {
        this.name = '';
        this.verts = null;
        this.tris = null;
        this.surfaces = null;
        this.normals = null;
        this.colors = null;
        this.material = null;
        this.rootTransform = null;
        this.transform = null;
        this.parent = null;
        this.maxDistance = 0;
        this.dirty = true;
        this.index = 0;
    }
    clone() {
        const m = new Model();
        m.name = this.name;
        m.verts = this.verts;
        m.tris = this.tris;
        m.surfaces = this.surfaces;
        m.normals = this.normals;
        m.colors = this.colors;
        m.material = this.material;
        m.transform = gl_matrix_1.mat4.clone(this.transform);
        m.rootTransform = gl_matrix_1.mat4.clone(this.rootTransform);
        m.parent = null;
        return m;
    }
    calcDistance() {
        let d = 0;
        for (let i = 0; i < this.verts.length; i++) {
            const a = Math.abs(this.verts[i]);
            if (a > d) {
                d = a;
            }
        }
        this.maxDistance = d;
    }
    scaleToSize([x, y, z]) {
        if (this.maxDistance === 0) {
            this.calcDistance();
        }
        return gl_matrix_1.vec3.fromValues(x / this.maxDistance, y / this.maxDistance, z / this.maxDistance);
    }
    setSize(size) {
        if (this.parent !== null) {
            gl_matrix_1.mat4.copy(this.transform, this.parent);
        }
        if (this.maxDistance === 0) {
            this.calcDistance();
        }
        gl_matrix_1.mat4.scale(this.transform, this.transform, this.scaleToSize(size));
    }
    moveTo(x, y, z) {
        this.transform[12] = x;
        this.transform[13] = y;
        this.transform[14] = z;
    }
    static loadObj(file) {
        let vertexCount = 0;
        let normalCount = 0;
        let faceCount = 0;
        const faceCounts = [];
        let nnl = file.indexOf('\n');
        let foundMore = false;
        let lnl = -1;
        while (nnl !== -1) {
            const line = file.substring(lnl, nnl).trim();
            const args = line.split(' ');
            switch (args[0]) {
                case 'o':
                    if (foundMore) {
                        faceCounts.push(faceCount);
                        faceCount = 0;
                    }
                    else {
                        foundMore = true;
                    }
                    break;
                case 'v':
                    vertexCount++;
                    break;
                case 'vn':
                    normalCount++;
                    break;
                case 'f':
                    faceCount++;
                    break;
                default:
                    break;
            }
            lnl = nnl + 1;
            nnl = file.indexOf('\n', lnl);
        }
        faceCounts.push(faceCount);
        {
            let startIndex = -1;
            const verts = new Float32Array(vertexCount * 3);
            let vn = 0;
            const normals = new Float32Array(normalCount * 3);
            let nn = 0;
            let objectNumber = 0;
            while (foundMore) {
                const m = new Model();
                const tris = new Int32Array(faceCounts[objectNumber] * 3);
                const surfaces = new Int32Array(faceCounts[objectNumber]);
                let tn = 0;
                let sn = 0;
                let matFn = '';
                let matName = '';
                let nextIndex = Infinity;
                let foundName = false;
                foundMore = false;
                m.transform = gl_matrix_1.mat4.create();
                m.rootTransform = gl_matrix_1.mat4.create();
                m.verts = verts;
                m.normals = normals;
                m.tris = tris;
                m.surfaces = surfaces;
                lnl = startIndex;
                nnl = file.indexOf('\n', lnl);
                main: while (nnl !== -1 && nnl < nextIndex) {
                    const line = file.substring(lnl, nnl).trim();
                    const args = line.split(' ');
                    switch (args[0]) {
                        case 'o':
                            m.name = args[1];
                            models.set(args[1], m);
                            if (foundName) {
                                nextIndex = lnl;
                                foundMore = true;
                                break main;
                            }
                            foundName = true;
                            break;
                        case 'v':
                            verts[vn] = parseFloat(args[1]);
                            verts[vn + 1] = parseFloat(args[2]);
                            verts[vn + 2] = parseFloat(args[3]);
                            vn += 3;
                            break;
                        case 'vn':
                            normals[nn] = parseFloat(args[1]);
                            normals[nn + 1] = parseFloat(args[2]);
                            normals[nn + 2] = parseFloat(args[3]);
                            nn += 3;
                            break;
                        case 'f':
                            tris[tn]
                                = parseInt(args[1].substring(0, args[1].indexOf('/')), 10) - 1;
                            surfaces[sn]
                                = parseInt(args[1].substring(args[1].lastIndexOf('/') + 1), 10)
                                    - 1;
                            tris[tn + 1]
                                = parseInt(args[2].substring(0, args[2].indexOf('/')), 10) - 1;
                            if (parseInt(args[2].substring(args[2].lastIndexOf('/') + 1), 10)
                                - 1
                                !== surfaces[sn]) {
                                console.warn('Found unexpectedly differing surface ID (vert#2), might need to save them separately for GPU');
                            }
                            tris[tn + 2]
                                = parseInt(args[3].substring(0, args[3].indexOf('/')), 10) - 1;
                            if (parseInt(args[3].substring(args[3].lastIndexOf('/') + 1), 10)
                                - 1
                                !== surfaces[sn]) {
                                console.warn('Found unexpectedly differing surface ID (vert#3), might need to save them separately for GPU');
                            }
                            sn = sn + 1;
                            tn += 3;
                            break;
                        case 's':
                            break;
                        case 'mtllib':
                            matFn = args[1];
                            if (matFn !== '' && matName !== '') {
                                if (!materials.has(matFn)) {
                                    console.warn('Material file not found:', matFn);
                                    break;
                                }
                                if (!materials.get(matFn).has(matName)) {
                                    console.warn('Material not found:', matName, 'in', matFn);
                                    break;
                                }
                                m.material = materials.get(matFn).get(matName);
                            }
                            break;
                        case 'usemtl':
                            matName = args[1];
                            if (matFn !== '' && matName !== '') {
                                if (!materials.has(matFn)) {
                                    console.warn('Could not find material file! ' + matFn);
                                    break;
                                }
                                if (!materials.get(matFn).has(matName)) {
                                    console.warn(`Could not find material ${matName} in ${matFn}!`);
                                    break;
                                }
                                m.material = materials.get(matFn).get(matName);
                            }
                            break;
                        case '':
                            break;
                        case '#':
                            break;
                        default:
                            console.warn('unrecognized object data:', args);
                            break;
                    }
                    lnl = nnl + 1;
                    nnl = file.indexOf('\n', lnl);
                }
                startIndex = nextIndex;
                objectNumber++;
            }
        }
    }
    static get(name) {
        if (!models.has(name)) {
            throw Error('Model not found! ' + name);
        }
        return models.get(name).clone();
    }
    static build(data) {
        const m = Model.get(data.name);
        if (data.material) {
            const material = data.material;
            const file = materials.get(material.file);
            if (!materials.has(material.file)) {
                throw 'MaterialError: Material file not found: ' + material.file;
            }
            if (!materials.get(material.file).has(material.name)) {
                throw `MaterialError: Material: ${material.name} not found in file: ${material.file}`;
            }
            m.material = file.get(material.name);
            if (utils_1.objHas(material, 'recolor')) {
                const name = material.name + material.recolor.name;
                if (file.has(name)) {
                    m.material = file.get(name);
                }
                else {
                    const mat = m.material.clone(name);
                    gl_matrix_1.vec4.set(mat.diffuseColor, material.recolor.rgba[0], material.recolor.rgba[1], material.recolor.rgba[2], material.recolor.rgba[3] || 1);
                    gl_matrix_1.vec4.set(mat.ambientColor, material.recolor.rgba[0], material.recolor.rgba[1], material.recolor.rgba[2], material.recolor.rgba[3] || 1);
                    file.set(name, mat);
                    m.material = mat;
                }
            }
        }
        let scale = null;
        if (data.size) {
            scale = m.scaleToSize(data.size);
        }
        else {
            scale = gl_matrix_1.vec3.fromValues(1, 1, 1);
        }
        if (data.scale) {
            gl_matrix_1.vec3.mul(scale, scale, gl_matrix_1.vec3.fromValues(data.scale[0], data.scale[1], data.scale[2]));
        }
        scale[1] = -scale[1];
        const rotation = data.rotation
            ? exports.parseRotation(data.rotation)
            : gl_matrix_1.quat.create();
        const translation = data.position
            ? gl_matrix_1.vec3.fromValues(data.position[0], -data.position[1], data.position[2])
            : gl_matrix_1.vec3.create();
        gl_matrix_1.mat4.fromRotationTranslationScale(m.transform, rotation, translation, scale);
        gl_matrix_1.mat4.copy(m.rootTransform, m.transform);
        return m;
    }
}
exports.Model = Model;
const parseRotation = (rotation) => {
    if (Array.isArray(rotation)) {
        return gl_matrix_1.quat.fromValues(...rotation);
    }
    if (rotation instanceof Object) {
        return math_1.quatFromRadians(rotation.pitch || 0, rotation.yaw || 0, rotation.roll || 0);
    }
    console.error('rotation could not be parsed:', rotation);
    return gl_matrix_1.quat.create();
};
exports.parseRotation = parseRotation;
class Prefab {
    constructor() {
        this.models = [];
        this.transform = null;
        this.names = new Map();
    }
    static build(data) {
        const p = new Prefab();
        const rotation = data.rotation
            ? exports.parseRotation(data.rotation)
            : gl_matrix_1.quat.create();
        const translation = data.position
            ? gl_matrix_1.vec3.fromValues(data.position[0], data.position[1], data.position[2])
            : gl_matrix_1.vec3.create();
        const scale = data.scale
            ? gl_matrix_1.vec3.fromValues(data.scale[0], data.scale[1], -data.scale[2])
            : gl_matrix_1.vec3.fromValues(1, 1, -1);
        p.transform = gl_matrix_1.mat4.fromRotationTranslationScale(gl_matrix_1.mat4.create(), rotation, translation, scale);
        for (let i = 0; i < data.models.length; i++) {
            const d = data.models[i];
            const m = Model.build(d);
            m.parent = p.transform;
            gl_matrix_1.mat4.mul(m.transform, m.transform, p.transform);
            p.models.push(m);
            p.names.set(d.alias || d.name, m);
        }
        return p;
    }
}
exports.Prefab = Prefab;
for (const [name, file] of mtlFiles) {
    Material.loadMtl(name, file);
}
for (const file of objFiles) {
    Model.loadObj(file);
}
utils_1.Sync.loading.done();
//# sourceMappingURL=model.js.map