cleaning up

This commit is contained in:
Yeicor
2024-02-16 20:35:27 +01:00
parent 0545d8fbe2
commit c10e5f0896
14 changed files with 58 additions and 105 deletions

View File

@@ -1,58 +1,42 @@
<script setup lang="ts">
import {defineAsyncComponent, ref, Ref} from "vue";
import Sidebar from "./Sidebar.vue";
import Loading from "./Loading.vue";
import ModelViewerOverlay from "./ModelViewerOverlay.vue";
import Tools from "./Tools.vue";
import {
VExpansionPanel,
VExpansionPanels,
VExpansionPanelText,
VExpansionPanelTitle,
VLayout,
VMain,
VToolbarTitle
} from "vuetify/lib/components";
import type {ModelViewerInfo} from "./ModelViewerWrapper.vue";
import Sidebar from "./models/Sidebar.vue";
import Loading from "./viewer/Loading.vue";
import ModelViewerOverlay from "./viewer/ModelViewerOverlay.vue";
import Tools from "./tools/Tools.vue";
import Models from "./models/Models.vue";
import {VLayout, VMain, VToolbarTitle} from "vuetify/lib/components";
import type {ModelViewerInfo} from "./viewer/ModelViewerWrapper.vue";
import {settings} from "./tools/settings";
// NOTE: The ModelViewer library is big, so we split it and import it asynchronously
// NOTE: The ModelViewer library is big (THREE.js), so we split it and import it asynchronously
const ModelViewerWrapper = defineAsyncComponent({
loader: () => import('./ModelViewerWrapper.vue'),
loader: () => import('./viewer/ModelViewerWrapper.vue'),
loadingComponent: Loading,
delay: 0,
});
// Open models by default on wide screens
let openSidebarsDefault: Ref<boolean> = ref(window.innerWidth > 1200);
let openSidebarsByDefault: Ref<boolean> = ref(window.innerWidth > 1200);
let modelViewerInfo: Ref<typeof ModelViewerInfo | null> = ref(null);
let modelSrc: Ref<string | Uint8Array> = ref(settings.preloadModels[0]);
</script>
<template>
<v-layout full-height>
<!-- The main content of the app is the model-viewer with the SVG "2D" overlay -->
<v-main id="main">
<model-viewer-wrapper @load-viewer="(args) => {
console.log('Model-Viewer finished loading:', args)
modelViewerInfo = args
}"/>
<model-viewer-wrapper :src="modelSrc" @load-viewer="(args) => modelViewerInfo = args"/>
<model-viewer-overlay v-if="modelViewerInfo"/>
</v-main>
<!-- The left collapsible sidebar has the list of models -->
<sidebar :opened-init="openSidebarsDefault" side="left">
<sidebar :opened-init="openSidebarsByDefault" side="left">
<template #toolbar>
<v-toolbar-title>Models</v-toolbar-title>
</template>
<v-expansion-panels>
<v-expansion-panel key="model-id">
<v-expansion-panel-title>? F ? E ? V | Model Name</v-expansion-panel-title>
<v-expansion-panel-text>
Content
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
<models :modelViewerInfo="modelViewerInfo"/>
</sidebar>
<!-- The right collapsible sidebar has the list of tools -->
<sidebar :opened-init="openSidebarsDefault" side="right" :width="120">
<sidebar :opened-init="openSidebarsByDefault" side="right" :width="120">
<template #toolbar>
<v-toolbar-title>Tools</v-toolbar-title>
</template>
@@ -67,12 +51,4 @@ html, body {
height: 100%;
overflow: hidden;
}
</style>
<!--suppress CssUnusedSymbol -->
<style scoped>
/* Fix bug in hidden expansion panel text next to active expansion panel */
.v-expansion-panel-title--active + .v-expansion-panel-text {
display: flex !important;
}
</style>

View File

@@ -19,7 +19,6 @@ const vuetify = createVuetify({
const app = createApp(App)
app.use(vuetify)
// noinspection JSUnresolvedReference
app.mount('body')
// Start non-blocking loading of Vuetify and icon styles

24
src/models/Models.vue Normal file
View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
import {VExpansionPanel, VExpansionPanels, VExpansionPanelText, VExpansionPanelTitle} from "vuetify/lib/components";
const props = defineProps({
modelViewerInfo: Object
});
</script>
<template>
<v-expansion-panels>
<v-expansion-panel key="model-id">
<v-expansion-panel-title>? F ? E ? V | Model Name</v-expansion-panel-title>
<v-expansion-panel-text>Content</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
</template>
<!--suppress CssUnusedSymbol -->
<style scoped>
/* Fix bug in hidden expansion panel text next to active expansion panel */
.v-expansion-panel-title--active + .v-expansion-panel-text {
display: flex !important;
}
</style>

View File

@@ -1,53 +0,0 @@
import * as OrientationGizmoRaw from "three-orientation-gizmo/src/OrientationGizmo";
import * as THREE from "three";
import {ModelScene} from "@google/model-viewer/lib/three-components/ModelScene";
window.THREE = THREE // HACK: Required for the gizmo to work
export class OrientationGizmo {
element: OrientationGizmoRaw
constructor(scene: ModelScene) {
// noinspection SpellCheckingInspection
this.element = new OrientationGizmoRaw(scene.camera, {
size: 120,
bubbleSizePrimary: 12,
bubbleSizeSeconday: 10,
fontSize: "14px"
});
// Place in the top right corner
this.element.style.position = "absolute";
this.element.style.top = "0px";
this.element.style.right = "0px";
this.element.style.zIndex = "1000";
// HACK: Swap axes to match A-Frame
for (let swap of [["y", "-z"], ["z", "-y"], ["z", "-z"]]) {
let indexA = this.element.bubbles.findIndex((bubble) => bubble.axis == swap[0])
let indexB = this.element.bubbles.findIndex((bubble) => bubble.axis == swap[1])
let dirA = this.element.bubbles[indexA].direction.clone();
let dirB = this.element.bubbles[indexB].direction.clone();
this.element.bubbles[indexA].direction.copy(dirB);
this.element.bubbles[indexB].direction.copy(dirA);
}
// Append and listen for events
this.element.onAxisSelected = (axis: { direction: { x: any; y: any; z: any; }; }) => {
let lookFrom = scene.getCamera().position.clone();
let lookAt = scene.getTarget().clone().add(scene.target.position);
let magnitude = lookFrom.clone().sub(lookAt).length()
let direction = new THREE.Vector3(axis.direction.x, axis.direction.y, axis.direction.z);
let newLookFrom = lookAt.clone().add(direction.clone().multiplyScalar(magnitude));
//console.log("New camera position", newLookFrom)
scene.getCamera().position.copy(newLookFrom);
scene.getCamera().lookAt(lookAt);
scene.queueRender();
}
}
install() {
document.body.appendChild(this.element);
}
update() {
this.element.update();
}
}

View File

@@ -1,7 +1,7 @@
// These are the default values for the settings, which are overridden below
export const settings = {
// @ts-ignore
preloadModels: [new URL('../assets/fox.glb', import.meta.url).href, "ws://localhost:8080/api/updates"],
preloadModels: [new URL('../../assets/fox.glb', import.meta.url).href, new URL('../../assets/logo.glbs', import.meta.url).href, "ws://localhost:8080"],
// ModelViewer settings
autoplay: true,
arModes: 'webxr scene-viewer quick-look',

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import {settings} from "./settings";
import {settings} from "../tools/settings";
import {ModelViewerElement} from '@google/model-viewer';
import {onMounted, ref} from "vue";
import {$scene} from "@google/model-viewer/lib/model-viewer-base";
@@ -11,14 +11,17 @@ let _ = ModelViewerElement; // HACK: Needed to avoid tree shaking
const emit = defineEmits(['load-viewer']);
const props = defineProps({
src: String
});
let viewer = ref<ModelViewerElement | null>(null);
onMounted(() => {
console.log('ModelViewerWrapper mounted');
viewer.value.addEventListener('load', () =>
emit('load-viewer', {
viewer: viewer.value,
scene: viewer.value[$scene] as ModelScene,
})
} as ModelViewerInfo)
);
});
@@ -27,7 +30,7 @@ onMounted(() => {
<template>
<!--suppress VueMissingComponentImportInspection -->
<model-viewer ref="viewer"
style="width: 100%; height: 100%" :src="settings.preloadModels[0]" alt="The 3D model(s)" camera-controls
style="width: 100%; height: 100%" :src="props.src" alt="The 3D model(s)" camera-controls
camera-orbit="30deg 75deg auto" max-camera-orbit="Infinity 180deg auto"
min-camera-orbit="-Infinity 0deg auto"
:exposure="settings.exposure" :shadow-intensity="settings.shadowIntensity" interaction-prompt="none"

View File

@@ -23,7 +23,7 @@ show_object = show
def _get_app() -> web.Application:
"""Required by aiohttp-devtools"""
logging.basicConfig(level=logging.DEBUG)
from logo.logo import build_logo
from logo import build_logo
server.show_cad(build_logo(), 'logo')
return server.app

View File

@@ -1,15 +1,19 @@
from typing import AsyncGenerator
async def glb_sequence_to_glbs(glb_sequence: AsyncGenerator[bytes, None]) -> AsyncGenerator[bytes, None]:
async def glb_sequence_to_glbs(glb_sequence: AsyncGenerator[bytes, None], count: int = -1) -> AsyncGenerator[bytes, None]:
"""Converts a sequence of GLB files into a single GLBS file.
This is a streaming response in the custom GLBS format which consists of the "GLBS" magic text followed by
a sequence of GLB files, each with a 4-byte little-endian length prefix."""
a count of GLB files (0xffffffff if unknown) and a sequence of GLB files, each with a length prefix. All numbers are
4-byte little-endian unsigned integers."""
# Write the magic text
yield b'GLBS'
# Write the count
yield count.to_bytes(4, 'little')
# Write the GLB files
async for glb in glb_sequence:
# Write the length prefix
@@ -26,7 +30,7 @@ if __name__ == '__main__':
yield b'glb00001'
yield b'glb2'
async for chunk in glb_sequence_to_glbs(glb_sequence()):
async for chunk in glb_sequence_to_glbs(glb_sequence(), 2):
print(chunk)
asyncio.run(test_glb_sequence_to_glbs())

View File

@@ -202,7 +202,7 @@ class Server:
if logger.isEnabledFor(logging.INFO):
# noinspection PyTypeChecker
export_data = tqdm.asyncio.tqdm(export_data, total=total_parts)
async for chunk in glb_sequence_to_glbs(export_data):
async for chunk in glb_sequence_to_glbs(export_data, total_parts):
await response.write(chunk)
finally:
# Close the export data subscription
@@ -308,7 +308,7 @@ class Server:
if logger.isEnabledFor(logging.INFO):
# noinspection PyTypeChecker
glbs_parts = tqdm.asyncio.tqdm(glbs_parts, total=total_export_size, position=0)
glbs_parts = glb_sequence_to_glbs(glbs_parts)
glbs_parts = glb_sequence_to_glbs(glbs_parts, total_export_size)
async for glbs_part in glbs_parts:
yield glbs_part
finally: