mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-05 15:28:29 +08:00
The EntityListView now shows renders of entities instead of green squares.
This commit is contained in:
parent
3d9b003e39
commit
f0d474ad40
@ -46,6 +46,16 @@ export const el = {
|
||||
return element;
|
||||
},
|
||||
|
||||
img: (
|
||||
attributes?: ElementAttributes & {
|
||||
src?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
alt?: string;
|
||||
},
|
||||
...children: HTMLImageElement[]
|
||||
): HTMLImageElement => create_element("img", attributes, ...children),
|
||||
|
||||
table: (attributes?: ElementAttributes, ...children: HTMLElement[]): HTMLTableElement =>
|
||||
create_element("table", attributes, ...children),
|
||||
|
||||
@ -82,17 +92,25 @@ export function create_element<T extends HTMLElement>(
|
||||
tag_name: string,
|
||||
attributes?: ElementAttributes & {
|
||||
href?: string;
|
||||
src?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
alt?: string;
|
||||
col_span?: number;
|
||||
},
|
||||
...children: HTMLElement[]
|
||||
): T {
|
||||
const element = document.createElement(tag_name) as (HTMLTableCellElement & HTMLAnchorElement);
|
||||
const element = document.createElement(tag_name) as any;
|
||||
|
||||
if (attributes) {
|
||||
if (attributes.class) element.className = attributes.class;
|
||||
if (attributes.text) element.textContent = attributes.text;
|
||||
if (attributes.title) element.title = attributes.title;
|
||||
if (attributes.href) element.href = attributes.href;
|
||||
if (attributes.class != undefined) element.className = attributes.class;
|
||||
if (attributes.text != undefined) element.textContent = attributes.text;
|
||||
if (attributes.title != undefined) element.title = attributes.title;
|
||||
if (attributes.href != undefined) element.href = attributes.href;
|
||||
if (attributes.src != undefined) element.src = attributes.src;
|
||||
if (attributes.width != undefined) element.width = attributes.width;
|
||||
if (attributes.height != undefined) element.height = attributes.height;
|
||||
if (attributes.alt != undefined) element.alt = attributes.alt;
|
||||
|
||||
if (attributes.data) {
|
||||
for (const [key, val] of Object.entries(attributes.data)) {
|
||||
@ -100,9 +118,9 @@ export function create_element<T extends HTMLElement>(
|
||||
}
|
||||
}
|
||||
|
||||
if (attributes.col_span) element.colSpan = attributes.col_span;
|
||||
if (attributes.col_span != undefined) element.colSpan = attributes.col_span;
|
||||
|
||||
if (attributes.tab_index) element.tabIndex = attributes.tab_index;
|
||||
if (attributes.tab_index != undefined) element.tabIndex = attributes.tab_index;
|
||||
}
|
||||
|
||||
element.append(...children);
|
||||
|
@ -38,12 +38,12 @@ export abstract class Renderer implements Disposable {
|
||||
readonly scene = new Scene();
|
||||
readonly light_holder = new Group();
|
||||
|
||||
private renderer = new WebGLRenderer({ antialias: true });
|
||||
private readonly renderer = new WebGLRenderer({ antialias: true });
|
||||
private render_scheduled = false;
|
||||
private animation_frame_handle?: number = undefined;
|
||||
private light = new HemisphereLight(0xffffff, 0x505050, 1.2);
|
||||
private controls_clock = new Clock();
|
||||
private size = new Vector2();
|
||||
private readonly light = new HemisphereLight(0xffffff, 0x505050, 1.2);
|
||||
private readonly controls_clock = new Clock();
|
||||
private readonly size = new Vector2();
|
||||
|
||||
protected constructor() {
|
||||
this.dom_element.tabIndex = 0;
|
||||
|
@ -5,6 +5,8 @@
|
||||
.quest_editor_EntityListView_entity_list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 100px);
|
||||
grid-column-gap: 6px;
|
||||
grid-row-gap: 6px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@ -12,11 +14,5 @@
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
height: 100px;
|
||||
padding: 5px;
|
||||
border: solid 2px olivedrab;
|
||||
background-color: darkolivegreen;
|
||||
color: greenyellow;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import "./EntityListView.css";
|
||||
import { entity_data, EntityType } from "../../core/data_formats/parsing/quest/entities";
|
||||
import { ListProperty } from "../../core/observable/property/list/ListProperty";
|
||||
import { entity_dnd_source } from "./entity_dnd";
|
||||
import { render_entity_to_image } from "../rendering/render_entity_to_image";
|
||||
|
||||
export abstract class EntityListView<T extends EntityType> extends ResizableWidget {
|
||||
readonly element: HTMLElement;
|
||||
@ -19,26 +20,50 @@ export abstract class EntityListView<T extends EntityType> extends ResizableWidg
|
||||
bind_children_to(list_element, entities, this.create_entity_element),
|
||||
|
||||
entity_dnd_source(list_element, target => {
|
||||
if (target !== list_element) {
|
||||
const drag_element = target.cloneNode(true) as HTMLElement;
|
||||
drag_element.style.width = "100px";
|
||||
return [drag_element, entities.get(parseInt(target.dataset.index!, 10))];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
let element: HTMLElement | null = target;
|
||||
|
||||
do {
|
||||
const index = target.dataset.index;
|
||||
|
||||
if (index != undefined) {
|
||||
return [
|
||||
element.querySelector("img")!.cloneNode(true) as HTMLElement,
|
||||
entities.get(parseInt(index, 10)),
|
||||
];
|
||||
}
|
||||
|
||||
element = element.parentElement;
|
||||
} while (element && element !== list_element);
|
||||
|
||||
return undefined;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private create_entity_element = (entity: T, index: number): HTMLElement => {
|
||||
const div = el.div({
|
||||
const entity_element = el.div({
|
||||
class: "quest_editor_EntityListView_entity",
|
||||
text: entity_data(entity).name,
|
||||
data: { index: index.toString() },
|
||||
});
|
||||
entity_element.draggable = true;
|
||||
|
||||
div.draggable = true;
|
||||
const img_element = el.img({ width: 100, height: 100 });
|
||||
img_element.style.visibility = "hidden";
|
||||
// Workaround for Chrome bug: when dragging an image, calling setDragImage on a DragEvent
|
||||
// has no effect.
|
||||
img_element.style.pointerEvents = "none";
|
||||
entity_element.append(img_element);
|
||||
|
||||
return div;
|
||||
render_entity_to_image(entity).then(url => {
|
||||
img_element.src = url;
|
||||
img_element.style.visibility = "visible";
|
||||
});
|
||||
|
||||
const name_element = el.span({
|
||||
text: entity_data(entity).name,
|
||||
});
|
||||
entity_element.append(name_element);
|
||||
|
||||
return entity_element;
|
||||
};
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ export function entity_dnd_source(
|
||||
const result = start(e.target);
|
||||
|
||||
if (result) {
|
||||
grab_point.set(e.offsetX + 2, e.offsetY + 2);
|
||||
grab_point.set(e.offsetX, e.offsetY);
|
||||
|
||||
dragging_details = {
|
||||
drag_element: result[0],
|
||||
@ -76,6 +76,8 @@ export function entity_dnd_source(
|
||||
entity_data(dragging_details.entity_type).name,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,13 @@ import { xvm_to_textures } from "../../core/rendering/conversion/ninja_textures"
|
||||
import { load_array_buffer } from "../../core/loading";
|
||||
import { object_data, ObjectType } from "../../core/data_formats/parsing/quest/object_types";
|
||||
import { NpcType } from "../../core/data_formats/parsing/quest/npc_types";
|
||||
import {
|
||||
entity_type_to_string,
|
||||
EntityType,
|
||||
is_npc_type,
|
||||
} from "../../core/data_formats/parsing/quest/entities";
|
||||
|
||||
const logger = Logger.get("loading/entities");
|
||||
const logger = Logger.get("quest_editor/loading/entities");
|
||||
|
||||
const DEFAULT_ENTITY = new CylinderBufferGeometry(3, 3, 20);
|
||||
DEFAULT_ENTITY.translate(0, 10, 0);
|
||||
@ -26,16 +31,22 @@ const DEFAULT_ENTITY_TEX_PROMISE: Promise<Texture[]> = new Promise(resolve =>
|
||||
resolve(DEFAULT_ENTITY_TEX),
|
||||
);
|
||||
|
||||
const npc_cache = new LoadingCache<NpcType, Promise<BufferGeometry>>();
|
||||
npc_cache.set(NpcType.Unknown, DEFAULT_ENTITY_PROMISE);
|
||||
const geom_cache = new LoadingCache<EntityType, Promise<BufferGeometry>>();
|
||||
|
||||
const npc_tex_cache = new LoadingCache<NpcType, Promise<Texture[]>>();
|
||||
npc_tex_cache.set(NpcType.Unknown, DEFAULT_ENTITY_TEX_PROMISE);
|
||||
|
||||
const object_cache = new LoadingCache<ObjectType, Promise<BufferGeometry>>();
|
||||
const object_tex_cache = new LoadingCache<ObjectType, Promise<Texture[]>>();
|
||||
const tex_cache = new LoadingCache<EntityType, Promise<Texture[]>>();
|
||||
|
||||
for (const type of [
|
||||
NpcType.Unknown,
|
||||
NpcType.Migium,
|
||||
NpcType.Hidoom,
|
||||
NpcType.DeathGunner,
|
||||
NpcType.StRappy,
|
||||
NpcType.HalloRappy,
|
||||
NpcType.EggRappy,
|
||||
NpcType.Migium2,
|
||||
NpcType.Hidoom2,
|
||||
NpcType.Recon,
|
||||
|
||||
ObjectType.Unknown,
|
||||
ObjectType.PlayerSet,
|
||||
ObjectType.FogCollision,
|
||||
@ -56,92 +67,49 @@ for (const type of [
|
||||
ObjectType.TempleMapDetect,
|
||||
ObjectType.LabInvisibleObject,
|
||||
]) {
|
||||
object_cache.set(type, DEFAULT_ENTITY_PROMISE);
|
||||
object_tex_cache.set(type, DEFAULT_ENTITY_TEX_PROMISE);
|
||||
geom_cache.set(type, DEFAULT_ENTITY_PROMISE);
|
||||
tex_cache.set(type, DEFAULT_ENTITY_TEX_PROMISE);
|
||||
}
|
||||
|
||||
export async function load_npc_geometry(npc_type: NpcType): Promise<BufferGeometry> {
|
||||
return npc_cache.get_or_set(npc_type, async () => {
|
||||
export async function load_entity_geometry(type: EntityType): Promise<BufferGeometry> {
|
||||
return geom_cache.get_or_set(type, async () => {
|
||||
try {
|
||||
const { url, data } = await load_npc_data(npc_type, AssetType.Geometry);
|
||||
const { url, data } = await load_entity_data(type, AssetType.Geometry);
|
||||
const cursor = new ArrayBufferCursor(data, Endianness.Little);
|
||||
const nj_objects = url.endsWith(".nj") ? parse_nj(cursor) : parse_xj(cursor);
|
||||
|
||||
if (nj_objects.length) {
|
||||
return ninja_object_to_buffer_geometry(nj_objects[0]);
|
||||
} else {
|
||||
logger.warn(`Couldn't parse ${url} for ${NpcType[npc_type]}.`);
|
||||
logger.warn(`Couldn't parse ${url} for ${entity_type_to_string(type)}.`);
|
||||
return DEFAULT_ENTITY;
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn(`Couldn't load geometry file for ${NpcType[npc_type]}.`, e);
|
||||
logger.warn(`Couldn't load geometry file for ${entity_type_to_string(type)}.`, e);
|
||||
return DEFAULT_ENTITY;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function load_npc_textures(npc_type: NpcType): Promise<Texture[]> {
|
||||
return npc_tex_cache.get_or_set(npc_type, async () => {
|
||||
export async function load_entity_textures(type: EntityType): Promise<Texture[]> {
|
||||
return tex_cache.get_or_set(type, async () => {
|
||||
try {
|
||||
const { data } = await load_npc_data(npc_type, AssetType.Texture);
|
||||
const { data } = await load_entity_data(type, AssetType.Texture);
|
||||
const cursor = new ArrayBufferCursor(data, Endianness.Little);
|
||||
const xvm = parse_xvm(cursor);
|
||||
return xvm_to_textures(xvm);
|
||||
} catch (e) {
|
||||
logger.warn(`Couldn't load texture file for ${NpcType[npc_type]}.`, e);
|
||||
logger.warn(`Couldn't load texture file for ${entity_type_to_string(type)}.`, e);
|
||||
return DEFAULT_ENTITY_TEX;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function load_object_geometry(object_type: ObjectType): Promise<BufferGeometry> {
|
||||
return object_cache.get_or_set(object_type, async () => {
|
||||
try {
|
||||
const { url, data } = await load_object_data(object_type, AssetType.Geometry);
|
||||
const cursor = new ArrayBufferCursor(data, Endianness.Little);
|
||||
const nj_objects = url.endsWith(".nj") ? parse_nj(cursor) : parse_xj(cursor);
|
||||
|
||||
if (nj_objects.length) {
|
||||
return ninja_object_to_buffer_geometry(nj_objects[0]);
|
||||
} else {
|
||||
logger.warn(`Couldn't parse ${url} for ${ObjectType[object_type]}.`);
|
||||
return DEFAULT_ENTITY;
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn(`Couldn't load geometry file for ${ObjectType[object_type]}.`, e);
|
||||
return DEFAULT_ENTITY;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function load_object_textures(object_type: ObjectType): Promise<Texture[]> {
|
||||
return object_tex_cache.get_or_set(object_type, async () => {
|
||||
try {
|
||||
const { data } = await load_object_data(object_type, AssetType.Texture);
|
||||
const cursor = new ArrayBufferCursor(data, Endianness.Little);
|
||||
const xvm = parse_xvm(cursor);
|
||||
return xvm_to_textures(xvm);
|
||||
} catch (e) {
|
||||
logger.warn(`Couldn't load texture file for ${ObjectType[object_type]}.`, e);
|
||||
return DEFAULT_ENTITY_TEX;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function load_npc_data(
|
||||
npc_type: NpcType,
|
||||
type: AssetType,
|
||||
export async function load_entity_data(
|
||||
type: EntityType,
|
||||
asset_type: AssetType,
|
||||
): Promise<{ url: string; data: ArrayBuffer }> {
|
||||
const url = npc_type_to_url(npc_type, type);
|
||||
const data = await load_array_buffer(url);
|
||||
return { url, data };
|
||||
}
|
||||
|
||||
export async function load_object_data(
|
||||
object_type: ObjectType,
|
||||
type: AssetType,
|
||||
): Promise<{ url: string; data: ArrayBuffer }> {
|
||||
const url = object_type_to_url(object_type, type);
|
||||
const url = entity_type_to_url(type, asset_type);
|
||||
const data = await load_array_buffer(url);
|
||||
return { url, data };
|
||||
}
|
||||
@ -151,88 +119,90 @@ enum AssetType {
|
||||
Texture,
|
||||
}
|
||||
|
||||
function npc_type_to_url(npc_type: NpcType, type: AssetType): string {
|
||||
switch (npc_type) {
|
||||
// The dubswitch model is in XJ format.
|
||||
case NpcType.Dubswitch:
|
||||
return `/npcs/${NpcType[npc_type]}.${type === AssetType.Geometry ? "xj" : "xvm"}`;
|
||||
function entity_type_to_url(type: EntityType, asset_type: AssetType): string {
|
||||
if (is_npc_type(type)) {
|
||||
switch (type) {
|
||||
// The dubswitch model is in XJ format.
|
||||
case NpcType.Dubswitch:
|
||||
return `/npcs/${NpcType[type]}.${asset_type === AssetType.Geometry ? "xj" : "xvm"}`;
|
||||
|
||||
// Episode II VR Temple
|
||||
// Episode II VR Temple
|
||||
|
||||
case NpcType.Hildebear2:
|
||||
return npc_type_to_url(NpcType.Hildebear, type);
|
||||
case NpcType.Hildeblue2:
|
||||
return npc_type_to_url(NpcType.Hildeblue, type);
|
||||
case NpcType.RagRappy2:
|
||||
return npc_type_to_url(NpcType.RagRappy, type);
|
||||
case NpcType.Monest2:
|
||||
return npc_type_to_url(NpcType.Monest, type);
|
||||
case NpcType.PoisonLily2:
|
||||
return npc_type_to_url(NpcType.PoisonLily, type);
|
||||
case NpcType.NarLily2:
|
||||
return npc_type_to_url(NpcType.NarLily, type);
|
||||
case NpcType.GrassAssassin2:
|
||||
return npc_type_to_url(NpcType.GrassAssassin, type);
|
||||
case NpcType.Dimenian2:
|
||||
return npc_type_to_url(NpcType.Dimenian, type);
|
||||
case NpcType.LaDimenian2:
|
||||
return npc_type_to_url(NpcType.LaDimenian, type);
|
||||
case NpcType.SoDimenian2:
|
||||
return npc_type_to_url(NpcType.SoDimenian, type);
|
||||
case NpcType.DarkBelra2:
|
||||
return npc_type_to_url(NpcType.DarkBelra, type);
|
||||
case NpcType.Hildebear2:
|
||||
return entity_type_to_url(NpcType.Hildebear, asset_type);
|
||||
case NpcType.Hildeblue2:
|
||||
return entity_type_to_url(NpcType.Hildeblue, asset_type);
|
||||
case NpcType.RagRappy2:
|
||||
return entity_type_to_url(NpcType.RagRappy, asset_type);
|
||||
case NpcType.Monest2:
|
||||
return entity_type_to_url(NpcType.Monest, asset_type);
|
||||
case NpcType.Mothmant2:
|
||||
return entity_type_to_url(NpcType.Mothmant, asset_type);
|
||||
case NpcType.PoisonLily2:
|
||||
return entity_type_to_url(NpcType.PoisonLily, asset_type);
|
||||
case NpcType.NarLily2:
|
||||
return entity_type_to_url(NpcType.NarLily, asset_type);
|
||||
case NpcType.GrassAssassin2:
|
||||
return entity_type_to_url(NpcType.GrassAssassin, asset_type);
|
||||
case NpcType.Dimenian2:
|
||||
return entity_type_to_url(NpcType.Dimenian, asset_type);
|
||||
case NpcType.LaDimenian2:
|
||||
return entity_type_to_url(NpcType.LaDimenian, asset_type);
|
||||
case NpcType.SoDimenian2:
|
||||
return entity_type_to_url(NpcType.SoDimenian, asset_type);
|
||||
case NpcType.DarkBelra2:
|
||||
return entity_type_to_url(NpcType.DarkBelra, asset_type);
|
||||
|
||||
// Episode II VR Spaceship
|
||||
// Episode II VR Spaceship
|
||||
|
||||
case NpcType.SavageWolf2:
|
||||
return npc_type_to_url(NpcType.SavageWolf, type);
|
||||
case NpcType.BarbarousWolf2:
|
||||
return npc_type_to_url(NpcType.BarbarousWolf, type);
|
||||
case NpcType.PanArms2:
|
||||
return npc_type_to_url(NpcType.PanArms, type);
|
||||
case NpcType.Dubchic2:
|
||||
return npc_type_to_url(NpcType.Dubchic, type);
|
||||
case NpcType.Gilchic2:
|
||||
return npc_type_to_url(NpcType.Gilchic, type);
|
||||
case NpcType.Garanz2:
|
||||
return npc_type_to_url(NpcType.Garanz, type);
|
||||
case NpcType.Dubswitch2:
|
||||
return npc_type_to_url(NpcType.Dubswitch, type);
|
||||
case NpcType.Delsaber2:
|
||||
return npc_type_to_url(NpcType.Delsaber, type);
|
||||
case NpcType.ChaosSorcerer2:
|
||||
return npc_type_to_url(NpcType.ChaosSorcerer, type);
|
||||
|
||||
default:
|
||||
return `/npcs/${NpcType[npc_type]}.${type === AssetType.Geometry ? "nj" : "xvm"}`;
|
||||
}
|
||||
}
|
||||
|
||||
function object_type_to_url(object_type: ObjectType, type: AssetType): string {
|
||||
if (type === AssetType.Geometry) {
|
||||
switch (object_type) {
|
||||
case ObjectType.EasterEgg:
|
||||
case ObjectType.ChristmasTree:
|
||||
case ObjectType.ChristmasWreath:
|
||||
case ObjectType.TwentyFirstCentury:
|
||||
case ObjectType.Sonic:
|
||||
case ObjectType.WelcomeBoard:
|
||||
case ObjectType.FloatingJelifish:
|
||||
case ObjectType.RuinsSeal:
|
||||
case ObjectType.Dolphin:
|
||||
case ObjectType.Cacti:
|
||||
case ObjectType.BigBrownRock:
|
||||
case ObjectType.PoisonPlant:
|
||||
case ObjectType.BigBlackRocks:
|
||||
case ObjectType.FallingRock:
|
||||
case ObjectType.DesertFixedTypeBoxBreakableCrystals:
|
||||
case ObjectType.BeeHive:
|
||||
return `/objects/${object_data(object_type).pso_id}.nj`;
|
||||
case NpcType.SavageWolf2:
|
||||
return entity_type_to_url(NpcType.SavageWolf, asset_type);
|
||||
case NpcType.BarbarousWolf2:
|
||||
return entity_type_to_url(NpcType.BarbarousWolf, asset_type);
|
||||
case NpcType.PanArms2:
|
||||
return entity_type_to_url(NpcType.PanArms, asset_type);
|
||||
case NpcType.Dubchic2:
|
||||
return entity_type_to_url(NpcType.Dubchic, asset_type);
|
||||
case NpcType.Gilchic2:
|
||||
return entity_type_to_url(NpcType.Gilchic, asset_type);
|
||||
case NpcType.Garanz2:
|
||||
return entity_type_to_url(NpcType.Garanz, asset_type);
|
||||
case NpcType.Dubswitch2:
|
||||
return entity_type_to_url(NpcType.Dubswitch, asset_type);
|
||||
case NpcType.Delsaber2:
|
||||
return entity_type_to_url(NpcType.Delsaber, asset_type);
|
||||
case NpcType.ChaosSorcerer2:
|
||||
return entity_type_to_url(NpcType.ChaosSorcerer, asset_type);
|
||||
|
||||
default:
|
||||
return `/objects/${object_data(object_type).pso_id}.xj`;
|
||||
return `/npcs/${NpcType[type]}.${asset_type === AssetType.Geometry ? "nj" : "xvm"}`;
|
||||
}
|
||||
} else {
|
||||
return `/objects/${object_data(object_type).pso_id}.xvm`;
|
||||
if (asset_type === AssetType.Geometry) {
|
||||
switch (type) {
|
||||
case ObjectType.EasterEgg:
|
||||
case ObjectType.ChristmasTree:
|
||||
case ObjectType.ChristmasWreath:
|
||||
case ObjectType.TwentyFirstCentury:
|
||||
case ObjectType.Sonic:
|
||||
case ObjectType.WelcomeBoard:
|
||||
case ObjectType.FloatingJelifish:
|
||||
case ObjectType.RuinsSeal:
|
||||
case ObjectType.Dolphin:
|
||||
case ObjectType.Cacti:
|
||||
case ObjectType.BigBrownRock:
|
||||
case ObjectType.PoisonPlant:
|
||||
case ObjectType.BigBlackRocks:
|
||||
case ObjectType.FallingRock:
|
||||
case ObjectType.DesertFixedTypeBoxBreakableCrystals:
|
||||
case ObjectType.BeeHive:
|
||||
return `/objects/${object_data(type).pso_id}.nj`;
|
||||
|
||||
default:
|
||||
return `/objects/${object_data(type).pso_id}.xj`;
|
||||
}
|
||||
} else {
|
||||
return `/objects/${object_data(type).pso_id}.xvm`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,13 @@ import Logger from "js-logger";
|
||||
import { Intersection, Mesh, Object3D, Raycaster, Vector3 } from "three";
|
||||
import { QuestRenderer } from "./QuestRenderer";
|
||||
import { QuestModel } from "../model/QuestModel";
|
||||
import {
|
||||
load_npc_geometry,
|
||||
load_npc_textures,
|
||||
load_object_geometry,
|
||||
load_object_textures,
|
||||
} from "../loading/entities";
|
||||
import { load_entity_geometry, load_entity_textures } from "../loading/entities";
|
||||
import { load_area_collision_geometry, load_area_render_geometry } from "../loading/areas";
|
||||
import { QuestEntityModel } from "../model/QuestEntityModel";
|
||||
import { Disposer } from "../../core/observable/Disposer";
|
||||
import { Disposable } from "../../core/observable/Disposable";
|
||||
import { AreaModel } from "../model/AreaModel";
|
||||
import { create_npc_mesh, create_object_mesh } from "./conversion/entities";
|
||||
import { create_entity_mesh } from "./conversion/entities";
|
||||
import { AreaUserData } from "./conversion/areas";
|
||||
import { quest_editor_store } from "../stores/QuestEditorStore";
|
||||
import {
|
||||
@ -271,19 +266,9 @@ class EntityModelManager {
|
||||
}
|
||||
|
||||
private async load(entity: QuestEntityModel): Promise<void> {
|
||||
let model: Mesh;
|
||||
|
||||
if (entity instanceof QuestNpcModel) {
|
||||
const npc_geom = await load_npc_geometry(entity.type);
|
||||
const npc_tex = await load_npc_textures(entity.type);
|
||||
model = create_npc_mesh(entity, npc_geom, npc_tex);
|
||||
} else if (entity instanceof QuestObjectModel) {
|
||||
const object_geom = await load_object_geometry(entity.type);
|
||||
const object_tex = await load_object_textures(entity.type);
|
||||
model = create_object_mesh(entity, object_geom, object_tex);
|
||||
} else {
|
||||
throw new Error(`Unknown entity type ${entity.type}.`);
|
||||
}
|
||||
const geom = await load_entity_geometry(entity.type);
|
||||
const tex = await load_entity_textures(entity.type);
|
||||
const model = create_entity_mesh(entity, geom, tex);
|
||||
|
||||
// The model load might be cancelled by now.
|
||||
if (this.queue.includes(entity)) {
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { QuestEntityModel } from "../../model/QuestEntityModel";
|
||||
import { QuestObjectModel } from "../../model/QuestObjectModel";
|
||||
import { BufferGeometry, DoubleSide, Mesh, MeshLambertMaterial, Texture } from "three";
|
||||
import { ObjectType } from "../../../core/data_formats/parsing/quest/object_types";
|
||||
import { QuestNpcModel } from "../../model/QuestNpcModel";
|
||||
import { NpcType } from "../../../core/data_formats/parsing/quest/npc_types";
|
||||
import { create_mesh } from "../../../core/rendering/conversion/create_mesh";
|
||||
import {
|
||||
entity_type_to_string,
|
||||
EntityType,
|
||||
is_npc_type,
|
||||
} from "../../../core/data_formats/parsing/quest/entities";
|
||||
|
||||
export enum ColorType {
|
||||
Normal,
|
||||
@ -26,37 +27,13 @@ export type EntityUserData = {
|
||||
entity: QuestEntityModel;
|
||||
};
|
||||
|
||||
export function create_object_mesh(
|
||||
object: QuestObjectModel,
|
||||
export function create_entity_type_mesh(
|
||||
type: EntityType,
|
||||
geometry: BufferGeometry,
|
||||
textures: Texture[],
|
||||
): Mesh {
|
||||
return create(
|
||||
object,
|
||||
geometry,
|
||||
textures,
|
||||
OBJECT_COLORS[ColorType.Normal],
|
||||
ObjectType[object.type],
|
||||
);
|
||||
}
|
||||
|
||||
export function create_npc_mesh(
|
||||
npc: QuestNpcModel,
|
||||
geometry: BufferGeometry,
|
||||
textures: Texture[],
|
||||
): Mesh {
|
||||
return create(npc, geometry, textures, NPC_COLORS[ColorType.Normal], NpcType[npc.type]);
|
||||
}
|
||||
|
||||
function create(
|
||||
entity: QuestEntityModel,
|
||||
geometry: BufferGeometry,
|
||||
textures: Texture[],
|
||||
color: number,
|
||||
name: string,
|
||||
): Mesh {
|
||||
const default_material = new MeshLambertMaterial({
|
||||
color,
|
||||
color: is_npc_type(type) ? NPC_COLORS[ColorType.Normal] : OBJECT_COLORS[ColorType.Normal],
|
||||
side: DoubleSide,
|
||||
});
|
||||
|
||||
@ -75,7 +52,18 @@ function create(
|
||||
default_material,
|
||||
);
|
||||
|
||||
mesh.name = name;
|
||||
mesh.name = entity_type_to_string(type);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
export function create_entity_mesh(
|
||||
entity: QuestEntityModel,
|
||||
geometry: BufferGeometry,
|
||||
textures: Texture[],
|
||||
): Mesh {
|
||||
const mesh = create_entity_type_mesh(entity.type, geometry, textures);
|
||||
|
||||
(mesh.userData as EntityUserData).entity = entity;
|
||||
|
||||
const { x, y, z } = entity.world_position.val;
|
||||
|
43
src/quest_editor/rendering/render_entity_to_image.ts
Normal file
43
src/quest_editor/rendering/render_entity_to_image.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { HemisphereLight, PerspectiveCamera, Scene, WebGLRenderer } from "three";
|
||||
import { EntityType } from "../../core/data_formats/parsing/quest/entities";
|
||||
import { load_entity_geometry, load_entity_textures } from "../loading/entities";
|
||||
import { create_entity_type_mesh } from "./conversion/entities";
|
||||
import { sequential } from "../../core/sequential";
|
||||
|
||||
const renderer = new WebGLRenderer({ alpha: true, antialias: true });
|
||||
renderer.setSize(100, 100);
|
||||
|
||||
const camera = new PerspectiveCamera(60, 1, 10, 1000);
|
||||
const light = new HemisphereLight(0xffffff, 0x505050, 1.2);
|
||||
const scene = new Scene();
|
||||
|
||||
const cache: Map<EntityType, Promise<string>> = new Map();
|
||||
|
||||
export async function render_entity_to_image(entity: EntityType): Promise<string> {
|
||||
let url = cache.get(entity);
|
||||
|
||||
if (!url) {
|
||||
url = render(entity);
|
||||
cache.set(entity, url);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
const render = sequential(
|
||||
async (entity: EntityType): Promise<string> => {
|
||||
const geometry = await load_entity_geometry(entity);
|
||||
const textures = await load_entity_textures(entity);
|
||||
|
||||
scene.remove(...scene.children);
|
||||
scene.add(light);
|
||||
scene.add(create_entity_type_mesh(entity, geometry, textures));
|
||||
|
||||
camera.position.set(10, 25, 20);
|
||||
camera.lookAt(0, 10, 0);
|
||||
|
||||
renderer.render(scene, camera);
|
||||
|
||||
return renderer.domElement.toDataURL();
|
||||
},
|
||||
);
|
Loading…
Reference in New Issue
Block a user