The model viewer can load NJ and XJ files again.

This commit is contained in:
Daan Vanden Bosch 2019-08-19 23:49:40 +02:00
parent 5571f6b1a8
commit c7cbf68411
7 changed files with 118 additions and 35 deletions

View File

@ -1,4 +1,5 @@
.core_Button { .core_Button {
display: inline-block;
box-sizing: border-box; box-sizing: border-box;
background-color: #404040; background-color: #404040;
height: 26px; height: 26px;

View File

@ -2,6 +2,8 @@ import { create_el } from "./dom";
import { View } from "./View"; import { View } from "./View";
import "./Button.css"; import "./Button.css";
function dummy_function(): void {}
export class Button extends View { export class Button extends View {
element: HTMLButtonElement = create_el("button", "core_Button"); element: HTMLButtonElement = create_el("button", "core_Button");
@ -9,5 +11,9 @@ export class Button extends View {
super(); super();
this.element.textContent = text; this.element.textContent = text;
this.element.onclick = () => this.on_click();
} }
on_click: () => void = dummy_function;
} }

View File

@ -0,0 +1,9 @@
.core_FileInput_input {
overflow: hidden;
clip: rect(0, 0, 0, 0);
position: absolute;
width: 1px;
height: 1px;
border: none;
padding: 0;
}

View File

@ -0,0 +1,29 @@
import { create_el } from "./dom";
import { View } from "./View";
import "./FileInput.css";
import "./Button.css";
function dummy_function(): void {}
export class FileInput extends View {
private input: HTMLInputElement = create_el("input", "core_FileInput_input");
element: HTMLLabelElement = create_el("label", "core_Button");
constructor(text: string, accept: string = "") {
super();
this.input.type = "file";
this.input.accept = accept;
this.input.onchange = () => {
if (this.input.files && this.input.files.length) {
this.on_files_chosen([...this.input.files!]);
}
};
this.element.textContent = text;
this.element.append(this.input);
}
on_files_chosen: (files: File[]) => void = dummy_function;
}

View File

@ -13,5 +13,6 @@ export abstract class View implements Disposable {
dispose(): void { dispose(): void {
this.element.remove(); this.element.remove();
this.disposables.forEach(d => d.dispose()); this.disposables.forEach(d => d.dispose());
this.disposables.splice(0, this.disposables.length);
} }
} }

View File

@ -1,12 +1,13 @@
import { create_el } from "../../core/gui/dom"; import { create_el } from "../../core/gui/dom";
import { ResizableView } from "../../core/gui/ResizableView"; import { ResizableView } from "../../core/gui/ResizableView";
import { ToolBar } from "../../core/gui/ToolBar"; import { ToolBar } from "../../core/gui/ToolBar";
import { Button } from "../../core/gui/Button";
import "./ModelView.css"; import "./ModelView.css";
import { model_store } from "../stores/ModelStore"; import { model_store } from "../stores/ModelStore";
import { Observable } from "../../core/observable/Observable"; import { Observable } from "../../core/observable/Observable";
import { RendererView } from "../../core/gui/RendererView"; import { RendererView } from "../../core/gui/RendererView";
import { ModelRenderer } from "../rendering/ModelRenderer"; import { ModelRenderer } from "../rendering/ModelRenderer";
import { View } from "../../core/gui/View";
import { FileInput } from "../../core/gui/FileInput";
const MODEL_LIST_WIDTH = 100; const MODEL_LIST_WIDTH = 100;
const ANIMATION_LIST_WIDTH = 150; const ANIMATION_LIST_WIDTH = 150;
@ -14,8 +15,7 @@ const ANIMATION_LIST_WIDTH = 150;
export class ModelView extends ResizableView { export class ModelView extends ResizableView {
element = create_el("div", "viewer_ModelView"); element = create_el("div", "viewer_ModelView");
private tool_bar = this.disposable(new ToolBar(new Button("Open file..."))); private tool_bar_view = this.disposable(new ToolBarView());
private container_element = create_el("div", "viewer_ModelView_container"); private container_element = create_el("div", "viewer_ModelView_container");
private model_list_view = this.disposable( private model_list_view = this.disposable(
new ModelSelectListView(model_store.models, model_store.current_model), new ModelSelectListView(model_store.models, model_store.current_model),
@ -36,7 +36,7 @@ export class ModelView extends ResizableView {
this.renderer_view.element, this.renderer_view.element,
); );
this.element.append(this.tool_bar.element, this.container_element); this.element.append(this.tool_bar_view.element, this.container_element);
model_store.current_model.set(model_store.models[5]); model_store.current_model.set(model_store.models[5]);
} }
@ -44,7 +44,7 @@ export class ModelView extends ResizableView {
resize(width: number, height: number): this { resize(width: number, height: number): this {
super.resize(width, height); super.resize(width, height);
const container_height = Math.max(0, height - this.tool_bar.height); const container_height = Math.max(0, height - this.tool_bar_view.height);
this.model_list_view.resize(MODEL_LIST_WIDTH, container_height); this.model_list_view.resize(MODEL_LIST_WIDTH, container_height);
this.animation_list_view.resize(ANIMATION_LIST_WIDTH, container_height); this.animation_list_view.resize(ANIMATION_LIST_WIDTH, container_height);
@ -57,6 +57,23 @@ export class ModelView extends ResizableView {
} }
} }
class ToolBarView extends View {
private readonly open_file_button = new FileInput("Open file...", ".nj, .xj");
private readonly tool_bar = this.disposable(new ToolBar(this.open_file_button));
readonly element = this.tool_bar.element;
get height(): number {
return this.tool_bar.height;
}
constructor() {
super();
this.open_file_button.on_files_chosen = files => model_store.load_file(files[0]);
}
}
class ModelSelectListView<T extends { name: string }> extends ResizableView { class ModelSelectListView<T extends { name: string }> extends ResizableView {
element = create_el("ul", "viewer_ModelSelectListView"); element = create_el("ul", "viewer_ModelSelectListView");

View File

@ -1,14 +1,20 @@
import { Clock } from "three"; import { Clock } from "three";
import { ArrayBufferCursor } from "../../../core/data_formats/cursor/ArrayBufferCursor"; import { ArrayBufferCursor } from "../../../core/data_formats/cursor/ArrayBufferCursor";
import { Endianness } from "../../../core/data_formats/Endianness"; import { Endianness } from "../../../core/data_formats/Endianness";
import { NjMotion } from "../../../core/data_formats/parsing/ninja/motion"; import { NjMotion, parse_njm } from "../../../core/data_formats/parsing/ninja/motion";
import { NjObject, parse_nj } from "../../../core/data_formats/parsing/ninja"; import { NjObject, parse_nj, parse_xj } from "../../../core/data_formats/parsing/ninja";
import { CharacterClassModel } from "../domain/CharacterClassModel"; import { CharacterClassModel } from "../domain/CharacterClassModel";
import { CharacterClassAnimation } from "../domain/CharacterClassAnimation"; import { CharacterClassAnimation } from "../domain/CharacterClassAnimation";
import { Observable } from "../../core/observable/Observable"; import { Observable } from "../../core/observable/Observable";
import { get_player_data } from "../../../viewer/loading/player"; import { get_player_data } from "../../../viewer/loading/player";
import { Disposable } from "../../core/gui/Disposable"; import { Disposable } from "../../core/gui/Disposable";
import { read_file } from "../../../core/read_file";
import { create_animation_clip } from "../../../core/rendering/conversion/ninja_animation";
import { parse_xvm } from "../../../core/data_formats/parsing/ninja/texture";
import { xvm_to_textures } from "../../../core/rendering/conversion/ninja_textures";
import Logger = require("js-logger");
const logger = Logger.get("viewer/stores/ModelStore");
const nj_object_cache: Map<string, Promise<NjObject>> = new Map(); const nj_object_cache: Map<string, Promise<NjObject>> = new Map();
const nj_motion_cache: Map<number, Promise<NjMotion>> = new Map(); const nj_motion_cache: Map<number, Promise<NjMotion>> = new Map();
@ -98,17 +104,31 @@ class ModelStore implements Disposable {
// }; // };
// TODO: notify user of problems. // TODO: notify user of problems.
// load_file = async (file: File) => { load_file = async (file: File) => {
// try { try {
// const buffer = await read_file(file); this.current_model.set(undefined);
// const cursor = new ArrayBufferCursor(buffer, Endianness.Little); this.current_nj_data.set(undefined);
// this.current_animation.set(undefined);
// if (file.name.endsWith(".nj")) {
// const model = parse_nj(cursor)[0]; const buffer = await read_file(file);
// this.set_selected(model, true); const cursor = new ArrayBufferCursor(buffer, Endianness.Little);
// } else if (file.name.endsWith(".xj")) {
// const model = parse_xj(cursor)[0]; if (file.name.endsWith(".nj")) {
// this.set_selected(model, false); const nj_object = parse_nj(cursor)[0];
this.current_nj_data.set({
nj_object,
bone_count: nj_object.bone_count(),
has_skeleton: true,
});
} else if (file.name.endsWith(".xj")) {
const nj_object = parse_xj(cursor)[0];
this.current_nj_data.set({
nj_object,
bone_count: 0,
has_skeleton: false,
});
// } else if (file.name.endsWith(".njm")) { // } else if (file.name.endsWith(".njm")) {
// if (this.current_model) { // if (this.current_model) {
// const njm = parse_njm(cursor, this.current_bone_count); // const njm = parse_njm(cursor, this.current_bone_count);
@ -119,13 +139,13 @@ class ModelStore implements Disposable {
// const xvm = parse_xvm(cursor); // const xvm = parse_xvm(cursor);
// this.set_textures(xvm_to_textures(xvm)); // this.set_textures(xvm_to_textures(xvm));
// } // }
// } else { } else {
// logger.error(`Unknown file extension in filename "${file.name}".`); logger.error(`Unknown file extension in filename "${file.name}".`);
// } }
// } catch (e) { } catch (e) {
// logger.error("Couldn't read file.", e); logger.error("Couldn't read file.", e);
// } }
// }; };
// pause_animation = () => { // pause_animation = () => {
// if (this.animation) { // if (this.animation) {