feat: Add legacy shaders, model I/O, and project panel functionality
continuous-integration/drone/push Build is passing

- Introduced `legacy-shaders.js` containing vertex and fragment shaders for rendering.
- Implemented `model-io.js` for parsing and encoding GLB files, ensuring compatibility with glTF 2.0.
- Created `project-panel.js` to manage project resources and survey overlays, enhancing user interaction.
- Added `solar.js` for calculating solar positions based on UTC time, latitude, and longitude.
- Developed `three-renderer.js` as a standard backend for Three.js, managing scene rendering and material synchronization.
- Included a new GLB model `dum.glb` for testing and demonstration purposes.
- Established `data_checks.mjs` for validating model data and ensuring integrity of assets.
- Created `prepare_export.py` to facilitate Blender GLB exports, maintaining material and node references.
- Added `vendor_three.py` to download and configure a local version of Three.js for the project.
This commit is contained in:
martin.fencl
2026-10-22 01:04:21 +02:00
parent c5fcd79fe9
commit ba7790daba
85 changed files with 21 additions and 18 deletions
+2 -9
View File
@@ -1,10 +1,3 @@
<?php <?php
// Upload this whole folder, not only this file. No Node.js or database required.
header('Content-Type: text/html; charset=utf-8'); readfile(__DIR__ . '/dum_3D_slunce.html');
header('X-Content-Type-Options: nosniff');
$file = __DIR__ . '/app.html';
if (!is_readable($file)) {
http_response_code(500);
exit('Chybi app.html. Nahrajte celou slozku locenice.');
}
readfile($file);

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 145 KiB

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 153 KiB

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Before

Width:  |  Height:  |  Size: 325 KiB

After

Width:  |  Height:  |  Size: 325 KiB

Before

Width:  |  Height:  |  Size: 575 KiB

After

Width:  |  Height:  |  Size: 575 KiB

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 203 KiB

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

@@ -8,7 +8,7 @@ import json
import bpy import bpy
ROOT=Path(__file__).resolve().parent.parent ROOT=Path(__file__).resolve().parent.parent
SOURCE=ROOT/'locenice'/'models'/'dum.glb' SOURCE=ROOT/'models'/'dum.glb'
TARGET=Path(__file__).resolve().parent/'dum_locenice.blend' TARGET=Path(__file__).resolve().parent/'dum_locenice.blend'
if not SOURCE.is_file(): if not SOURCE.is_file():
raise FileNotFoundError(SOURCE) raise FileNotFoundError(SOURCE)
@@ -16,7 +16,7 @@ if TARGET.exists():
raise FileExistsError(f'Rename or remove {TARGET} before running again.') raise FileExistsError(f'Rename or remove {TARGET} before running again.')
bpy.ops.wm.read_factory_settings(use_empty=True) bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.ops.import_scene.gltf(filepath=str(SOURCE)) bpy.ops.import_scene.gltf(filepath=str(SOURCE))
defaults=json.loads((ROOT/'locenice'/'data'/'catalog.json').read_text())['defaults'] defaults=json.loads((ROOT/'data'/'catalog.json').read_text())['defaults']
scene=bpy.context.scene scene=bpy.context.scene
scene.unit_settings.system='METRIC' scene.unit_settings.system='METRIC'
scene.unit_settings.scale_length=1.0 scene.unit_settings.scale_length=1.0
@@ -60,6 +60,6 @@ Blender reorders glTF nodes, materials and textures. The included web manifest
is indexed for the supplied GLB; the preparation tool rebinds stable IDs. is indexed for the supplied GLB; the preparation tool rebinds stable IDs.
A changed wall or cabinet also requires checking web collision volumes / camera presets. A changed wall or cabinet also requires checking web collision volumes / camera presets.
''') ''')
scene['locenice_metadata_json']=json.dumps(json.loads((ROOT/'locenice'/'data'/'scene.json').read_text()),ensure_ascii=False) scene['locenice_metadata_json']=json.dumps(json.loads((ROOT/'data'/'scene.json').read_text()),ensure_ascii=False)
bpy.ops.wm.save_as_mainfile(filepath=str(TARGET)) bpy.ops.wm.save_as_mainfile(filepath=str(TARGET))
print('Saved:',TARGET) print('Saved:',TARGET)
+10
View File
@@ -0,0 +1,10 @@
<?php
// Upload this whole folder, not only this file. No Node.js or database required.
header('Content-Type: text/html; charset=utf-8');
header('X-Content-Type-Options: nosniff');
$file = __DIR__ . '/app.html';
if (!is_readable($file)) {
http_response_code(500);
exit('Chybi app.html. Nahrajte celou slozku locenice2.');
}
readfile($file);
@@ -1,8 +1,8 @@
import fs from 'node:fs'; import fs from 'node:fs';
import assert from 'node:assert/strict'; import assert from 'node:assert/strict';
import {parseGLB,encodeGLB} from '../locenice/js/model-io.js'; import {parseGLB,encodeGLB} from '../js/model-io.js';
import {solarPosition} from '../locenice/js/solar.js'; import {solarPosition} from '../js/solar.js';
const root=new URL('../locenice/',import.meta.url); const root=new URL('../',import.meta.url);
const bytes=new Uint8Array(fs.readFileSync(new URL('models/dum.glb',root))); const bytes=new Uint8Array(fs.readFileSync(new URL('models/dum.glb',root)));
const {gltf,binary}=parseGLB(bytes); const {gltf,binary}=parseGLB(bytes);
const catalog=JSON.parse(fs.readFileSync(new URL('data/catalog.json',root),'utf8')); const catalog=JSON.parse(fs.readFileSync(new URL('data/catalog.json',root),'utf8'));
@@ -102,7 +102,7 @@ def rebind(source,edited,output,metadata=None):
if __name__=='__main__': if __name__=='__main__':
parser=argparse.ArgumentParser(description=__doc__) parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('edited',type=Path);parser.add_argument('--out',type=Path,default=Path('prepared')) parser.add_argument('edited',type=Path);parser.add_argument('--out',type=Path,default=Path('prepared'))
parser.add_argument('--source',type=Path,default=ROOT/'locenice'/'models'/'dum.glb') parser.add_argument('--source',type=Path,default=ROOT/'models'/'dum.glb')
args=parser.parse_args() args=parser.parse_args()
print(rebind(args.source,args.edited,args.out)) print(rebind(args.source,args.edited,args.out))
print('Back up the website first. Replace models/dum.glb AND data/scene.json together.') print('Back up the website first. Replace models/dum.glb AND data/scene.json together.')
@@ -1,12 +1,12 @@
"""Optional: download pinned Three.js r180 locally, on a computer with internet. """Optional: download pinned Three.js r180 locally, on a computer with internet.
python tools/vendor_three.py python tools/vendor_three.py
Then upload the generated locenice/vendor folder AND data/app-config.json. Then upload the generated locenice2/vendor folder AND data/app-config.json.
Does not upload anything. No credentials or server-side installer required. Does not upload anything. No credentials or server-side installer required.
""" """
from pathlib import Path from pathlib import Path
from urllib.request import Request, urlopen from urllib.request import Request, urlopen
import json, re, hashlib import json, re, hashlib
ROOT=Path(__file__).resolve().parent.parent/'locenice' ROOT=Path(__file__).resolve().parent.parent
BASE='https://raw.githubusercontent.com/mrdoob/three.js/r180/' BASE='https://raw.githubusercontent.com/mrdoob/three.js/r180/'
VENDOR=ROOT/'vendor'/'three-r180' VENDOR=ROOT/'vendor'/'three-r180'
seen=set() seen=set()