Model assets are now reloaded when the model property is changed.

This commit is contained in:
Daan Vanden Bosch 2020-09-24 19:52:02 +02:00
parent edba25c3bd
commit 5d3d6a24ba
5 changed files with 144 additions and 23 deletions

View File

@ -16,6 +16,7 @@ import { Vec3 } from "../../core/data_formats/vector";
import { WritableListProperty } from "../../core/observable/property/list/WritableListProperty";
import { QuestEntityPropModel } from "./QuestEntityPropModel";
import { ListProperty } from "../../core/observable/property/list/ListProperty";
import { ObjectType } from "../../core/data_formats/parsing/quest/object_types";
// These quaternions are used as temporary variables to avoid memory allocation.
const q1 = new Quaternion();
@ -25,6 +26,7 @@ export abstract class QuestEntityModel<
Type extends EntityType = EntityType,
Entity extends QuestEntity = QuestEntity
> {
private readonly _model: WritableProperty<number | undefined>;
private readonly _section_id: WritableProperty<number>;
private readonly _section: WritableProperty<SectionModel | undefined> = property(undefined);
private readonly _position: WritableProperty<Vector3>;
@ -34,14 +36,14 @@ export abstract class QuestEntityModel<
private readonly _props: WritableListProperty<QuestEntityPropModel>;
/**
* Many modifications done to the underlying entity directly will not be reflected in this
* model's properties.
* Don't modify the underlying entity directly because most of those modifications will not be
* reflected in this model's properties.
*/
readonly entity: Entity;
abstract readonly type: Type;
abstract readonly model?: number;
readonly model: Property<number | undefined>;
get area_id(): number {
return this.entity.area_id;
@ -70,6 +72,9 @@ export abstract class QuestEntityModel<
protected constructor(entity: Entity) {
this.entity = entity;
this._model = property(this.get_entity_model());
this.model = this._model;
this.section = this._section;
this._section_id = property(this.get_entity_section_id());
@ -95,12 +100,61 @@ export abstract class QuestEntityModel<
this._props = list_property(
undefined,
...entity_data(get_entity_type(entity)).properties.map(
p => new QuestEntityPropModel(entity, p),
p => new QuestEntityPropModel(this, p),
),
);
this.props = this._props;
}
set_model(model: number, propagate_to_props: boolean = true): this {
this._model.val = model;
if (propagate_to_props) {
let props: QuestEntityPropModel[];
switch (this.type) {
case ObjectType.Probe:
props = this.props.val.filter(p => p.offset === 40);
break;
case ObjectType.Saw:
case ObjectType.LaserDetect:
props = this.props.val.filter(p => p.offset === 48);
break;
case ObjectType.Sonic:
case ObjectType.LittleCryotube:
case ObjectType.Cactus:
case ObjectType.BigBrownRock:
case ObjectType.BigBlackRocks:
case ObjectType.BeeHive:
props = this.props.val.filter(p => p.offset === 52);
break;
case ObjectType.ForestConsole:
props = this.props.val.filter(p => p.offset === 56);
break;
case ObjectType.PrincipalWarp:
case ObjectType.LaserFence:
case ObjectType.LaserSquareFence:
case ObjectType.LaserFenceEx:
case ObjectType.LaserSquareFenceEx:
props = this.props.val.filter(p => p.offset === 60);
break;
default:
return this;
}
for (const prop of props) {
prop.set_value(model, false);
}
}
return this;
}
set_section(section: SectionModel): this {
if (section.area_variant.area.id !== this.area_id) {
throw new Error(`Quest entities can't be moved across areas.`);
@ -191,6 +245,8 @@ export abstract class QuestEntityModel<
return this;
}
protected abstract get_entity_model(): number | undefined;
protected abstract get_entity_section_id(): number;
protected abstract set_entity_section_id(section_id: number): void;

View File

@ -4,33 +4,78 @@ import { EntityProp, EntityPropType } from "../../core/data_formats/parsing/ques
import { property } from "../../core/observable";
import {
get_entity_prop_value,
QuestEntity,
set_entity_prop_value,
} from "../../core/data_formats/parsing/quest/Quest";
import { QuestEntityModel } from "./QuestEntityModel";
import { ObjectType } from "../../core/data_formats/parsing/quest/object_types";
export class QuestEntityPropModel {
private readonly entity: QuestEntity;
private readonly entity: QuestEntityModel;
private readonly prop: EntityProp;
private readonly _value: WritableProperty<number>;
private readonly affects_model: boolean;
readonly name: string;
readonly offset: number;
readonly type: EntityPropType;
readonly value: Property<number>;
constructor(quest_entity: QuestEntity, entity_prop: EntityProp) {
this.entity = quest_entity;
constructor(entity: QuestEntityModel, entity_prop: EntityProp) {
this.entity = entity;
this.prop = entity_prop;
this.name = entity_prop.name;
this.offset = entity_prop.offset;
this.type = entity_prop.type;
this._value = property(get_entity_prop_value(quest_entity, entity_prop));
this._value = property(get_entity_prop_value(entity.entity, entity_prop));
this.value = this._value;
switch (this.entity.type) {
case ObjectType.Probe:
this.affects_model = entity_prop.offset === 40;
break;
case ObjectType.Saw:
case ObjectType.LaserDetect:
this.affects_model = entity_prop.offset === 48;
break;
case ObjectType.Sonic:
case ObjectType.LittleCryotube:
case ObjectType.Cactus:
case ObjectType.BigBrownRock:
case ObjectType.BigBlackRocks:
case ObjectType.BeeHive:
this.affects_model = entity_prop.offset === 52;
break;
case ObjectType.ForestConsole:
this.affects_model = entity_prop.offset === 56;
break;
case ObjectType.PrincipalWarp:
case ObjectType.LaserFence:
case ObjectType.LaserSquareFence:
case ObjectType.LaserFenceEx:
case ObjectType.LaserSquareFenceEx:
this.affects_model = entity_prop.offset === 60;
break;
default:
this.affects_model = false;
break;
}
}
set_value(value: number): void {
set_entity_prop_value(this.entity, this.prop, value);
set_value(value: number, propagate_to_entity: boolean = true): void {
set_entity_prop_value(this.entity.entity, this.prop, value);
this._value.val = value;
if (propagate_to_entity && this.affects_model) {
this.entity.set_model(value, false);
}
}
}

View File

@ -24,8 +24,6 @@ export class QuestNpcModel extends QuestEntityModel<NpcType, QuestNpc> {
return get_npc_type(this.entity);
}
readonly model?: number;
private readonly _wave: WritableProperty<WaveModel | undefined>;
readonly wave: Property<WaveModel | undefined>;
@ -47,6 +45,10 @@ export class QuestNpcModel extends QuestEntityModel<NpcType, QuestNpc> {
return this;
}
protected get_entity_model(): undefined {
return undefined;
}
protected get_entity_section_id(): number {
return get_npc_section_id(this.entity);
}

View File

@ -19,16 +19,16 @@ export class QuestObjectModel extends QuestEntityModel<ObjectType, QuestObject>
return get_object_type(this.entity);
}
get model(): number | undefined {
return get_object_model(this.entity);
}
constructor(object: QuestObject) {
defined(object, "object");
super(object);
}
protected get_entity_model(): number | undefined {
return get_object_model(this.entity);
}
protected get_entity_section_id(): number {
return get_object_section_id(this.entity);
}

View File

@ -281,14 +281,27 @@ class Entity3DModelManager {
}
private async load(entity: QuestEntityModel): Promise<void> {
const geom = await this.entity_asset_loader.load_geometry(entity.type, entity.model);
if (!this.queue.includes(entity)) return; // Could be cancelled by now.
let orig_model: number | undefined;
const tex = await this.entity_asset_loader.load_textures(entity.type, entity.model);
if (!this.queue.includes(entity)) return; // Could be cancelled by now.
while (true) {
orig_model = entity.model.val;
const model = create_entity_mesh(entity, geom, tex);
this.update_entity_geometry(entity, model);
const geom = await this.entity_asset_loader.load_geometry(
entity.type,
entity.model.val,
);
if (!this.queue.includes(entity)) return; // Could be cancelled by now.
if (entity.model.val != orig_model) continue; // Load again if model changed.
const tex = await this.entity_asset_loader.load_textures(entity.type, entity.model.val);
if (!this.queue.includes(entity)) return; // Could be cancelled by now.
if (entity.model.val != orig_model) continue; // Load again if model changed.
const model = create_entity_mesh(entity, geom, tex);
this.update_entity_geometry(entity, model);
break;
}
}
private update_entity_geometry(entity: QuestEntityModel, model: Mesh): void {
@ -304,6 +317,11 @@ class Entity3DModelManager {
model.rotation.copy(value);
this.renderer.schedule_render();
}),
entity.model.observe(() => {
this.remove([entity]);
this.add([entity]);
}),
);
if (entity instanceof QuestNpcModel) {