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 {
display: inline-block;
box-sizing: border-box;
background-color: #404040;
height: 26px;

View File

@ -2,6 +2,8 @@ import { create_el } from "./dom";
import { View } from "./View";
import "./Button.css";
function dummy_function(): void {}
export class Button extends View {
element: HTMLButtonElement = create_el("button", "core_Button");
@ -9,5 +11,9 @@ export class Button extends View {
super();
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 {
this.element.remove();
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 { ResizableView } from "../../core/gui/ResizableView";
import { ToolBar } from "../../core/gui/ToolBar";
import { Button } from "../../core/gui/Button";
import "./ModelView.css";
import { model_store } from "../stores/ModelStore";
import { Observable } from "../../core/observable/Observable";
import { RendererView } from "../../core/gui/RendererView";
import { ModelRenderer } from "../rendering/ModelRenderer";
import { View } from "../../core/gui/View";
import { FileInput } from "../../core/gui/FileInput";
const MODEL_LIST_WIDTH = 100;
const ANIMATION_LIST_WIDTH = 150;
@ -14,8 +15,7 @@ const ANIMATION_LIST_WIDTH = 150;
export class ModelView extends ResizableView {
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 model_list_view = this.disposable(
new ModelSelectListView(model_store.models, model_store.current_model),
@ -36,7 +36,7 @@ export class ModelView extends ResizableView {
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]);
}
@ -44,7 +44,7 @@ export class ModelView extends ResizableView {
resize(width: number, height: number): this {
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.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 {
element = create_el("ul", "viewer_ModelSelectListView");

View File

@ -1,14 +1,20 @@
import { Clock } from "three";
import { ArrayBufferCursor } from "../../../core/data_formats/cursor/ArrayBufferCursor";
import { Endianness } from "../../../core/data_formats/Endianness";
import { NjMotion } from "../../../core/data_formats/parsing/ninja/motion";
import { NjObject, parse_nj } from "../../../core/data_formats/parsing/ninja";
import { NjMotion, parse_njm } from "../../../core/data_formats/parsing/ninja/motion";
import { NjObject, parse_nj, parse_xj } from "../../../core/data_formats/parsing/ninja";
import { CharacterClassModel } from "../domain/CharacterClassModel";
import { CharacterClassAnimation } from "../domain/CharacterClassAnimation";
import { Observable } from "../../core/observable/Observable";
import { get_player_data } from "../../../viewer/loading/player";
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_motion_cache: Map<number, Promise<NjMotion>> = new Map();
@ -98,34 +104,48 @@ class ModelStore implements Disposable {
// };
// TODO: notify user of problems.
// load_file = async (file: File) => {
// try {
// const buffer = await read_file(file);
// const cursor = new ArrayBufferCursor(buffer, Endianness.Little);
//
// if (file.name.endsWith(".nj")) {
// const model = parse_nj(cursor)[0];
// this.set_selected(model, true);
// } else if (file.name.endsWith(".xj")) {
// const model = parse_xj(cursor)[0];
// this.set_selected(model, false);
// } else if (file.name.endsWith(".njm")) {
// if (this.current_model) {
// const njm = parse_njm(cursor, this.current_bone_count);
// this.set_animation(create_animation_clip(this.current_model, njm));
// }
// } else if (file.name.endsWith(".xvm")) {
// if (this.current_model) {
// const xvm = parse_xvm(cursor);
// this.set_textures(xvm_to_textures(xvm));
// }
// } else {
// logger.error(`Unknown file extension in filename "${file.name}".`);
// }
// } catch (e) {
// logger.error("Couldn't read file.", e);
// }
// };
load_file = async (file: File) => {
try {
this.current_model.set(undefined);
this.current_nj_data.set(undefined);
this.current_animation.set(undefined);
const buffer = await read_file(file);
const cursor = new ArrayBufferCursor(buffer, Endianness.Little);
if (file.name.endsWith(".nj")) {
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")) {
// if (this.current_model) {
// const njm = parse_njm(cursor, this.current_bone_count);
// this.set_animation(create_animation_clip(this.current_model, njm));
// }
// } else if (file.name.endsWith(".xvm")) {
// if (this.current_model) {
// const xvm = parse_xvm(cursor);
// this.set_textures(xvm_to_textures(xvm));
// }
} else {
logger.error(`Unknown file extension in filename "${file.name}".`);
}
} catch (e) {
logger.error("Couldn't read file.", e);
}
};
// pause_animation = () => {
// if (this.animation) {