Skip to content

Commit

Permalink
Merge pull request #15 from mml-io/refactor/logging-improvements
Browse files Browse the repository at this point in the history
Avatar Exporter Logging Improvements
  • Loading branch information
TheCodeTherapy authored Nov 30, 2023
2 parents fc090e7 + a35e506 commit 7afbc30
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 30 deletions.
65 changes: 64 additions & 1 deletion tools/gltf-avatar-exporter/public/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ html {

#neQuadrant {
/* Upper Right */
color: white;
}

#swQuadrant {
Expand Down Expand Up @@ -63,11 +64,13 @@ html {
.log-element {
padding: 5px;
background-color: #333;
border-top: 1px solid black;
border-top: 1px solid #111;
padding-left: 31px;
}

.log-element.info {
color: #ffffff;
border-left: 1px solid #333;
}

.log-element.warn {
Expand Down Expand Up @@ -95,3 +98,63 @@ html {
padding-left: 10px;
}

#swQuadrant .foldable-log-container {
padding-left: 7px;
background-color: #333;
border-left: 1px solid white;
}

#swQuadrant .foldable-log-top-container {
font-family: monospace;
background-color: #333;
}

/* Styles for foldable containers */
#swQuadrant .foldable-log-container,
#swQuadrant .foldable-log-top-container {
padding: 5px;
border-top: 1px solid black;
border-left: 1px solid white;
border-radius: 0px;
overflow: hidden;
}

/* Styles for toggle buttons */
#swQuadrant .foldable-log-container > button,
#swQuadrant .foldable-log-top-container > button {
background-color: #333333;
color: #eeeeee;
padding: 0px;
width: 100%;
text-align: left;
border: none;
outline: none;
cursor: pointer;
transition: background-color 0.3s;
font-family: monospace;
}

/* Hover styles for toggle button */
#swQuadrant .foldable-log-container > button:hover,
#swQuadrant .foldable-log-top-container > button:hover {
background-color: #555555;
color: #ffffff;
}

/* Styles for hidden content */
#swQuadrant .foldable-log-content {
display: none;
padding: 3px 0px 3px 7px;
}

/* Styles for expanded sections */
#swQuadrant .foldable-log-container.expanded > .foldable-log-content,
#swQuadrant .foldable-log-top-container.expanded > .foldable-log-content {
display: block;
}

/* Specific rule to hide all nested elements inside a non-expanded top container */
#swQuadrant .foldable-log-top-container:not(.expanded) .foldable-log-container,
#swQuadrant .foldable-log-top-container:not(.expanded) .foldable-log-container .foldable-log-content {
display: none;
}
18 changes: 17 additions & 1 deletion tools/gltf-avatar-exporter/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,26 @@ class App {
this.logger,
this.modelLoader,
(group: Group, name: string) => {
this.logger.logFoldableData("skeleton data", ["skeleton data"]);
group.traverse((child) => {
if (child.type === "Bone") {
const bonePosition = child.position.toArray().toString();
const boneRotation = child.rotation.toArray().toString();
const boneQuaternion = child.quaternion.toArray().toString();
const boneScale = child.scale.toArray().toString();
this.logger.logFoldableData("skeleton data", [`${child.name}`, `pos: ${bonePosition}`]);
this.logger.logFoldableData("skeleton data", [`${child.name}`, `rot: ${boneRotation}`]);
this.logger.logFoldableData("skeleton data", [
`${child.name}`,
`quat: ${boneQuaternion}`,
]);
this.logger.logFoldableData("skeleton data", [`${child.name}`, `scale: ${boneScale}`]);
}
});
this.exportView.setImportedModelGroup(group, name);
},
);
this.exportView = new ExportView(this.logger, this.modelLoader);
this.exportView = new ExportView(this.logger, this.modelLoader, this.timeManager);
this.animationView = new AnimationView(this.modelLoader, (clip) => {
this.exportView.setAnimationClip(clip);
});
Expand Down
137 changes: 115 additions & 22 deletions tools/gltf-avatar-exporter/src/logger/LoggerView.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,161 @@
import { StepResult } from "../scene/correction-steps/types";

function createLogElement(level: string, logString: string) {
function createLogElement(level: string, logString: string, showLevel: boolean = true) {
const logElement = document.createElement("div");
logElement.classList.add("log-element");
logElement.classList.add(level);
logElement.textContent = `(${level}) ${logString}`;
logElement.textContent = showLevel ? `(${level}) ${logString}` : `${logString}`;
return logElement;
}

function createStepResultElement(stepName: string, stepResult: StepResult) {
function createStepResultElement(stepResult: StepResult): HTMLElement {
const stepResultElement = document.createElement("div");
stepResultElement.classList.add("step-result");

if (!stepResult.didApply) {
stepResultElement.classList.add("skipped");
}

const rowElement = document.createElement("div");
rowElement.classList.add("step-result-row");

const titleElement = document.createElement("span");
titleElement.textContent = `${stepResult.didApply ? "🔨(Applied)" : "⏭️ (Skipped)"} ${stepName}${
stepResult.topLevelMessage ? ` - ${stepResult.topLevelMessage.message}` : ""
}`;
rowElement.appendChild(titleElement);

const logElementHolder = document.createElement("div");
logElementHolder.classList.add("step-result-inner-logs");

// Add logs if any
if (stepResult.logs) {
for (const logString of stepResult.logs) {
const logElement = createLogElement(logString.level, logString.message);
const logElementHolder = document.createElement("div");
logElementHolder.classList.add("step-result-inner-logs");

for (const log of stepResult.logs) {
const logElement = createLogElement(log.level, log.message);
logElementHolder.appendChild(logElement);
}

stepResultElement.appendChild(logElementHolder);
}

stepResultElement.appendChild(rowElement);
stepResultElement.appendChild(logElementHolder);
return stepResultElement;
}

export class LoggerView {
private logElement: HTMLDivElement;
private foldableLogs: Map<string, { container: HTMLDivElement; content: string[] }>;

constructor() {
this.logElement = document.getElementById("swQuadrant") as HTMLDivElement;
this.foldableLogs = new Map();
this.reset();
}

public reset() {
// Remove all children
while (this.logElement.firstChild) {
this.logElement.removeChild(this.logElement.firstChild);
}
this.log("⬆️ Drag & drop your asset onto the quadrant above to load it ⬆️");
}

public logStepResult(stepName: string, stepResult: StepResult) {
const stepResultElement = createStepResultElement(stepName, stepResult);
this.logElement.appendChild(stepResultElement);
// Determine the folder title and icon based on the step result
const folderTitle = `${stepResult.didApply ? "🔨 (Applied)" : "⏭️ (Skipped)"} - ${
stepResult.topLevelMessage ? stepResult.topLevelMessage.message : stepName
}`;

// Only create a foldable container if there are logs
if (stepResult.logs && stepResult.logs.length > 0) {
let stepContainerEntry = this.foldableLogs.get(folderTitle);
if (!stepContainerEntry) {
const stepContainer = document.createElement("div");
stepContainer.classList.add("foldable-log-container");
this.logElement.appendChild(stepContainer);

// Create the foldable container with the folder title
this.createFoldableContainer(stepContainer, folderTitle);

// If the step did apply, unfold the container by default
if (stepResult.didApply) {
stepContainer.classList.add("expanded");
}

stepContainerEntry = { container: stepContainer, content: [] };
this.foldableLogs.set(folderTitle, stepContainerEntry);
}

// Create the step result element without the top level message
const stepResultElement = createStepResultElement(stepResult);
stepContainerEntry.container
.querySelector(".foldable-log-content")!
.appendChild(stepResultElement);
} else {
// Log the topLevelMessage directly without a foldable container
const logElement = createLogElement(stepResult.topLevelMessage.level, folderTitle, false);
this.logElement.appendChild(logElement);
}
}

public log(dataString: string) {
console.log("log", dataString);
const logElement = createLogElement("info", dataString);
this.logElement.appendChild(logElement);
}

public logFoldableData(
id: string,
dataStrings: string[],
logLevel: "info" | "warn" | "error" = "info",
) {
if (!dataStrings || dataStrings.length === 0) {
return;
}

let topLevelEntry = this.foldableLogs.get(id);
if (!topLevelEntry) {
const topLevelContainer = document.createElement("div");
topLevelContainer.classList.add("foldable-log-top-container");
this.logElement.appendChild(topLevelContainer);

topLevelEntry = { container: topLevelContainer, content: [] };
this.foldableLogs.set(id, topLevelEntry);
}

let currentContainer = topLevelEntry.container;

if (dataStrings.length > 1 && !topLevelEntry.container.querySelector("button")) {
this.createFoldableContainer(topLevelEntry.container, id);
}

dataStrings.forEach((label, index) => {
if (index < dataStrings.length - 1) {
let nestedContainer = currentContainer.querySelector(
`.foldable-log-container[data-label="${label}"]`,
);
if (!nestedContainer) {
nestedContainer = document.createElement("div") as HTMLElement;
nestedContainer.classList.add("foldable-log-container");
nestedContainer.setAttribute("data-label", label);
this.createFoldableContainer(nestedContainer as HTMLElement, label);
currentContainer.appendChild(nestedContainer);
}
currentContainer = nestedContainer.querySelector(".foldable-log-content")!;
}
});

const logElement = createLogElement(logLevel, dataStrings[dataStrings.length - 1]);
currentContainer.appendChild(logElement);
}

private createFoldableContainer(container: HTMLElement, label: string): void {
const toggleButton = document.createElement("button");
toggleButton.textContent = "📁 " + label;
toggleButton.onclick = () => {
container.classList.toggle("expanded");
toggleButton.textContent = container.classList.contains("expanded")
? `📂 ${label}`
: `📁 ${label}`;
};

const contentContainer = document.createElement("div");
contentContainer.classList.add("foldable-log-content");

while (container.firstChild) {
contentContainer.appendChild(container.firstChild);
}

container.appendChild(toggleButton);
container.appendChild(contentContainer);
}
}
4 changes: 4 additions & 0 deletions tools/gltf-avatar-exporter/src/scene/AnimationView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ export class AnimationView extends QuadrantScene {
const skeletonHelper = new SkeletonHelper(rootBone);
this.scene.add(skeletonHelper);

setTimeout(() => {
this.fitCameraToGeometry(skeletonHelper.geometry);
}, 1000);

const firstAnimation = animations[0];
const animationMixer = new AnimationMixer(rootBone);
animationMixer.clipAction(firstAnimation).play();
Expand Down
5 changes: 4 additions & 1 deletion tools/gltf-avatar-exporter/src/scene/ExportView.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { TimeManager } from "@mml-io/3d-web-client-core";
import { AnimationClip, AnimationMixer, Group, SkinnedMesh, Vector3 } from "three";
import { GLTFExporter } from "three/examples/jsm/exporters/GLTFExporter.js";

Expand Down Expand Up @@ -31,6 +32,7 @@ export class ExportView extends QuadrantScene {
constructor(
private logger: LoggerView,
private modelLoader: ModelLoader,
private timeManager: TimeManager,
) {
super("neQuadrant");
this.lights = new Lights(this.camOffset);
Expand Down Expand Up @@ -91,6 +93,7 @@ export class ExportView extends QuadrantScene {
}
this.currentModel = group;
this.scene.add(this.currentModel);
this.fitCameraToGroup(this.currentModel);

this.setAnimationClip(this.currentAnimationClip);
} else {
Expand Down Expand Up @@ -135,7 +138,7 @@ export class ExportView extends QuadrantScene {

public update() {
if (this.loadedAnimationState !== null) {
this.loadedAnimationState.animationMixer.setTime(Date.now() / 1000);
this.loadedAnimationState.animationMixer.update(this.timeManager.deltaTime);
}
super.update();
}
Expand Down
1 change: 1 addition & 0 deletions tools/gltf-avatar-exporter/src/scene/ImportView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export class ImportView extends QuadrantScene {
}
this.currentModel = group;
this.scene.add(this.currentModel);
setTimeout(() => this.fitCameraToGroup(this.currentModel!), 1000);
} else {
console.error("Unable to load model");
return;
Expand Down
Loading

0 comments on commit 7afbc30

Please sign in to comment.