import { mat4 } from "gl-matrix";
import SimplexNoise from "simplex-noise";
import {
    AmbientLight,
    BoxBufferGeometry,
    Group,
    HemisphereLight,
    Line,
    LineBasicMaterial,
    LineSegments,
    Mesh,
    MeshBasicMaterial,
    OctahedronBufferGeometry,
    PerspectiveCamera,
    PlaneHelper,
    Scene,
    TorusKnotBufferGeometry,
    Vector2,
    WebGLRenderer,
} from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import Stats from "three/examples/jsm/libs/stats.module.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { Float32BufferAttribute } from "three/src/core/BufferAttribute.js";
import { BufferGeometry } from "three/src/core/BufferGeometry.js";
import { DEG2RAD, smoothstep } from "three/src/math/MathUtils.js";
import '../../../../../../../../../css/game.scss'
import { makeHelperBoxGeometry } from "../../../../../../editor/process/symbolic/makeHelperBoxGeometry.js";
import { makeHelperSphereGeometry } from "../../../../../../editor/process/symbolic/makeHelperSphereGeometry.js";
import { array_copy } from "../../../../../core/collection/array/array_copy.js";
import { RingBuffer } from "../../../../../core/collection/RingBuffer.js";
import { Color } from "../../../../../core/color/Color.js";
import { parse_color } from "../../../../../core/color/parse_color.js";
import Signal from "../../../../../core/events/signal/Signal.js";
import Quaternion from "../../../../../core/geom/Quaternion.js";
import { v2_distance } from "../../../../../core/geom/vec2/v2_distance.js";
import Vector3 from "../../../../../core/geom/Vector3.js";
import { Localization } from "../../../../../core/localization/Localization.js";
import { min2 } from "../../../../../core/math/min2.js";
import { randomFloatBetween } from "../../../../../core/math/random/randomFloatBetween.js";
import { randomFromArray } from "../../../../../core/math/random/randomFromArray.js";
import { seededRandom } from "../../../../../core/math/random/seededRandom.js";
import { number_pretty_print } from "../../../../../core/primitives/numbers/number_pretty_print.js";
import domify from "../../../../../view/DOM.js";
import ButtonView from "../../../../../view/elements/button/ButtonView.js";
import { GMLEngine } from "../../../../../view/tooltip/gml/GMLEngine.js";
import { TooltipManager } from "../../../../../view/tooltip/TooltipManager.js";
import { AssetManager } from "../../../../asset/AssetManager.js";
import { ImageRGBADataLoader } from "../../../../asset/loaders/image/ImageRGBADataLoader.js";
import { traverseMaterialTextures } from "../../../../asset/loaders/material/traverseMaterialTextures.js";
import { MetricCollection } from "../../../../development/performance/MetricCollection.js";
import { MetricStatistics } from "../../../../development/performance/MetricStatistics.js";
import { MouseEvents } from "../../../../input/devices/events/MouseEvents.js";
import { PointerDevice } from "../../../../input/devices/PointerDevice.js";
import { StaticKnowledgeDatabase } from "../../../../knowledge/database/StaticKnowledgeDatabase.js";
import { debugAtlas } from "../../../particles/particular/engine/shader/debugAtlas.js";
import { Sampler2D } from "../../../texture/sampler/Sampler2D.js";
import { three_setSceneAutoUpdate } from "../../../three/three_setSceneAutoUpdate.js";
import { threeUpdateTransform } from "../../../util/threeUpdateTransform.js";
import { buildLightClusterWidget } from "../debug/buildLightClusterWidget.js";
import { createLayerSwitcher } from "../debug/createLayerSwitcher.js";
import { createScreenGrid } from "../debug/createScreenGrid.js";
import { LightManager } from "../LightManager.js";
import { FPlusDebugMaterial } from "../materials/FPlusDebugMaterial.js";
import { FPlusStandardMaterial } from "../materials/FPlusStandardMaterial.js";
import { Decal } from "../model/Decal.js";
import { PointLight } from "../model/PointLight.js";
import { SpotLight } from "../model/SpotLight.js";

document.body.style.margin = 0;

const on_render = new Signal();

const lm = new LightManager();

const camera = new PerspectiveCamera(30);
camera.position.z = 1;

camera.layers.enable(1);
camera.layers.enable(3);
camera.layers.enable(4);
// camera.layers.enable(1);

camera.near = 1;
camera.far = 15;


camera.updateProjectionMatrix();
camera.updateMatrixWorld(true);

// lm.setTileMapResolution(32,32,32);
// lm.setTileMapResolution(2, 2, 2);
lm.setTileMapResolution(24, 14, 8);
// lm.setTileMapResolution(16, 8, 10);

console.log(lm);
window.lights = lm;

const random = seededRandom(0);

const scene = new Scene();
scene.matrixAutoUpdate = false;
three_setSceneAutoUpdate(scene, false);

const sphere_geometry = new OctahedronBufferGeometry(1, 2);
const sphere_geometry_shell = makeHelperSphereGeometry(1);
// const sphere_geometry = new SphereBufferGeometry(1, 8, 8);

// const directionalLight = new DirectionalLight(0xffffff, 0.7);
// directionalLight.position.set(10, 20, 0);
// directionalLight.castShadow = true;
// directionalLight.shadow.mapSize.set(2048, 2048);
// scene.add(directionalLight);
//

scene.add(new HemisphereLight("rgb(107,187,255)", "rgb(105,97,83)", 0.2));


const fp_materials = [];
const scratch_v2 = new Vector2();

function getFPStandard() {
    const r = new FPlusStandardMaterial();

    if (lm !== undefined) {
        r.attach_lights(lm);
    }


    fp_materials.push(r);

    return r;
}

const material_fp_standard = new FPlusStandardMaterial({
    color: 0xFFFF00,
    polygonOffset: true,
    depthWrite: false
});

fp_materials.push(material_fp_standard);


function addKnot(position, scale, rotation) {

    const group = new Group();

    group.position.set(
        position[0],
        position[1],
        position[2]
    );

    group.rotation.set(
        rotation[0],
        rotation[1],
        rotation[1]
    );

    group.scale.setScalar(scale);


    const mesh = new Mesh(knot_geometry, debug_material);

    mesh.layers.set(2);


    const outline = new Mesh(knot_geometry, material_fp_standard);
    outline.layers.set(3);

    group.add(mesh);
    group.add(outline);

    group.matrixAutoUpdate = false;

    threeUpdateTransform(group);

    scene.add(group);
}

function addBox(position, scale, rotation) {

    const group = new Group();

    group.position.set(
        position[0],
        position[1],
        position[2]
    );

    group.rotation.set(
        rotation[0],
        rotation[1],
        rotation[1]
    );

    if (typeof scale === "number") {
        group.scale.setScalar(scale);
    } else {
        group.scale.fromArray(scale);
    }


    const mesh = new Mesh(box_geometry, debug_material);

    mesh.layers.set(2);


    const outline = new Mesh(box_geometry, material_fp_standard);
    outline.layers.set(3);

    group.add(mesh);
    group.add(outline);

    group.matrixAutoUpdate = false;

    threeUpdateTransform(group);

    scene.add(group);
}

function addSpotLight(position, direction, color, distance = 1, angle = 1, penumbra = 0.4, intensity = 1) {

    const light = new SpotLight();

    light.position.fromArray(position, 0);

    light.angle.set(
        angle
    );
    light.penumbra.set(penumbra);
    light.distance.set(distance);
    light.intensity.set(intensity);
    light.direction.fromArray(direction, 0);

    if (typeof color === "number") {
        light.color.fromUint(color);
    } else {

        const c = parse_color(color);

        light.color.setRGB(c[0] / 255, c[1] / 255, c[2] / 255);
    }

    lm.addLight(light);
}

const noise = new SimplexNoise(random);

/**
 *
 * @param {number[]} position
 * @param {number} radius
 * @param {string|number} color
 * @param {number} intensity
 * @param {boolean} wander
 * @param {number} [wander_radius]
 */
function addPointLight({
                           position, radius, color, intensity = 1,
                           wander = false,
                           wander_radius = 5
                       }) {

    const light = new PointLight();

    const p = light.position;
    p.set(
        position[0],
        position[1],
        position[2],
    );

    light.radius.set(
        radius
    );

    light.intensity.set(intensity);

    if (typeof color === "number") {
        light.color.fromUint(color);
    } else {

        const c = parse_color(color);

        light.color.setRGB(c[0] / 255, c[1] / 255, c[2] / 255);
    }

    lm.addLight(light);

    // outer shell
    const visual_group = new Group();

    const color_uint = light.color.toUint();
    const sphere_material = new LineBasicMaterial({
        depthTest: true,
        depthWrite: false,
        transparent: true,
        linewidth: 1,
        fog: false,
        color: color_uint,
        opacity: min2(intensity, 1)
    });

    const mesh = new Line(sphere_geometry_shell, sphere_material);

    mesh.layers.set(1);
    mesh.scale.setScalar(light.radius.getValue());

    // center
    const mesh_center_0 = new Mesh(sphere_geometry, new MeshBasicMaterial({
        color: color_uint
    }));
    mesh_center_0.layers.disableAll();
    mesh_center_0.layers.set(4);
    mesh_center_0.scale.setScalar(0.02);
    mesh_center_0.matrixAutoUpdate = false;

    visual_group.add(mesh_center_0);

    const mesh_center_1 = new Mesh(sphere_geometry, new MeshBasicMaterial({
        color: color_uint,
        transparent: true,
        opacity: 0.033
    }));
    mesh_center_1.scale.setScalar(0.1);
    mesh_center_1.layers.disableAll();
    mesh_center_1.layers.set(4);
    mesh_center_1.matrixAutoUpdate = false;

    visual_group.add(mesh_center_1);


    visual_group.add(mesh);
    visual_group.position.set(p.x, p.y, p.z);

    threeUpdateTransform(visual_group);
    visual_group.matrixAutoUpdate = false;

    scene.add(visual_group);

    if (wander) {
        const source_p_x = position[0];
        const source_p_y = position[1];
        const source_p_z = position[2];

        on_render.add((renderer, camera, scene, time_delta, time) => {

            const t = time * 0.0001;

            const noise_x = noise.noise4D(source_p_x, source_p_y, source_p_z, t) * wander_radius;
            const noise_y = noise.noise4D(source_p_x + 31.341, source_p_y - 43.23, source_p_z + 12.34, t) * wander_radius;
            const noise_z = noise.noise4D(source_p_x - 231.141, source_p_y + 124.123, source_p_z - 54.4341, t) * wander_radius;

            p.set(
                source_p_x + noise_x,
                source_p_y + noise_y,
                source_p_z + noise_z,
            );

            visual_group.position.set(
                p.x,
                p.y,
                p.z,
            );

            threeUpdateTransform(visual_group);
        });
    }
}

/**
 *
 * @param {number[]} position
 * @param {number[]} scale
 * @param {number[]} [rotation] Quaternion
 * @param {Sampler2D} sampler
 * @param {THREE.Scene} scene
 * @param {LightManager} light_manager
 */
function addDecal({
                      position,
                      scale,
                      rotation = [0, 0, 0, 1],
                      sampler,
                      scene,
                      light_manager
                  }) {
    const decal = new Decal();

    mat4.fromRotationTranslationScale(decal.transform, rotation, position, scale);
    mat4.invert(decal.transform_inverse, decal.transform);

    decal.texture_diffuse = sampler;

    const vis = new Group();
    vis.frustumCulled = false;

    const tLine = new LineSegments(makeHelperBoxGeometry(), new LineBasicMaterial({
        color: 0xFFFFFF,
        transparent: true,
        opacity: 0.4
    }));

    vis.add(tLine);


    const _lineGeometry = new BufferGeometry();
    _lineGeometry.setAttribute('position', new Float32BufferAttribute([0, 0, 0.3, 0, 0, 0.5], 3));

    const tHelper = new Line(_lineGeometry, new LineBasicMaterial({ color: 0xFFFFFF, toneMapped: false }));

    vis.add(tHelper);

    vis.traverse(o => {
        o.layers.set(1);

        o.matrixAutoUpdate = false;
        o.matrixWorldNeedsUpdate = false;
        array_copy(decal.transform, 0, o.matrixWorld.elements, 0, 16);

    });

    scene.add(vis);

    light_manager.addLight(decal);
}

const knot_geometry = new TorusKnotBufferGeometry(1, 0.3, 128, 128);
const box_geometry = new BoxBufferGeometry(1, 1, 1);
const debug_material = new FPlusDebugMaterial();


fp_materials.push(debug_material);
fp_materials.forEach(m => m.attach_lights(lm));


function prepare_scene_0() {

    camera.near = 1;
    camera.far = 30;

    controls.minDistance = 2;

    const bounds_size = 15;

    const light_radius = 5;

    for (let i = 0; i < 5000; i++) {
        const radius = randomFloatBetween(random, light_radius, light_radius);
        const intensity = randomFloatBetween(random, 2, 2);
        addPointLight({
                position: [
                    randomFloatBetween(random, -bounds_size, bounds_size),
                    randomFloatBetween(random, -bounds_size, bounds_size),
                    randomFloatBetween(random, -bounds_size, bounds_size),
                ], radius: radius, color: `hsv(${random().toFixed(4)}, 0.8, 1)`, intensity: intensity
            }
        );
    }

    const bounds_size_2 = 15;

    for (let i = 0; i < 500; i++) {

        addKnot([
            randomFloatBetween(random, -bounds_size_2, bounds_size_2),
            randomFloatBetween(random, -bounds_size_2, bounds_size_2),
            randomFloatBetween(random, -bounds_size_2, bounds_size_2)
        ], 0.5, [
            randomFloatBetween(random, 0, Math.PI),
            randomFloatBetween(random, 0, Math.PI),
            randomFloatBetween(random, 0, Math.PI)
        ]);
    }
}

function prepare_scene_1() {
    addPointLight({
            position: [
                0, 0, -3
            ], radius: 1, color: 0xFF0000
        }
    );

    // tight planes around the light
    camera.near = 3;
    camera.far = 5;

    // camera.position.set(0, 0, 0);
    camera.lookAt(0, 0, -5);
}

function prepare_scene_3() {
    addPointLight({
            position: [
                0, 0, -4
            ], radius: 1, color: 0xFF0000
        }
    );

    // tight planes around the light
    camera.near = 3;
    camera.far = 7;

    addKnot([
        0, 0, -4
    ], 1, [0, 0, 0]);

    // camera.position.set(0, 0, 0);
    camera.lookAt(0, 0, -5);
}

function prepare_scene_2() {
    addPointLight({
            position: [
                1.5, 0, -4
            ], radius: 1.5, color: 0xFF0000, intensity: 1
        }
    );
    addPointLight({
            position: [
                -1.5, 0, -4
            ], radius: 1.5, color: 0x00FF00, intensity: 1
        }
    );

    addKnot([0, 0, -5], 1, [0, 0, 0]);

    camera.near = 1;
    camera.far = 7;

    camera.lookAt(0, 0, -5);
}

function prepare_scene_decal_0() {
    addPointLight({
            position: [
                1.5, 0, -4
            ], radius: 1.5, color: 0xFF0000, intensity: 1
        }
    );

    addPointLight({
            position: [
                -1.5, 0, -4
            ], radius: 1.5, color: 0xFFFFFF, intensity: 1
        }
    );


    function make_radial_sampler(size = 16) {

        const s = Sampler2D.uint8(4, size, size);

        const size_2 = size * 0.5;
        const size_2_inv = 1 / size_2;

        const sample = [];
        for (let y = 0; y < size; y++) {
            for (let x = 0; x < size; x++) {
                const distance = v2_distance(x, y, size_2 - 0.5, size_2 - 0.5) * size_2_inv;

                sample[0] = Math.round((x / size) * 255);
                sample[1] = Math.round((y / size) * 255);
                sample[2] = 255;
                sample[3] = Math.round((1 - smoothstep(distance, 0.5, 1)) * 255);

                s.write(x, y, sample);
            }
        }

        return s;
    }

    // const sampler = new Sampler2D(new Uint8Array([
    //     255, 0, 0, 255,
    //     0, 255, 0, 255,
    //     0, 0, 255, 255,
    //     255, 255, 255, 255,
    // ]), 4, 2, 2);

    const sampler = make_radial_sampler();

    const q = new Quaternion();
    q.fromEulerAnglesXYZ(0, (90 + 60) * DEG2RAD, 0);

    addDecal({
        position: [
            -1.5, 0, -4
        ],
        scale: [
            1.5, 1.5, 1.5
        ],
        rotation: q,
        sampler: sampler,
        scene,
        light_manager: lm
    });

    // addBox([0, 0, -4], 2, [0, 0, 0]);
    addKnot([0, 0, -4], 1, [0, 0, 0]);

    scene.add(new AmbientLight(0xFFFFFF, 0.1));

    camera.near = 1;
    camera.far = 7;

    camera.lookAt(0, 0, -5);
}

async function prepare_scene_decal_1() {

    const am = new AssetManager(null);

    await am.registerLoader("image", new ImageRGBADataLoader());


    const decal_urls = `data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_13_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_13_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_14_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_10_b.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_13_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_14_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_15_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_16_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_17_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_13_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_12_t.png
data/textures/icons/sci-fi-nathan/v6/Proc normal_3.png
data/textures/icons/sci-fi-nathan/v6/Proc strong_1.png
data/textures/icons/sci-fi-nathan/v6/Proc strong_2.png
data/textures/icons/sci-fi-nathan/v6/Proc strong_3.png
data/textures/icons/sci-fi-nathan/v6/Proc very strong_1.png
data/textures/icons/sci-fi-nathan/v6/Proc very strong_2.png
data/textures/icons/sci-fi-nathan/v6/Proc very strong_3.png
data/textures/icons/sci-fi-nathan/v6/Proc very weak_1.png
data/textures/icons/sci-fi-nathan/v6/Proc very weak_2.png
data/textures/icons/sci-fi-nathan/v6/Proc very weak_3.png
data/textures/icons/sci-fi-nathan/v6/Proc weak_1.png
data/textures/icons/sci-fi-nathan/v6/Proc weak_2.png
data/textures/icons/sci-fi-nathan/v6/Proc weak_3.png
data/textures/icons/sci-fi-nathan/v6/Core normal_1.png
data/textures/icons/sci-fi-nathan/v6/Core normal_2.png
data/textures/icons/sci-fi-nathan/v6/Core normal_3.png
data/textures/icons/sci-fi-nathan/v6/Core strong_1.png
data/textures/icons/sci-fi-nathan/v6/Core strong_2.png
data/textures/icons/sci-fi-nathan/v6/Core strong_3.png
data/textures/icons/sci-fi-nathan/v6/Core very strong_1.png
data/textures/icons/sci-fi-nathan/v6/Core very strong_2.png
data/textures/icons/sci-fi-nathan/v6/Core very strong_3.png
data/textures/icons/sci-fi-nathan/v6/Core very weak_1.png
data/textures/icons/sci-fi-nathan/v6/Core very weak_2.png
data/textures/icons/sci-fi-nathan/v6/Core very weak_3.png
data/textures/icons/sci-fi-nathan/v6/Core weak_1.png
data/textures/icons/sci-fi-nathan/v6/Core weak_2.png
data/textures/icons/sci-fi-nathan/v6/Core weak_3.png
data/textures/icons/sci-fi-nathan/v6/Memory normal_1.png
data/textures/icons/sci-fi-nathan/v6/Memory normal_2.png
data/textures/icons/sci-fi-nathan/v6/Memory normal_3.png
data/textures/icons/sci-fi-nathan/v6/Memory strong_1.png
data/textures/icons/sci-fi-nathan/v6/Memory strong_2.png
data/textures/icons/sci-fi-nathan/v6/Memory strong_3.png
data/textures/icons/sci-fi-nathan/v6/Memory very strong_1.png
data/textures/icons/sci-fi-nathan/v6/Memory very strong_2.png
data/textures/icons/sci-fi-nathan/v6/Memory very strong_3.png
data/textures/icons/sci-fi-nathan/v6/Memory very weak_1.png
data/textures/icons/sci-fi-nathan/v6/Memory very weak_2.png
data/textures/icons/sci-fi-nathan/v6/Memory very weak_3.png
data/textures/icons/sci-fi-nathan/v6/Memory weak_1.png
data/textures/icons/sci-fi-nathan/v6/Memory weak_2.png
data/textures/icons/sci-fi-nathan/v6/Memory weak_3.png
data/textures/icons/sci-fi-nathan/v6/Module normal_1.png
data/textures/icons/sci-fi-nathan/v6/Module normal_2.png
data/textures/icons/sci-fi-nathan/v6/Module normal_3.png
data/textures/icons/sci-fi-nathan/v6/Module strong_1.png
data/textures/icons/sci-fi-nathan/v6/Module strong_2.png
data/textures/icons/sci-fi-nathan/v6/Module strong_3.png
data/textures/icons/sci-fi-nathan/v6/Module very strong_1.png
data/textures/icons/sci-fi-nathan/v6/Module very strong_2.png
data/textures/icons/sci-fi-nathan/v6/Module very strong_3.png
data/textures/icons/sci-fi-nathan/v6/Module very weak_1.png
data/textures/icons/sci-fi-nathan/v6/Module very weak_2.png
data/textures/icons/sci-fi-nathan/v6/Module very weak_3.png
data/textures/icons/sci-fi-nathan/v6/Module weak_1.png
data/textures/icons/sci-fi-nathan/v6/Module weak_2.png
data/textures/icons/sci-fi-nathan/v6/Module weak_3.png
data/textures/icons/sci-fi-nathan/v6/Power normal_1.png
data/textures/icons/sci-fi-nathan/v6/Power normal_2.png
data/textures/icons/sci-fi-nathan/v6/Power normal_3.png
data/textures/icons/sci-fi-nathan/v6/Power strong_1.png
data/textures/icons/sci-fi-nathan/v6/Power strong_2.png
data/textures/icons/sci-fi-nathan/v6/Power strong_3.png
data/textures/icons/sci-fi-nathan/v6/Power very strong_1.png
data/textures/icons/sci-fi-nathan/v6/Power very strong_2.png
data/textures/icons/sci-fi-nathan/v6/Power very strong_3.png
data/textures/icons/sci-fi-nathan/v6/Power very weak_1.png
data/textures/icons/sci-fi-nathan/v6/Power very weak_2.png
data/textures/icons/sci-fi-nathan/v6/Power very weak_3.png
data/textures/icons/sci-fi-nathan/v6/Power weak_1.png
data/textures/icons/sci-fi-nathan/v6/Power weak_2.png
data/textures/icons/sci-fi-nathan/v6/Power weak_3.png
data/textures/icons/sci-fi-nathan/v6/Proc normal_1.png
data/textures/icons/sci-fi-nathan/v6/Proc normal_2.png`
        .split('\n')
        .map(a => a.trim());

    const samplers = [];
    const promises = [];

    for (let i = 0; i < decal_urls.length; i++) {
        const promise = am.promise(decal_urls[i], 'image');
        promises.push(promise);
    }

    const assets = await Promise.all(promises);

    for (let i = 0; i < assets.length; i++) {
        const asset = assets[i];

        samplers[i] = asset.create();
    }

    const canvas_size = 5;

    const random = seededRandom();

    const COUNT = 1024;

    const ROW_WIDTH = Math.ceil(Math.sqrt(COUNT));
    const size = canvas_size / ROW_WIDTH;

    for (let i = 0; i < COUNT; i++) {

        const q = new Quaternion();
        // q.fromEulerAnglesXYZ(0, (90 + randomFloatBetween(random, -60, 60)) * DEG2RAD, 0);
        q.fromEulerAnglesXYZ(0, (-180) * DEG2RAD, 0);

        const sampler = randomFromArray(random, samplers);
        // const sampler = samplers[i % samplers.length];

        // const size = randomFloatBetween(random, 1, 3);
        //const size = 0.5;

        // const position = [
        //     randomFloatBetween(random, -canvas_size * 0.5, canvas_size * 0.5), randomFloatBetween(random, -canvas_size * 0.5, canvas_size * 0.5), -4
        // ];

        const x = ((i % ROW_WIDTH)) / ROW_WIDTH - 0.5 + 0.5 / ROW_WIDTH;
        const y = Math.floor(i / ROW_WIDTH) / ROW_WIDTH - 0.5 + 0.5 / ROW_WIDTH;
        const position = [
            x * canvas_size, y * canvas_size, -4
        ];

        addDecal({
            position: position,
            scale: [
                size, size, 1.5
            ],
            rotation: q,
            sampler: sampler,
            scene,
            light_manager: lm
        });

    }

    // addBox([0, 0, -4], [canvas_size, canvas_size, 1], [0, 0, 0]);
    //
    addKnot([0, 0, -4], 2, [0, 0, 0])

    scene.add(new AmbientLight(0xFFFFFF, 0.8));

    camera.near = 1;
    camera.far = 30;

    camera.lookAt(0, 0, -5);
    camera.position.set(0, 0, 10);
}

async function prepare_scene_decal_2() {

    const am = new AssetManager(null);

    await am.registerLoader("image", new ImageRGBADataLoader());


    const decal_urls = `data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_13_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_13_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/artifact_14_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_10_b.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/bag_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_13_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_14_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_15_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_16_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/book_17_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/orb_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/potion_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/rune_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_12_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/scroll_13_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_01_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_02_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_03_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_04_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_05_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_06_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_07_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_08_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_09_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_10_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_11_t.png
data/textures/icons/FantasyIconsMegaPack/MagicItems/MagicItems_png/transparent/x64/staff_12_t.png
data/textures/icons/sci-fi-nathan/v6/Proc normal_3.png
data/textures/icons/sci-fi-nathan/v6/Proc strong_1.png
data/textures/icons/sci-fi-nathan/v6/Proc strong_2.png
data/textures/icons/sci-fi-nathan/v6/Proc strong_3.png
data/textures/icons/sci-fi-nathan/v6/Proc very strong_1.png
data/textures/icons/sci-fi-nathan/v6/Proc very strong_2.png
data/textures/icons/sci-fi-nathan/v6/Proc very strong_3.png
data/textures/icons/sci-fi-nathan/v6/Proc very weak_1.png
data/textures/icons/sci-fi-nathan/v6/Proc very weak_2.png
data/textures/icons/sci-fi-nathan/v6/Proc very weak_3.png
data/textures/icons/sci-fi-nathan/v6/Proc weak_1.png
data/textures/icons/sci-fi-nathan/v6/Proc weak_2.png
data/textures/icons/sci-fi-nathan/v6/Proc weak_3.png
data/textures/icons/sci-fi-nathan/v6/Core normal_1.png
data/textures/icons/sci-fi-nathan/v6/Core normal_2.png
data/textures/icons/sci-fi-nathan/v6/Core normal_3.png
data/textures/icons/sci-fi-nathan/v6/Core strong_1.png
data/textures/icons/sci-fi-nathan/v6/Core strong_2.png
data/textures/icons/sci-fi-nathan/v6/Core strong_3.png
data/textures/icons/sci-fi-nathan/v6/Core very strong_1.png
data/textures/icons/sci-fi-nathan/v6/Core very strong_2.png
data/textures/icons/sci-fi-nathan/v6/Core very strong_3.png
data/textures/icons/sci-fi-nathan/v6/Core very weak_1.png
data/textures/icons/sci-fi-nathan/v6/Core very weak_2.png
data/textures/icons/sci-fi-nathan/v6/Core very weak_3.png
data/textures/icons/sci-fi-nathan/v6/Core weak_1.png
data/textures/icons/sci-fi-nathan/v6/Core weak_2.png
data/textures/icons/sci-fi-nathan/v6/Core weak_3.png
data/textures/icons/sci-fi-nathan/v6/Memory normal_1.png
data/textures/icons/sci-fi-nathan/v6/Memory normal_2.png
data/textures/icons/sci-fi-nathan/v6/Memory normal_3.png
data/textures/icons/sci-fi-nathan/v6/Memory strong_1.png
data/textures/icons/sci-fi-nathan/v6/Memory strong_2.png
data/textures/icons/sci-fi-nathan/v6/Memory strong_3.png
data/textures/icons/sci-fi-nathan/v6/Memory very strong_1.png
data/textures/icons/sci-fi-nathan/v6/Memory very strong_2.png
data/textures/icons/sci-fi-nathan/v6/Memory very strong_3.png
data/textures/icons/sci-fi-nathan/v6/Memory very weak_1.png
data/textures/icons/sci-fi-nathan/v6/Memory very weak_2.png
data/textures/icons/sci-fi-nathan/v6/Memory very weak_3.png
data/textures/icons/sci-fi-nathan/v6/Memory weak_1.png
data/textures/icons/sci-fi-nathan/v6/Memory weak_2.png
data/textures/icons/sci-fi-nathan/v6/Memory weak_3.png
data/textures/icons/sci-fi-nathan/v6/Module normal_1.png
data/textures/icons/sci-fi-nathan/v6/Module normal_2.png
data/textures/icons/sci-fi-nathan/v6/Module normal_3.png
data/textures/icons/sci-fi-nathan/v6/Module strong_1.png
data/textures/icons/sci-fi-nathan/v6/Module strong_2.png
data/textures/icons/sci-fi-nathan/v6/Module strong_3.png
data/textures/icons/sci-fi-nathan/v6/Module very strong_1.png
data/textures/icons/sci-fi-nathan/v6/Module very strong_2.png
data/textures/icons/sci-fi-nathan/v6/Module very strong_3.png
data/textures/icons/sci-fi-nathan/v6/Module very weak_1.png
data/textures/icons/sci-fi-nathan/v6/Module very weak_2.png
data/textures/icons/sci-fi-nathan/v6/Module very weak_3.png
data/textures/icons/sci-fi-nathan/v6/Module weak_1.png
data/textures/icons/sci-fi-nathan/v6/Module weak_2.png
data/textures/icons/sci-fi-nathan/v6/Module weak_3.png
data/textures/icons/sci-fi-nathan/v6/Power normal_1.png
data/textures/icons/sci-fi-nathan/v6/Power normal_2.png
data/textures/icons/sci-fi-nathan/v6/Power normal_3.png
data/textures/icons/sci-fi-nathan/v6/Power strong_1.png
data/textures/icons/sci-fi-nathan/v6/Power strong_2.png
data/textures/icons/sci-fi-nathan/v6/Power strong_3.png
data/textures/icons/sci-fi-nathan/v6/Power very strong_1.png
data/textures/icons/sci-fi-nathan/v6/Power very strong_2.png
data/textures/icons/sci-fi-nathan/v6/Power very strong_3.png
data/textures/icons/sci-fi-nathan/v6/Power very weak_1.png
data/textures/icons/sci-fi-nathan/v6/Power very weak_2.png
data/textures/icons/sci-fi-nathan/v6/Power very weak_3.png
data/textures/icons/sci-fi-nathan/v6/Power weak_1.png
data/textures/icons/sci-fi-nathan/v6/Power weak_2.png
data/textures/icons/sci-fi-nathan/v6/Power weak_3.png
data/textures/icons/sci-fi-nathan/v6/Proc normal_1.png
data/textures/icons/sci-fi-nathan/v6/Proc normal_2.png
data/textures/utility/sampling-test.png`
        .split('\n')
        .map(a => a.trim());

    const samplers = [];
    const promises = [];

    for (let i = 0; i < decal_urls.length; i++) {
        const promise = am.promise(decal_urls[i], 'image');
        promises.push(promise);
    }

    const assets = await Promise.all(promises);

    for (let i = 0; i < assets.length; i++) {
        const asset = assets[i];

        samplers[i] = asset.create();
    }

    const canvas_size = 5;

    const random = seededRandom();

    const COUNT = 1024;

    const ROW_WIDTH = Math.ceil(Math.sqrt(COUNT));
    const size = canvas_size / ROW_WIDTH;

    for (let i = 0; i < COUNT; i++) {

        const q = new Quaternion();
        // q.fromEulerAnglesXYZ(0, (90 + randomFloatBetween(random, -60, 60)) * DEG2RAD, 0);
        q.fromEulerAnglesXYZ(0, (-180) * DEG2RAD, 0);

        const sampler = randomFromArray(random, samplers);
        // const sampler = samplers[i % samplers.length];

        // const size = randomFloatBetween(random, 1, 3);
        //const size = 0.5;

        // const position = [
        //     randomFloatBetween(random, -canvas_size * 0.5, canvas_size * 0.5), randomFloatBetween(random, -canvas_size * 0.5, canvas_size * 0.5), -4
        // ];

        const x = ((i % ROW_WIDTH)) / ROW_WIDTH - 0.5 + 0.5 / ROW_WIDTH;
        const y = Math.floor(i / ROW_WIDTH) / ROW_WIDTH - 0.5 + 0.5 / ROW_WIDTH;
        const position = [
            x * canvas_size, y * canvas_size, -4
        ];

        addDecal({
            position: position,
            scale: [
                size, size, 1.5
            ],
            rotation: q,
            sampler: sampler,
            scene,
            light_manager: lm
        });

    }

    addBox([0, 0, -4], [canvas_size, canvas_size, 1], [0, 0, 0]);
    //
    // addKnot([0, 0, -4], 2, [0, 0, 0])

    scene.add(new AmbientLight(0xFFFFFF, 0.8));

    camera.near = 1;
    camera.far = 30;

    camera.lookAt(0, 0, -5);
    camera.position.set(0, 0, 10);
}


async function prepare_scene_9() {


    const canvas_size = 20;

    const random = seededRandom();

    const color = new Color();

    for (let i = 0; i < 100; i++) {

        color.setHSV(random(), 1, 1);

        addPointLight({
                position: [
                    randomFloatBetween(random, -canvas_size * 0.5, canvas_size * 0.5), randomFloatBetween(random, -canvas_size * 0.5, canvas_size * 0.5), -3
                ],
                radius: 1.5,
                color: color.toUint(),
                intensity: 1
            }
        );

    }

    addBox([0, 0, -4], [canvas_size, canvas_size, 1], [0, 0, 0]);

    // scene.add(new AmbientLight(0xFFFFFF, 0.5));

    camera.near = 1;
    camera.far = 30;

    camera.lookAt(0, 0, -5);
}


function prepare_scene_5() {

    const offset = new Vector3(100, 100, 100);


    addPointLight({
            position: [
                1.5 + offset.x, 0 + offset.y, -4 + offset.z
            ], radius: 1.5, color: 0xFF0000, intensity: 10
        }
    );
    addPointLight({
            position: [
                -1.5 + offset.x, 0 + offset.y, -4 + offset.z
            ], radius: 1.5, color: 0x00FF00, intensity: 10
        }
    );

    addKnot(
        [
            0 + offset.x,
            0 + offset.y,
            -5 + offset.z
        ],

        1,
        [0, 0, 0]
    );

    camera.near = 1;
    camera.far = 7;

    camera.lookAt(0, 0, -5);
    camera.position.set(0 + offset.x, offset.y, offset.z + 1);
}


function addSponza() {
    const loader = new GLTFLoader();

    loader.load("data/models/sponza-pbr/gltf/sponza.glb", (gltf) => {

        gltf.scene.traverse(m => {
            if (m.isLight) {
                m.parent.remove(m);
            }
        });

        const m_0 = gltf.scene.clone(true);

        m_0.traverse(m => {
            m.layers.set(3);
            if (m.isMesh) {
                const mat = m.material;

                traverseMaterialTextures(mat, t => {
                    t.anisotropy = 16;
                });

                m.material = getFPStandard();
                m.material.copy_basic(mat);

                m.castShadow = true;
                m.receiveShadow = true;
            }

        });

        threeUpdateTransform(m_0);

        scene.add(m_0);

        const m_1 = gltf.scene.clone(true);

        m_1.traverse(m => {
            m.layers.set(2);
            if (m.isMesh) {
                m.material = debug_material;
            }

        });

        threeUpdateTransform(m_1);

        scene.add(m_1);

    });
}

function prepare_scene_4() {

    controls.target.set(2.1731179577352857, 2.4, -0.26);
    camera.position.set(-8, 3.3, -0.26);

    camera.near = 1;
    camera.far = 50;

    controls.minDistance = 2;

    controls.update();


    for (let i = 0; i < 500; i++) {
        const radius = randomFloatBetween(random, 2, 2);
        addPointLight({
                position: [
                    randomFloatBetween(random, -17, 17),
                    randomFloatBetween(random, -2, 13),
                    randomFloatBetween(random, -10, 10),
                ],
                radius: radius,
                color: `hsv(${random().toFixed(4)}, 0.8, 1)`,
                intensity: randomFloatBetween(random, 6, 6),
                wander: true
            }
        );
    }

    //
    // for (let i = 0; i < 4; i++) {
    //     addLight([
    //             randomFloatBetween(random, -20, 20),
    //             randomFloatBetween(random, -20, 20),
    //             randomFloatBetween(random, -20, 20),
    //         ],
    //         randomFloatBetween(random, 30, 50),
    //         `hsv(${random().toFixed(4)}, 0.8, 1)`,
    //         randomFloatBetween(random, 0.55, 0.6)
    //     );
    // }

    addSponza();
}

function prepare_scene_6() {

    controls.target.set(2.1731179577352857, 1.7178052208111272, -0.4444756234843362);
    camera.position.set(-3.6592606144314597, 3.7632561258064934, -0.3287586440078351);

    camera.near = 1;
    camera.far = 30;

    controls.minDistance = 2;

    controls.update();


    addPointLight({
            position: [
                11,
                1,
                0,
            ], radius: 2, color: `hsv(0.5, 0.8, 1)`, intensity: 3
        }
    );

    //
    // for (let i = 0; i < 4; i++) {
    //     addLight([
    //             randomFloatBetween(random, -20, 20),
    //             randomFloatBetween(random, -20, 20),
    //             randomFloatBetween(random, -20, 20),
    //         ],
    //         randomFloatBetween(random, 30, 50),
    //         `hsv(${random().toFixed(4)}, 0.8, 1)`,
    //         randomFloatBetween(random, 0.55, 0.6)
    //     );
    // }

    addSponza();
}


function prepare_scene_7() {
    addSpotLight([
            0, 0, -3
        ], [0, 0, 1],
        0xFF0000
    );

    // tight planes around the light
    camera.near = 3;
    camera.far = 5;

    // camera.position.set(0, 0, 0);
    camera.lookAt(0, 0, -5);
}


// for (let i = 0; i < 100; i++) {
//
//     lm.buildTiles(camera);
//
// }


console.log("done");


const renderer = new WebGLRenderer({
    antialias: true
});
// renderer.shadowMap.type = VSMShadowMap;
// renderer.shadowMap.enabled = true;
renderer.sortObjects = false;


document.body.appendChild(renderer.domElement);

renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);

fp_materials.forEach(m => {
    const v2 = new Vector2();

    renderer.getSize(v2);

    m.uniforms.fp_resolution.value.set(
        v2.x * window.devicePixelRatio, v2.y * window.devicePixelRatio
    );

});

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();


// scene.add(new GridHelper());
// scene.background = new Color(0x666666);

const stats = new Stats();
stats.dom.style.cssText = 'position:fixed;top:0;right:0;cursor:pointer;opacity:0.9;z-index:10000';
document.body.appendChild(stats.dom);

const controls = new OrbitControls(camera, renderer.domElement);

const exec_times_light_build = new RingBuffer(100);
const bvh_traversal_checks = new RingBuffer(100);
const bvh_traversal_rejects = new RingBuffer(100);
const bvh_traversal_rejects_sphere = new RingBuffer(100);

let time_previous = performance.now();

console.log(camera, renderer, scene);

function move_camera(time_delta) {

    camera.rotation.x += 0.0123 * time_delta;
    camera.rotation.y += 0.123 * time_delta;
    camera.rotation.z += 0.0131 * time_delta;
}


const pointerDevice = new PointerDevice(renderer.domElement);

const gml = new GMLEngine();
gml.initialize(new StaticKnowledgeDatabase(), new Localization());

const tips = new TooltipManager();
tips.initialize(gml, pointerDevice);
tips.startup();

tips.contextView.css({
    position: 'absolute',
    left: 0,
    top: 0
});

document.body.appendChild(tips.contextView.el);
tips.contextView.link();
tips.contextView.size.set(window.innerWidth, window.innerHeight);


const lightClusterWidget = buildLightClusterWidget(lm, camera);
const screenGrid = createScreenGrid({
    width: window.innerWidth,
    height: window.innerHeight,
    lm: lm,
    heatmap: true,
    tooltips: tips
});
screenGrid.view.link();

function animate() {

    requestAnimationFrame(animate);

    const t = performance.now();

    const time_delta = (t - time_previous) / 1000;

    time_previous = t;


    // controls.update();

    camera.updateMatrix();
    camera.updateWorldMatrix(true, true);
    camera.updateProjectionMatrix();

    do_sample();

    debug_material.uniforms.fp_f_camera_near.value = camera.near;
    debug_material.uniforms.fp_f_camera_far.value = camera.far;

    material_fp_standard.uniforms.fp_f_camera_near.value = camera.near;
    material_fp_standard.uniforms.fp_f_camera_far.value = camera.far;


    renderer.getSize(scratch_v2);
    scratch_v2.multiplyScalar(renderer.getPixelRatio());

    fp_materials.forEach(m => {

        m.uniforms.fp_f_camera_near.value = camera.near;
        m.uniforms.fp_f_camera_far.value = camera.far;


        m.uniforms.fp_resolution.value.set(scratch_v2.x, scratch_v2.y);
    });

    lightClusterWidget.update();
    screenGrid.update();

    renderer.render(scene, camera);

    on_render.send6(renderer, camera, scene, time_delta, t);

    stats.update();
}

const metrics = new MetricCollection();

const METRIC_BUFFER_SIZE = 200;

metrics.create({ name: "time", buffer_size: METRIC_BUFFER_SIZE })
metrics.create({ name: "iterations", buffer_size: METRIC_BUFFER_SIZE })
metrics.create({ name: "rejections", buffer_size: METRIC_BUFFER_SIZE })
metrics.create({ name: "sphere_rejections", buffer_size: METRIC_BUFFER_SIZE })

function print_perf_stats() {

    const values = [];

    const stats = new MetricStatistics();
    metrics.list().forEach(m => {
        const metric = metrics.get(m);

        metric.computeStats(stats);

        values.push(`${m}: ${number_pretty_print(stats.mean)}`);
    });

    console.log(values.join(", "));
}

setInterval(print_perf_stats, 2000);

function do_sample() {
    // query_bvh_frustum_from_objects.iteration_count = 0;
    // query_bvh_frustum_from_objects.rejection_count = 0;
    // query_bvh_frustum_from_objects.rejection_sphere_count = 0;
    // query_bvh_frustum_from_objects.plane_tests = 0;
    // query_bvh_frustum_from_objects.plane_tests_early_exits = 0;

    const t0 = performance.now();
    lm.buildTiles(camera);
    const t1 = performance.now();

    metrics.get("time").record(t1 - t0);
    // metrics.get("iterations").record(query_bvh_frustum_from_objects.iteration_count);
    // metrics.get("rejections").record(query_bvh_frustum_from_objects.rejection_count);
    // metrics.get("sphere_rejections").record(query_bvh_frustum_from_objects.rejection_sphere_count);
}

function draw_camera_view_planes() {
    const frustum = lm.__view_frustum;

    const planes = frustum.planes;

    planes.forEach(p => {
        scene.add(new PlaneHelper(c, 2, 0xFFFF00));
    });
}

// prepare_scene_2();
// prepare_scene_decal_0();
// prepare_scene_decal_1();
// prepare_scene_decal_2();
// prepare_scene_9();
// prepare_scene_0(); // stress test
prepare_scene_4(); // sponza
animate();

// draw_camera_view_planes();


function perf() {

    console.profile("buildTiles");
    for (let i = 0; i < 100; i++) {
        lm.buildTiles(camera);
    }
    console.profileEnd("buildTiles");

}

renderer.domElement.addEventListener(MouseEvents.DoubleClick, () => {

    console.time("buildTiles");
    lm.buildTiles(camera);
    console.timeEnd("buildTiles");

    console.log(lm);
});

domify(lightClusterWidget.domElement).css({
    position: "absolute",
    zIndex: 2,
    bottom: 0,
    right: 0
});

document.body.appendChild(lightClusterWidget.domElement);

const debugger_button = new ButtonView({
    action() {
        if (screenGrid.domElement.parentNode !== null) {
            document.body.removeChild(screenGrid.domElement);
            screenGrid.view.unlink();
        } else {
            document.body.appendChild(screenGrid.domElement);
            screenGrid.view.link();
        }
    },
    name: "debugger",
    css: {
        position: "absolute",
        color: "white",
        background: "rgba(0,0,0,0.5)",
        zIndex: 2,
        top: 0,
        left: 0
    }
});

debugAtlas(lm.__decal_atlas.atlas, { scale: new Vector2(0.125, 0.125) });

debugger_button.link();

document.body.appendChild(debugger_button.el);

const layerSwitcher = createLayerSwitcher(camera);

document.body.appendChild(layerSwitcher.domElement);

function onWindowResize() {

    const w = window.innerWidth;
    const h = window.innerHeight;

    camera.aspect = w / h;
    camera.updateProjectionMatrix();

    renderer.setSize(w, h);
}

onWindowResize();
window.addEventListener("resize", onWindowResize);
