Clearer naming of NJ types.

This commit is contained in:
Daan Vanden Bosch 2019-07-06 21:55:02 +02:00
parent ee7756487d
commit 9c71c3deb8
6 changed files with 133 additions and 125 deletions

View File

@ -1,6 +1,6 @@
import { Vec3 } from "../../Vec3";
import { BufferCursor } from "../../BufferCursor";
import { NjModel, parse_nj_model } from "./nj";
import { Vec3 } from "../../Vec3";
import { NjcmModel, parse_njcm_model } from "./njcm";
import { parse_xj_model, XjModel } from "./xj";
// TODO:
@ -9,7 +9,7 @@ import { parse_xj_model, XjModel } from "./xj";
const ANGLE_TO_RAD = (2 * Math.PI) / 65536;
export type NinjaVertex = {
export type NjVertex = {
position: Vec3;
normal?: Vec3;
bone_weight: number;
@ -17,26 +17,34 @@ export type NinjaVertex = {
calc_continue: boolean;
};
export type NinjaModel = NjModel | XjModel;
export type NjModel = NjcmModel | XjModel;
export class NinjaObject<M extends NinjaModel> {
evaluation_flags: NinjaEvaluationFlags;
export function is_njcm_model(model: NjModel): model is NjcmModel {
return model.type === "njcm";
}
export function is_xj_model(model: NjModel): model is XjModel {
return model.type === "xj";
}
export class NjObject<M extends NjModel> {
evaluation_flags: NjEvaluationFlags;
model: M | undefined;
position: Vec3;
rotation: Vec3; // Euler angles in radians.
scale: Vec3;
children: NinjaObject<M>[];
children: NjObject<M>[];
private bone_cache = new Map<number, NinjaObject<M> | null>();
private bone_cache = new Map<number, NjObject<M> | null>();
private _bone_count = -1;
constructor(
evaluation_flags: NinjaEvaluationFlags,
evaluation_flags: NjEvaluationFlags,
model: M | undefined,
position: Vec3,
rotation: Vec3, // Euler angles in radians.
scale: Vec3,
children: NinjaObject<M>[]
children: NjObject<M>[]
) {
this.evaluation_flags = evaluation_flags;
this.model = model;
@ -56,7 +64,7 @@ export class NinjaObject<M extends NinjaModel> {
return this._bone_count;
}
get_bone(bone_id: number): NinjaObject<M> | undefined {
get_bone(bone_id: number): NjObject<M> | undefined {
let bone = this.bone_cache.get(bone_id);
// Strict check because null means there's no bone with this id.
@ -69,10 +77,10 @@ export class NinjaObject<M extends NinjaModel> {
}
private get_bone_internal(
object: NinjaObject<M>,
object: NjObject<M>,
bone_id: number,
id_ref: [number]
): NinjaObject<M> | undefined {
): NjObject<M> | undefined {
if (!object.evaluation_flags.skip) {
const id = id_ref[0]++;
this.bone_cache.set(id, object);
@ -91,7 +99,7 @@ export class NinjaObject<M extends NinjaModel> {
}
}
export type NinjaEvaluationFlags = {
export type NjEvaluationFlags = {
no_translate: boolean;
no_rotate: boolean;
no_scale: boolean;
@ -102,19 +110,19 @@ export type NinjaEvaluationFlags = {
shape_skip: boolean;
};
export function parse_nj(cursor: BufferCursor): NinjaObject<NjModel>[] {
return parse_ninja(cursor, parse_nj_model, []);
export function parse_nj(cursor: BufferCursor): NjObject<NjcmModel>[] {
return parse_ninja(cursor, parse_njcm_model, []);
}
export function parse_xj(cursor: BufferCursor): NinjaObject<XjModel>[] {
export function parse_xj(cursor: BufferCursor): NjObject<XjModel>[] {
return parse_ninja(cursor, parse_xj_model, undefined);
}
function parse_ninja<M extends NinjaModel>(
function parse_ninja<M extends NjModel>(
cursor: BufferCursor,
parse_model: (cursor: BufferCursor, context: any) => M,
context: any
): NinjaObject<M>[] {
): NjObject<M>[] {
while (cursor.bytes_left) {
// Ninja uses a little endian variant of the IFF format.
// IFF files contain chunks preceded by an 8-byte header.
@ -137,11 +145,11 @@ function parse_ninja<M extends NinjaModel>(
}
// TODO: cache model and object offsets so we don't reparse the same data.
function parse_sibling_objects<M extends NinjaModel>(
function parse_sibling_objects<M extends NjModel>(
cursor: BufferCursor,
parse_model: (cursor: BufferCursor, context: any) => M,
context: any
): NinjaObject<M>[] {
): NjObject<M>[] {
const eval_flags = cursor.u32();
const no_translate = (eval_flags & 0b1) !== 0;
const no_rotate = (eval_flags & 0b10) !== 0;
@ -166,8 +174,8 @@ function parse_sibling_objects<M extends NinjaModel>(
const sibling_offset = cursor.u32();
let model: M | undefined;
let children: NinjaObject<M>[];
let siblings: NinjaObject<M>[];
let children: NjObject<M>[];
let siblings: NjObject<M>[];
if (model_offset) {
cursor.seek_start(model_offset);
@ -188,7 +196,7 @@ function parse_sibling_objects<M extends NinjaModel>(
siblings = [];
}
const object = new NinjaObject<M>(
const object = new NjObject<M>(
{
no_translate,
no_rotate,

View File

@ -1,7 +1,7 @@
import Logger from "js-logger";
import { BufferCursor } from "../../BufferCursor";
import { Vec3 } from "../../Vec3";
import { NinjaVertex } from "../ninja";
import { NjVertex } from ".";
const logger = Logger.get("data_formats/parsing/ninja/nj");
@ -12,19 +12,19 @@ const logger = Logger.get("data_formats/parsing/ninja/nj");
// - animation
// - deal with vertex information contained in triangle strips
export type NjModel = {
type: "nj";
export type NjcmModel = {
type: "njcm";
/**
* Sparse array of vertices.
*/
vertices: NinjaVertex[];
meshes: NjTriangleStrip[];
vertices: NjVertex[];
meshes: NjcmTriangleStrip[];
// materials: [],
bounding_sphere_center: Vec3;
bounding_sphere_radius: number;
};
enum NjChunkType {
enum NjcmChunkType {
Unknown,
Null,
Bits,
@ -38,72 +38,72 @@ enum NjChunkType {
End,
}
type NjChunk = {
type: NjChunkType;
type NjcmChunk = {
type: NjcmChunkType;
type_id: number;
} & (
| NjUnknownChunk
| NjNullChunk
| NjBitsChunk
| NjCachePolygonListChunk
| NjDrawPolygonListChunk
| NjTinyChunk
| NjMaterialChunk
| NjVertexChunk
| NjVolumeChunk
| NjStripChunk
| NjEndChunk);
| NjcmUnknownChunk
| NjcmNullChunk
| NjcmBitsChunk
| NjcmCachePolygonListChunk
| NjcmDrawPolygonListChunk
| NjcmTinyChunk
| NjcmMaterialChunk
| NjcmVertexChunk
| NjcmVolumeChunk
| NjcmStripChunk
| NjcmEndChunk);
type NjUnknownChunk = {
type: NjChunkType.Unknown;
type NjcmUnknownChunk = {
type: NjcmChunkType.Unknown;
};
type NjNullChunk = {
type: NjChunkType.Null;
type NjcmNullChunk = {
type: NjcmChunkType.Null;
};
type NjBitsChunk = {
type: NjChunkType.Bits;
type NjcmBitsChunk = {
type: NjcmChunkType.Bits;
};
type NjCachePolygonListChunk = {
type: NjChunkType.CachePolygonList;
type NjcmCachePolygonListChunk = {
type: NjcmChunkType.CachePolygonList;
cache_index: number;
offset: number;
};
type NjDrawPolygonListChunk = {
type: NjChunkType.DrawPolygonList;
type NjcmDrawPolygonListChunk = {
type: NjcmChunkType.DrawPolygonList;
cache_index: number;
};
type NjTinyChunk = {
type: NjChunkType.Tiny;
type NjcmTinyChunk = {
type: NjcmChunkType.Tiny;
};
type NjMaterialChunk = {
type: NjChunkType.Material;
type NjcmMaterialChunk = {
type: NjcmChunkType.Material;
};
type NjVertexChunk = {
type: NjChunkType.Vertex;
vertices: NjVertex[];
type NjcmVertexChunk = {
type: NjcmChunkType.Vertex;
vertices: NjcmVertex[];
};
type NjVolumeChunk = {
type: NjChunkType.Volume;
type NjcmVolumeChunk = {
type: NjcmChunkType.Volume;
};
type NjStripChunk = {
type: NjChunkType.Strip;
triangle_strips: NjTriangleStrip[];
type NjcmStripChunk = {
type: NjcmChunkType.Strip;
triangle_strips: NjcmTriangleStrip[];
};
type NjEndChunk = {
type: NjChunkType.End;
type NjcmEndChunk = {
type: NjcmChunkType.End;
};
type NjVertex = {
type NjcmVertex = {
index: number;
position: Vec3;
normal?: Vec3;
@ -112,7 +112,7 @@ type NjVertex = {
calc_continue: boolean;
};
type NjTriangleStrip = {
type NjcmTriangleStrip = {
ignore_light: boolean;
ignore_specular: boolean;
ignore_ambient: boolean;
@ -121,27 +121,27 @@ type NjTriangleStrip = {
flat_shading: boolean;
environment_mapping: boolean;
clockwise_winding: boolean;
vertices: NjMeshVertex[];
vertices: NjcmMeshVertex[];
};
type NjMeshVertex = {
type NjcmMeshVertex = {
index: number;
normal?: Vec3;
};
export function parse_nj_model(cursor: BufferCursor, cached_chunk_offsets: number[]): NjModel {
export function parse_njcm_model(cursor: BufferCursor, cached_chunk_offsets: number[]): NjcmModel {
const vlist_offset = cursor.u32(); // Vertex list
const plist_offset = cursor.u32(); // Triangle strip index list
const bounding_sphere_center = new Vec3(cursor.f32(), cursor.f32(), cursor.f32());
const bounding_sphere_radius = cursor.f32();
const vertices: NinjaVertex[] = [];
const meshes: NjTriangleStrip[] = [];
const vertices: NjVertex[] = [];
const meshes: NjcmTriangleStrip[] = [];
if (vlist_offset) {
cursor.seek_start(vlist_offset);
for (const chunk of parse_chunks(cursor, cached_chunk_offsets, true)) {
if (chunk.type === NjChunkType.Vertex) {
if (chunk.type === NjcmChunkType.Vertex) {
for (const vertex of chunk.vertices) {
vertices[vertex.index] = {
position: vertex.position,
@ -159,14 +159,14 @@ export function parse_nj_model(cursor: BufferCursor, cached_chunk_offsets: numbe
cursor.seek_start(plist_offset);
for (const chunk of parse_chunks(cursor, cached_chunk_offsets, false)) {
if (chunk.type === NjChunkType.Strip) {
if (chunk.type === NjcmChunkType.Strip) {
meshes.push(...chunk.triangle_strips);
}
}
}
return {
type: "nj",
type: "njcm",
vertices,
meshes,
bounding_sphere_center,
@ -179,8 +179,8 @@ function parse_chunks(
cursor: BufferCursor,
cached_chunk_offsets: number[],
wide_end_chunks: boolean
): NjChunk[] {
const chunks: NjChunk[] = [];
): NjcmChunk[] {
const chunks: NjcmChunk[] = [];
let loop = true;
while (loop) {
@ -191,19 +191,19 @@ function parse_chunks(
if (type_id === 0) {
chunks.push({
type: NjChunkType.Null,
type: NjcmChunkType.Null,
type_id,
});
} else if (1 <= type_id && type_id <= 3) {
chunks.push({
type: NjChunkType.Bits,
type: NjcmChunkType.Bits,
type_id,
});
} else if (type_id === 4) {
const cache_index = flags;
const offset = cursor.position;
chunks.push({
type: NjChunkType.CachePolygonList,
type: NjcmChunkType.CachePolygonList,
type_id,
cache_index,
offset,
@ -220,53 +220,53 @@ function parse_chunks(
}
chunks.push({
type: NjChunkType.DrawPolygonList,
type: NjcmChunkType.DrawPolygonList,
type_id,
cache_index,
});
} else if (8 <= type_id && type_id <= 9) {
size = 2;
chunks.push({
type: NjChunkType.Tiny,
type: NjcmChunkType.Tiny,
type_id,
});
} else if (17 <= type_id && type_id <= 31) {
size = 2 + 2 * cursor.u16();
chunks.push({
type: NjChunkType.Material,
type: NjcmChunkType.Material,
type_id,
});
} else if (32 <= type_id && type_id <= 50) {
size = 2 + 4 * cursor.u16();
chunks.push({
type: NjChunkType.Vertex,
type: NjcmChunkType.Vertex,
type_id,
vertices: parse_vertex_chunk(cursor, type_id, flags),
});
} else if (56 <= type_id && type_id <= 58) {
size = 2 + 2 * cursor.u16();
chunks.push({
type: NjChunkType.Volume,
type: NjcmChunkType.Volume,
type_id,
});
} else if (64 <= type_id && type_id <= 75) {
size = 2 + 2 * cursor.u16();
chunks.push({
type: NjChunkType.Strip,
type: NjcmChunkType.Strip,
type_id,
triangle_strips: parse_triangle_strip_chunk(cursor, type_id, flags),
});
} else if (type_id === 255) {
size = wide_end_chunks ? 2 : 0;
chunks.push({
type: NjChunkType.End,
type: NjcmChunkType.End,
type_id,
});
loop = false;
} else {
size = 2 + 2 * cursor.u16();
chunks.push({
type: NjChunkType.Unknown,
type: NjcmChunkType.Unknown,
type_id,
});
logger.warn(`Unknown chunk type ${type_id} at offset ${chunk_start_position}.`);
@ -282,7 +282,7 @@ function parse_vertex_chunk(
cursor: BufferCursor,
chunk_type_id: number,
flags: number
): NjVertex[] {
): NjcmVertex[] {
if (chunk_type_id < 32 || chunk_type_id > 50) {
logger.warn(`Unknown vertex chunk type ${chunk_type_id}.`);
return [];
@ -294,10 +294,10 @@ function parse_vertex_chunk(
const index = cursor.u16();
const vertex_count = cursor.u16();
const vertices: NjVertex[] = [];
const vertices: NjcmVertex[] = [];
for (let i = 0; i < vertex_count; ++i) {
const vertex: NjVertex = {
const vertex: NjcmVertex = {
index: index + i,
position: new Vec3(
cursor.f32(), // x
@ -374,7 +374,7 @@ function parse_triangle_strip_chunk(
cursor: BufferCursor,
chunk_type_id: number,
flags: number
): NjTriangleStrip[] {
): NjcmTriangleStrip[] {
const render_flags = {
ignore_light: (flags & 0b1) !== 0,
ignore_specular: (flags & 0b10) !== 0,
@ -432,17 +432,17 @@ function parse_triangle_strip_chunk(
const [parse_texture_coords, parse_color, parse_normal, parse_texture_coords_hires] = options;
const strips: NjTriangleStrip[] = [];
const strips: NjcmTriangleStrip[] = [];
for (let i = 0; i < strip_count; ++i) {
const winding_flag_and_index_count = cursor.i16();
const clockwise_winding = winding_flag_and_index_count < 1;
const index_count = Math.abs(winding_flag_and_index_count);
const vertices: NjMeshVertex[] = [];
const vertices: NjcmMeshVertex[] = [];
for (let j = 0; j < index_count; ++j) {
const vertex: NjMeshVertex = {
const vertex: NjcmMeshVertex = {
index: cursor.u16(),
};
vertices.push(vertex);

View File

@ -1,6 +1,6 @@
import { BufferCursor } from "../../BufferCursor";
import { Vec3 } from "../../Vec3";
import { NinjaVertex } from "../ninja";
import { NjVertex } from "../ninja";
// TODO:
// - textures
@ -10,7 +10,7 @@ import { NinjaVertex } from "../ninja";
export type XjModel = {
type: "xj";
vertices: NinjaVertex[];
vertices: NjVertex[];
meshes: XjTriangleStrip[];
};

View File

@ -8,7 +8,7 @@ import {
QuaternionKeyframeTrack,
VectorKeyframeTrack,
} from "three";
import { NinjaModel, NinjaObject } from "../data_formats/parsing/ninja";
import { NjModel, NjObject } from "../data_formats/parsing/ninja";
import {
NjInterpolation,
NjKeyframeTrackType,
@ -18,16 +18,16 @@ import {
export const PSO_FRAME_RATE = 30;
export function create_animation_clip(
object: NinjaObject<NinjaModel>,
motion: NjMotion
nj_object: NjObject<NjModel>,
nj_motion: NjMotion
): AnimationClip {
const interpolation =
motion.interpolation === NjInterpolation.Spline ? InterpolateSmooth : InterpolateLinear;
nj_motion.interpolation === NjInterpolation.Spline ? InterpolateSmooth : InterpolateLinear;
const tracks: KeyframeTrack[] = [];
motion.motion_data.forEach((motion_data, bone_id) => {
const bone = object.get_bone(bone_id);
nj_motion.motion_data.forEach((motion_data, bone_id) => {
const bone = nj_object.get_bone(bone_id);
if (!bone) return;
motion_data.tracks.forEach(({ type, keyframes }) => {
@ -71,7 +71,7 @@ export function create_animation_clip(
return new AnimationClip(
"Animation",
(motion.frame_count - 1) / PSO_FRAME_RATE,
(nj_motion.frame_count - 1) / PSO_FRAME_RATE,
tracks
).optimize();
}

View File

@ -15,8 +15,8 @@ import {
Vector3,
} from "three";
import { vec3_to_threejs } from ".";
import { NinjaModel, NinjaObject } from "../data_formats/parsing/ninja";
import { NjModel } from "../data_formats/parsing/ninja/nj";
import { NjModel, NjObject, is_njcm_model } from "../data_formats/parsing/ninja";
import { NjcmModel } from "../data_formats/parsing/ninja/njcm";
import { XjModel } from "../data_formats/parsing/ninja/xj";
const DEFAULT_MATERIAL = new MeshLambertMaterial({
@ -34,14 +34,14 @@ const NO_ROTATION = new Quaternion(0, 0, 0, 1);
const NO_SCALE = new Vector3(1, 1, 1);
export function ninja_object_to_buffer_geometry(
object: NinjaObject<NinjaModel>,
object: NjObject<NjModel>,
material: Material = DEFAULT_MATERIAL
): BufferGeometry {
return new Object3DCreator(material).create_buffer_geometry(object);
}
export function ninja_object_to_skinned_mesh(
object: NinjaObject<NinjaModel>,
object: NjObject<NjModel>,
material: Material = DEFAULT_SKINNED_MATERIAL
): SkinnedMesh {
return new Object3DCreator(material).create_skinned_mesh(object);
@ -93,7 +93,7 @@ class Object3DCreator {
this.material = material;
}
create_buffer_geometry(object: NinjaObject<NinjaModel>): BufferGeometry {
create_buffer_geometry(object: NjObject<NjModel>): BufferGeometry {
this.object_to_geometry(object, undefined, new Matrix4());
const geom = new BufferGeometry();
@ -108,7 +108,7 @@ class Object3DCreator {
return geom;
}
create_skinned_mesh(object: NinjaObject<NinjaModel>): SkinnedMesh {
create_skinned_mesh(object: NjObject<NjModel>): SkinnedMesh {
const geom = this.create_buffer_geometry(object);
geom.addAttribute("skinIndex", new Uint16BufferAttribute(this.bone_indices, 4));
geom.addAttribute("skinWeight", new Float32BufferAttribute(this.bone_weights, 4));
@ -123,7 +123,7 @@ class Object3DCreator {
}
private object_to_geometry(
object: NinjaObject<NinjaModel>,
object: NjObject<NjModel>,
parent_bone: Bone | undefined,
parent_matrix: Matrix4
): void {
@ -184,15 +184,15 @@ class Object3DCreator {
}
}
private model_to_geometry(model: NinjaModel, matrix: Matrix4): void {
if (model.type === "nj") {
this.nj_model_to_geometry(model, matrix);
private model_to_geometry(model: NjModel, matrix: Matrix4): void {
if (is_njcm_model(model)) {
this.njcm_model_to_geometry(model, matrix);
} else {
this.xj_model_to_geometry(model, matrix);
}
}
private nj_model_to_geometry(model: NjModel, matrix: Matrix4): void {
private njcm_model_to_geometry(model: NjcmModel, matrix: Matrix4): void {
const normal_matrix = new Matrix3().getNormalMatrix(matrix);
const new_vertices = model.vertices.map(vertex => {

View File

@ -2,7 +2,7 @@ import Logger from "js-logger";
import { action, observable } from "mobx";
import { AnimationAction, AnimationClip, AnimationMixer, SkinnedMesh } from "three";
import { BufferCursor } from "../data_formats/BufferCursor";
import { NinjaModel, NinjaObject, parse_nj, parse_xj } from "../data_formats/parsing/ninja";
import { NjModel, NjObject, parse_nj, parse_xj } from "../data_formats/parsing/ninja";
import { parse_njm, NjMotion } from "../data_formats/parsing/ninja/motion";
import { PlayerModel, PlayerAnimation } from "../domain";
import { create_animation_clip, PSO_FRAME_RATE } from "../rendering/animation";
@ -10,7 +10,7 @@ import { ninja_object_to_skinned_mesh } from "../rendering/models";
import { get_player_data, get_player_animation_data } from "./binary_assets";
const logger = Logger.get("stores/ModelViewerStore");
const nj_object_cache: Map<string, Promise<NinjaObject<NinjaModel>>> = new Map();
const nj_object_cache: Map<string, Promise<NjObject<NjModel>>> = new Map();
const nj_motion_cache: Map<number, Promise<NjMotion>> = new Map();
class ModelViewerStore {
@ -33,7 +33,7 @@ class ModelViewerStore {
.map((_, i) => new PlayerAnimation(i, `Animation ${i + 1}`));
@observable.ref current_player_model?: PlayerModel;
@observable.ref current_model?: NinjaObject<NinjaModel>;
@observable.ref current_model?: NjObject<NjModel>;
@observable.ref current_bone_count: number = 0;
@observable.ref current_obj3d?: SkinnedMesh;
@ -130,7 +130,7 @@ class ModelViewerStore {
private set_model = action(
"set_model",
(model: NinjaObject<NinjaModel>, player_model?: PlayerModel) => {
(model: NjObject<NjModel>, player_model?: PlayerModel) => {
if (this.current_obj3d && this.animation) {
this.animation.mixer.stopAllAction();
this.animation.mixer.uncacheRoot(this.current_obj3d);
@ -174,8 +174,8 @@ class ModelViewerStore {
};
private add_to_bone(
object: NinjaObject<NinjaModel>,
head_part: NinjaObject<NinjaModel>,
object: NjObject<NjModel>,
head_part: NjObject<NjModel>,
bone_id: number
): void {
const bone = object.get_bone(bone_id);
@ -187,7 +187,7 @@ class ModelViewerStore {
}
}
private async get_player_ninja_object(model: PlayerModel): Promise<NinjaObject<NinjaModel>> {
private async get_player_ninja_object(model: PlayerModel): Promise<NjObject<NjModel>> {
let ninja_object = nj_object_cache.get(model.name);
if (ninja_object) {
@ -199,7 +199,7 @@ class ModelViewerStore {
}
}
private async get_all_assets(model: PlayerModel): Promise<NinjaObject<NinjaModel>> {
private async get_all_assets(model: PlayerModel): Promise<NjObject<NjModel>> {
const body_data = await get_player_data(model.name, "Body");
const body = parse_nj(new BufferCursor(body_data, true))[0];