playground: fully working (snapshots left as future work) and other quality of life improvements

This commit is contained in:
Yeicor
2025-07-25 17:59:40 +02:00
parent a63d018850
commit 8a435b5f1a
15 changed files with 289 additions and 81 deletions

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import {mdiLockQuestion} from "@mdi/js";
import {VBtn, VTooltip} from "vuetify/lib/components/index.mjs";
// @ts-expect-error
import SvgIcon from "@jamescoyle/vue-icon";
// @ts-expect-error

View File

@@ -2,6 +2,7 @@
import {ref} from "vue";
import {VBtn, VNavigationDrawer, VToolbar, VToolbarItems} from "vuetify/lib/components/index.mjs";
import {mdiChevronLeft, mdiChevronRight, mdiClose} from '@mdi/js'
// @ts-expect-error
import SvgIcon from '@jamescoyle/vue-icon';
const props = defineProps<{

View File

@@ -1,5 +1,5 @@
import {Buffer, Document, Scene, type Transform, WebIO} from "@gltf-transform/core";
import {unpartition, mergeDocuments} from "@gltf-transform/functions";
import {mergeDocuments, unpartition} from "@gltf-transform/functions";
let io = new WebIO();
export let extrasNameKey = "__yacv_name";
@@ -34,7 +34,9 @@ export async function mergePartial(url: string, name: string, document: Document
if (alreadyTried["draco"]) throw e; else alreadyTried["draco"] = true;
// WARNING: Draco decompression on web is really slow for non-trivial models! (it should work?)
let {KHRDracoMeshCompression} = await import("@gltf-transform/extensions")
// @ts-expect-error
let dracoDecoderWeb = await import("three/examples/jsm/libs/draco/draco_decoder.js");
// @ts-expect-error
let dracoEncoderWeb = await import("three/examples/jsm/libs/draco/draco_encoder.js");
io.registerExtensions([KHRDracoMeshCompression])
.registerDependencies({

View File

@@ -82,7 +82,7 @@ export function newAxes(doc: Document, size: Vector3, transform: Matrix4) {
* The grid is built as a box of triangles (representing lines) looking to the inside of the box.
* This ensures that only the back of the grid is always visible, regardless of the camera position.
*/
export async function newGridBox(doc: Document, size: Vector3, baseTransform: Matrix4, divisions = 10) {
export function newGridBox(doc: Document, size: Vector3, baseTransform: Matrix4, divisions = 10) {
// Create transformed positions for the inner faces of the box
let allPositions: number[] = [];
let allIndices: number[] = [];

View File

@@ -1,5 +1,5 @@
import {type Ref} from 'vue';
import {Document} from '@gltf-transform/core';
import {Buffer, Document, Scene} from '@gltf-transform/core';
import {extrasNameKey, extrasNameValueHelpers, mergeFinalize, mergePartial, removeModel, toBuffer} from "./gltf";
import {newAxes, newGridBox} from "./helpers";
import {Vector3} from "three/src/math/Vector3.js"
@@ -80,7 +80,19 @@ export class SceneMgr {
private static async reloadHelpers(sceneUrl: Ref<string>, document: Document, reloadScene: boolean): Promise<Document> {
let bb = SceneMgr.getBoundingBox(document);
if (!bb) return document;
if (!bb) return document; // Empty document, no helpers to show
// If only the helpers remain, go back to the empty scene
let noOtherModels = true;
for (let elem of document.getGraph().listEdges().map(e => e.getChild())) {
if (elem.getExtras() && !(elem instanceof Scene) && !(elem instanceof Buffer) &&
elem.getExtras()[extrasNameKey] !== extrasNameValueHelpers) {
// There are other elements in the document, so we can show the helpers
noOtherModels = false;
break;
}
}
if (noOtherModels) return await removeModel(extrasNameValueHelpers, document);
// Create the helper axes and grid box
let helpersDoc = new Document();

View File

@@ -43,21 +43,37 @@ export async function settings() {
"12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg=="),
// Playground settings
code: "", // Automatically loaded and executed code for the playground
pg_code: "", // Automatically loaded and executed code for the playground
pg_code_url: "", // URL to load the code from (overrides pg_code)
pg_opacity_loading: -1, // Opacity of the code during first load and run (< 0 is 0.0 if preload and 0.9 if not)
pg_opacity_loaded: 0.9, // Opacity of the code after it has been run for the first time
};
// Auto-override any settings from the URL
// Auto-override any settings from the URL (either GET parameters or hash)
const url = new URL(window.location.href);
url.searchParams.forEach((value, key) => {
if (key in settings) (settings as any)[key] = parseSetting(key, value, settings);
})
if (url.hash.length > 0) { // Hash has bigger limits as it is not sent to the server
const hash = url.hash.slice(1);
const hashParams = new URLSearchParams(hash);
hashParams.forEach((value, key) => {
if (key in settings) (settings as any)[key] = parseSetting(key, value, settings);
});
}
// Auto-decompress the code
if (settings.code.length > 0) {
// Grab the code from the URL if it is set
if (settings.pg_code_url.length > 0) {
// If the code URL is set, override the code
try {
settings.code = ungzip(b66Decode(settings.code), {to: 'string'});
const response = await fetch(settings.pg_code_url);
if (response.ok) {
settings.pg_code = await response.text();
} else {
console.warn("Failed to load code from URL:", settings.pg_code_url);
}
} catch (error) {
console.warn("Failed to decompress code (assuming raw code):", error);
console.error("Error fetching code from URL:", settings.pg_code_url, error);
}
}
@@ -65,7 +81,7 @@ export async function settings() {
for (let i = 0; i < settings.preload.length; i++) {
let url = settings.preload[i];
if (url === '<auto>') {
if (settings.code != "") { // <auto> means no preload URL if code is set
if (settings.pg_code != "") { // <auto> means no preload URL if code is set
settings.preload = settings.preload.slice(0, i).concat(settings.preload.slice(i + 1));
continue; // Skip this preload URL
}
@@ -82,6 +98,20 @@ export async function settings() {
}
settings.preload[i] = url;
}
// Auto-decompress the code and other playground settings
if (settings.pg_code.length > 0) {
try {
settings.pg_code = ungzip(b66Decode(settings.pg_code), {to: 'string'});
} catch (error) {
console.warn("Failed to decompress code (assuming raw code):", error);
}
if (settings.pg_opacity_loading < 0) {
// If the opacity is not set, use 0.0 if preload is set, otherwise 0.9
settings.pg_opacity_loading = settings.preload.length > 0 ? 0.0 : 0.9;
}
}
settingsCache = settings;
return settings;
}