mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-05 07:18:29 +08:00
182 lines
5.5 KiB
TypeScript
182 lines
5.5 KiB
TypeScript
import { DisposableThreeRenderer, ThreeRenderer } from "../../core/rendering/ThreeRenderer";
|
|
import { Group, Mesh, MeshLambertMaterial, Object3D, PerspectiveCamera } from "three";
|
|
import { QuestEntityModel } from "../model/QuestEntityModel";
|
|
import { Quest3DModelManager } from "./Quest3DModelManager";
|
|
import { Disposer } from "../../core/observable/Disposer";
|
|
import { ColorType, EntityUserData, NPC_COLORS, OBJECT_COLORS } from "./conversion/entities";
|
|
import { QuestNpcModel } from "../model/QuestNpcModel";
|
|
|
|
export class QuestRenderer extends ThreeRenderer {
|
|
private _collision_geometry = new Object3D();
|
|
private _render_geometry = new Object3D();
|
|
private _entity_models = new Object3D();
|
|
private readonly disposer = new Disposer();
|
|
private readonly entity_to_mesh = new Map<QuestEntityModel, Mesh>();
|
|
private hovered_mesh?: Mesh;
|
|
private selected_mesh?: Mesh;
|
|
|
|
get debug(): boolean {
|
|
return super.debug;
|
|
}
|
|
|
|
set debug(debug: boolean) {
|
|
if (this.debug !== debug) {
|
|
super.debug = debug;
|
|
this._render_geometry.visible = debug;
|
|
this.schedule_render();
|
|
}
|
|
}
|
|
|
|
readonly camera = new PerspectiveCamera(60, 1, 10, 10000);
|
|
|
|
get collision_geometry(): Object3D {
|
|
return this._collision_geometry;
|
|
}
|
|
|
|
set collision_geometry(collision_geometry: Object3D) {
|
|
this.scene.remove(this.collision_geometry);
|
|
this._collision_geometry = collision_geometry;
|
|
this.scene.add(collision_geometry);
|
|
}
|
|
|
|
set render_geometry(render_geometry: Object3D) {
|
|
this.scene.remove(this._render_geometry);
|
|
this._render_geometry = render_geometry;
|
|
render_geometry.visible = this.debug;
|
|
this.scene.add(render_geometry);
|
|
}
|
|
|
|
get entity_models(): Object3D {
|
|
return this._entity_models;
|
|
}
|
|
|
|
selected_entity: QuestEntityModel | undefined;
|
|
|
|
constructor(
|
|
three_renderer: DisposableThreeRenderer,
|
|
create_model_manager: (renderer: QuestRenderer) => Quest3DModelManager,
|
|
) {
|
|
super(three_renderer);
|
|
|
|
this.disposer.add(create_model_manager(this));
|
|
}
|
|
|
|
/**
|
|
* Initialize camera-controls after {@link QuestEntityControls} to ensure correct order of event
|
|
* listener registration. This is a fragile work-around for the fact that camera-controls
|
|
* doesn't support intercepting pointer events.
|
|
*/
|
|
init_camera_controls(): void {
|
|
super.init_camera_controls();
|
|
}
|
|
|
|
dispose(): void {
|
|
super.dispose();
|
|
this.disposer.dispose();
|
|
}
|
|
|
|
set_size(width: number, height: number): void {
|
|
this.camera.aspect = width / height;
|
|
this.camera.updateProjectionMatrix();
|
|
super.set_size(width, height);
|
|
}
|
|
|
|
reset_entity_models(): void {
|
|
this.scene.remove(this._entity_models);
|
|
this._entity_models = new Group();
|
|
this.scene.add(this._entity_models);
|
|
this.entity_to_mesh.clear();
|
|
this.schedule_render();
|
|
}
|
|
|
|
add_entity_model(model: Mesh): void {
|
|
const entity = (model.userData as EntityUserData).entity;
|
|
this._entity_models.add(model);
|
|
this.entity_to_mesh.set(entity, model);
|
|
|
|
if (entity === this.selected_entity) {
|
|
this.mark_selected(model);
|
|
}
|
|
|
|
this.schedule_render();
|
|
}
|
|
|
|
remove_entity_model(entity: QuestEntityModel): void {
|
|
const mesh = this.entity_to_mesh.get(entity);
|
|
|
|
if (mesh) {
|
|
this.entity_to_mesh.delete(entity);
|
|
this._entity_models.remove(mesh);
|
|
this.schedule_render();
|
|
}
|
|
}
|
|
|
|
get_entity_mesh(entity: QuestEntityModel): Mesh | undefined {
|
|
return this.entity_to_mesh.get(entity);
|
|
}
|
|
|
|
mark_selected(entity_mesh: Mesh): void {
|
|
if (entity_mesh === this.hovered_mesh) {
|
|
this.hovered_mesh = undefined;
|
|
}
|
|
|
|
if (entity_mesh !== this.selected_mesh) {
|
|
if (this.selected_mesh) {
|
|
set_color(this.selected_mesh, ColorType.Normal);
|
|
}
|
|
|
|
set_color(entity_mesh, ColorType.Selected);
|
|
|
|
this.schedule_render();
|
|
}
|
|
|
|
this.selected_mesh = entity_mesh;
|
|
}
|
|
|
|
mark_hovered(entity_mesh?: Mesh): void {
|
|
if (!this.selected_mesh || entity_mesh !== this.selected_mesh) {
|
|
if (entity_mesh !== this.hovered_mesh) {
|
|
if (this.hovered_mesh) {
|
|
set_color(this.hovered_mesh, ColorType.Normal);
|
|
}
|
|
|
|
if (entity_mesh) {
|
|
set_color(entity_mesh, ColorType.Hovered);
|
|
}
|
|
|
|
this.schedule_render();
|
|
}
|
|
|
|
this.hovered_mesh = entity_mesh;
|
|
}
|
|
}
|
|
|
|
unmark_selected(): void {
|
|
if (this.selected_mesh) {
|
|
set_color(this.selected_mesh, ColorType.Normal);
|
|
this.schedule_render();
|
|
}
|
|
|
|
this.selected_mesh = undefined;
|
|
}
|
|
}
|
|
|
|
function set_color(mesh: Mesh, type: ColorType): void {
|
|
const entity = (mesh.userData as EntityUserData).entity;
|
|
const color = entity instanceof QuestNpcModel ? NPC_COLORS[type] : OBJECT_COLORS[type];
|
|
|
|
if (mesh) {
|
|
if (Array.isArray(mesh.material)) {
|
|
for (const mat of mesh.material as MeshLambertMaterial[]) {
|
|
if (type === ColorType.Normal && mat.map) {
|
|
mat.color.set(0xffffff);
|
|
} else {
|
|
mat.color.set(color);
|
|
}
|
|
}
|
|
} else {
|
|
(mesh.material as MeshLambertMaterial).color.set(color);
|
|
}
|
|
}
|
|
}
|