forked from mirror/ObjToSchematic
Finished moving material editing to the new material step
This commit is contained in:
parent
ac0e1eb22b
commit
eb139e9cad
@ -3,39 +3,37 @@ import path from 'path';
|
||||
|
||||
import { FallableBehaviour } from './block_mesh';
|
||||
import { ArcballCamera } from './camera';
|
||||
import { RGBA, RGBAUtil } from './colour';
|
||||
import { AppConfig } from './config';
|
||||
import { EAppEvent, EventManager } from './event';
|
||||
import { IExporter } from './exporters/base_exporter';
|
||||
import { ExporterFactory, TExporters } from './exporters/exporters';
|
||||
import { MaterialMap, MaterialType, SolidMaterial, TexturedMaterial } from './mesh';
|
||||
import { Renderer } from './renderer';
|
||||
import { MaterialMapManager } from './material-map';
|
||||
import { MaterialType } from './mesh';
|
||||
import { MeshType, Renderer } from './renderer';
|
||||
import { StatusHandler, StatusMessage } from './status';
|
||||
import { CheckboxElement } from './ui/elements/checkbox';
|
||||
import { SolidMaterialUIElement, TextureMaterialUIElement } from './ui/elements/material';
|
||||
import { OutputStyle } from './ui/elements/output';
|
||||
import { SolidMaterialElement } from './ui/elements/solid_material_element';
|
||||
import { TexturedMaterialElement } from './ui/elements/textured_material_element';
|
||||
import { UI } from './ui/layout';
|
||||
import { UIMessageBuilder, UITreeBuilder } from './ui/misc';
|
||||
import { UIMessageBuilder } from './ui/misc';
|
||||
import { ColourSpace, EAction } from './util';
|
||||
import { ASSERT } from './util/error_util';
|
||||
import { FileUtil } from './util/file_util';
|
||||
import { LOG_ERROR, Logger } from './util/log_util';
|
||||
import { AppPaths, PathUtil } from './util/path_util';
|
||||
import { AppPaths } from './util/path_util';
|
||||
import { Vector3 } from './vector';
|
||||
import { TWorkerJob, WorkerController } from './worker_controller';
|
||||
import { SetMaterialsParams, TFromWorkerMessage, TToWorkerMessage } from './worker_types';
|
||||
import { TFromWorkerMessage, TToWorkerMessage } from './worker_types';
|
||||
|
||||
export class AppContext {
|
||||
private _ui: UI;
|
||||
private _workerController: WorkerController;
|
||||
private _lastAction?: EAction;
|
||||
public maxConstraint?: Vector3;
|
||||
private _materialMap: MaterialMap;
|
||||
private _materialManager: MaterialMapManager;
|
||||
|
||||
public constructor() {
|
||||
this._materialMap = {};
|
||||
this._materialManager = new MaterialMapManager(new Map());
|
||||
|
||||
Logger.Get.enableLogToFile();
|
||||
Logger.Get.initLogFile('client');
|
||||
@ -206,8 +204,7 @@ export class AppContext {
|
||||
);
|
||||
dimensions.mulScalar(AppConfig.Get.CONSTRAINT_MAXIMUM_HEIGHT / 8.0).floor();
|
||||
this.maxConstraint = dimensions;
|
||||
this._materialMap = payload.result.materials;
|
||||
this._onMaterialMapChanged();
|
||||
this._materialManager = new MaterialMapManager(payload.result.materials);
|
||||
|
||||
if (payload.result.triangleCount < AppConfig.Get.RENDER_TRIANGLE_THRESHOLD) {
|
||||
outputElement.setTaskInProgress('render', '[Renderer]: Processing...');
|
||||
@ -217,8 +214,9 @@ export class AppContext {
|
||||
outputElement.setTaskComplete('render', '[Renderer]: Stopped', [message], 'warning');
|
||||
}
|
||||
|
||||
this._updateMaterialsAction(payload.result.materials);
|
||||
this._updateMaterialsAction();
|
||||
};
|
||||
callback.bind(this);
|
||||
|
||||
return { id: 'Import', payload: payload, callback: callback };
|
||||
}
|
||||
@ -230,7 +228,7 @@ export class AppContext {
|
||||
const payload: TToWorkerMessage = {
|
||||
action: 'SetMaterials',
|
||||
params: {
|
||||
materials: this._materialMap,
|
||||
materials: this._materialManager.materials,
|
||||
},
|
||||
};
|
||||
|
||||
@ -250,8 +248,10 @@ export class AppContext {
|
||||
}
|
||||
|
||||
payload.result.materialsChanged.forEach((materialName) => {
|
||||
const material = this._materialMap[materialName];
|
||||
const material = this._materialManager.materials.get(materialName);
|
||||
ASSERT(material !== undefined);
|
||||
Renderer.Get.recreateMaterialBuffer(materialName, material);
|
||||
Renderer.Get.setModelToUse(MeshType.TriangleMesh);
|
||||
});
|
||||
|
||||
this._ui.enableTo(EAction.Voxelise);
|
||||
@ -260,204 +260,32 @@ export class AppContext {
|
||||
return { id: 'Import', payload: payload, callback: callback };
|
||||
}
|
||||
|
||||
private _updateMaterialsAction(materials: MaterialMap) {
|
||||
private _updateMaterialsAction() {
|
||||
this._ui.layoutDull['materials'].elements = {};
|
||||
this._ui.layoutDull['materials'].elementsOrder = [];
|
||||
|
||||
for (const materialName in materials) {
|
||||
const material = this._materialMap[materialName];
|
||||
this._materialManager.materials.forEach((material, materialName) => {
|
||||
if (material.type === MaterialType.solid) {
|
||||
this._ui.layoutDull['materials'].elements[`mat_${materialName}`] = new SolidMaterialElement(materialName, material)
|
||||
.setLabel(materialName);
|
||||
.setLabel(materialName)
|
||||
.onChangeTypeDelegate(() => {
|
||||
this._materialManager.changeMaterialType(materialName, MaterialType.textured);
|
||||
this._updateMaterialsAction();
|
||||
});
|
||||
} else {
|
||||
this._ui.layoutDull['materials'].elements[`mat_${materialName}`] = new TexturedMaterialElement(materialName, material)
|
||||
.setLabel(materialName);
|
||||
.setLabel(materialName)
|
||||
.onChangeTypeDelegate(() => {
|
||||
this._materialManager.changeMaterialType(materialName, MaterialType.solid);
|
||||
this._updateMaterialsAction();
|
||||
});
|
||||
}
|
||||
|
||||
this._ui.layoutDull['materials'].elementsOrder.push(`mat_${materialName}`);
|
||||
}
|
||||
});
|
||||
this._ui.refreshSubcomponents(this._ui.layoutDull['materials']);
|
||||
}
|
||||
|
||||
private _sendMaterialsToWorker(callback: (result: SetMaterialsParams.Output) => void) {
|
||||
const payload: TToWorkerMessage = {
|
||||
action: 'SetMaterials',
|
||||
params: {
|
||||
materials: this._materialMap,
|
||||
},
|
||||
};
|
||||
const job: TWorkerJob = {
|
||||
id: 'SetMaterial',
|
||||
payload: payload,
|
||||
callback: (result: TFromWorkerMessage) => {
|
||||
ASSERT(result.action === 'SetMaterials');
|
||||
// TODO: Check the action didn't fail
|
||||
this._materialMap = result.result.materials;
|
||||
this._onMaterialMapChanged();
|
||||
this._ui.enableTo(EAction.Voxelise);
|
||||
callback(result.result);
|
||||
},
|
||||
};
|
||||
|
||||
this._workerController.addJob(job);
|
||||
this._ui.disableAll();
|
||||
}
|
||||
|
||||
public onMaterialTypeSwitched(materialName: string) {
|
||||
const oldMaterial = this._materialMap[materialName];
|
||||
|
||||
if (oldMaterial.type == MaterialType.textured) {
|
||||
this._materialMap[materialName] = {
|
||||
type: MaterialType.solid,
|
||||
colour: RGBAUtil.random(),
|
||||
edited: true,
|
||||
canBeTextured: oldMaterial.canBeTextured,
|
||||
set: false,
|
||||
open: true,
|
||||
};
|
||||
} else {
|
||||
this._materialMap[materialName] = {
|
||||
type: MaterialType.textured,
|
||||
alphaFactor: 1.0,
|
||||
path: PathUtil.join(AppPaths.Get.static, 'debug.png'),
|
||||
edited: true,
|
||||
canBeTextured: oldMaterial.canBeTextured,
|
||||
extension: 'repeat',
|
||||
interpolation: 'linear',
|
||||
open: true,
|
||||
};
|
||||
}
|
||||
|
||||
this._sendMaterialsToWorker((result: SetMaterialsParams.Output) => {
|
||||
// TODO: Check the action didn't fail
|
||||
Renderer.Get.recreateMaterialBuffer(materialName, result.materials[materialName]);
|
||||
});
|
||||
}
|
||||
|
||||
public onMaterialExtensionChanged(materialName: string) {
|
||||
const oldMaterial = this._materialMap[materialName];
|
||||
ASSERT(oldMaterial.type === MaterialType.textured);
|
||||
|
||||
this._materialMap[materialName] = {
|
||||
type: MaterialType.textured,
|
||||
alphaFactor: oldMaterial.alphaFactor,
|
||||
alphaPath: oldMaterial.alphaPath,
|
||||
path: oldMaterial.path,
|
||||
edited: true,
|
||||
canBeTextured: oldMaterial.canBeTextured,
|
||||
extension: oldMaterial.extension === 'clamp' ? 'repeat' : 'clamp',
|
||||
interpolation: oldMaterial.interpolation,
|
||||
open: true,
|
||||
};
|
||||
|
||||
this._sendMaterialsToWorker((result: SetMaterialsParams.Output) => {
|
||||
// TODO: Check the action didn't fail
|
||||
Renderer.Get.recreateMaterialBuffer(materialName, result.materials[materialName]);
|
||||
});
|
||||
}
|
||||
|
||||
public onMaterialInterpolationChanged(materialName: string) {
|
||||
const oldMaterial = this._materialMap[materialName];
|
||||
ASSERT(oldMaterial.type === MaterialType.textured);
|
||||
|
||||
this._materialMap[materialName] = {
|
||||
type: MaterialType.textured,
|
||||
alphaFactor: oldMaterial.alphaFactor,
|
||||
alphaPath: oldMaterial.alphaPath,
|
||||
path: oldMaterial.path,
|
||||
edited: true,
|
||||
canBeTextured: oldMaterial.canBeTextured,
|
||||
extension: oldMaterial.extension,
|
||||
interpolation: oldMaterial.interpolation === 'linear' ? 'nearest' : 'linear',
|
||||
open: true,
|
||||
};
|
||||
|
||||
this._sendMaterialsToWorker((result: SetMaterialsParams.Output) => {
|
||||
// TODO: Check the action didn't fail
|
||||
Renderer.Get.recreateMaterialBuffer(materialName, result.materials[materialName]);
|
||||
});
|
||||
}
|
||||
|
||||
public onMaterialTextureReplace(materialName: string, newTexturePath: string) {
|
||||
const oldMaterial = this._materialMap[materialName];
|
||||
ASSERT(oldMaterial.type === MaterialType.textured);
|
||||
|
||||
this._materialMap[materialName] = {
|
||||
type: MaterialType.textured,
|
||||
alphaFactor: oldMaterial.alphaFactor,
|
||||
alphaPath: oldMaterial.alphaPath,
|
||||
path: newTexturePath,
|
||||
edited: true,
|
||||
canBeTextured: oldMaterial.canBeTextured,
|
||||
extension: oldMaterial.extension,
|
||||
interpolation: oldMaterial.interpolation,
|
||||
open: true,
|
||||
};
|
||||
|
||||
this._sendMaterialsToWorker((result: SetMaterialsParams.Output) => {
|
||||
Renderer.Get.updateMeshMaterialTexture(materialName, result.materials[materialName] as TexturedMaterial);
|
||||
});
|
||||
}
|
||||
|
||||
public onMaterialColourChanged(materialName: string, newColour: RGBA) {
|
||||
ASSERT(this._materialMap[materialName].type === MaterialType.solid);
|
||||
const oldMaterial = this._materialMap[materialName] as TexturedMaterial;
|
||||
this._materialMap[materialName] = {
|
||||
type: MaterialType.solid,
|
||||
colour: newColour,
|
||||
edited: true,
|
||||
canBeTextured: oldMaterial.canBeTextured,
|
||||
set: true,
|
||||
open: true,
|
||||
};
|
||||
|
||||
this._sendMaterialsToWorker((result: SetMaterialsParams.Output) => {
|
||||
Renderer.Get.recreateMaterialBuffer(materialName, result.materials[materialName] as SolidMaterial);
|
||||
});
|
||||
}
|
||||
|
||||
private _onMaterialMapChanged() {
|
||||
// Add material information to the output log
|
||||
const outputElement = this._ui.getActionOutput(EAction.Import);
|
||||
|
||||
const messageBuilder = outputElement.getMessage();
|
||||
const tree = UITreeBuilder.create('Materials');
|
||||
tree.toggleIsOpen();
|
||||
|
||||
for (const [materialName, material] of Object.entries(this._materialMap)) {
|
||||
if (materialName === 'DEFAULT_UNASSIGNED') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const subTree = UITreeBuilder.create(material.edited ? `<i>${materialName}*</i>` : materialName);
|
||||
|
||||
if (material.open) {
|
||||
subTree.toggleIsOpen();
|
||||
}
|
||||
|
||||
if (material.type === MaterialType.solid) {
|
||||
const uiElement = new SolidMaterialUIElement(materialName, this, material);
|
||||
|
||||
subTree.addChild({ html: uiElement.buildHTML(), warning: uiElement.hasWarning()}, () => {
|
||||
uiElement.registerEvents();
|
||||
});
|
||||
} else {
|
||||
const uiElement = new TextureMaterialUIElement(materialName, this, material);
|
||||
|
||||
subTree.addChild({ html: uiElement.buildHTML(), warning: uiElement.hasWarning()}, () => {
|
||||
uiElement.registerEvents();
|
||||
});
|
||||
}
|
||||
|
||||
tree.addChild(subTree);
|
||||
}
|
||||
|
||||
messageBuilder.setTree('materials', tree);
|
||||
outputElement.updateMessage();
|
||||
|
||||
this._updateMaterialsAction(this._materialMap);
|
||||
}
|
||||
|
||||
private _renderMesh(): TWorkerJob {
|
||||
const payload: TToWorkerMessage = {
|
||||
action: 'RenderMesh',
|
||||
|
@ -243,6 +243,8 @@ export class BufferGenerator {
|
||||
}
|
||||
|
||||
const material = mesh.getMaterialByName(materialName);
|
||||
ASSERT(material !== undefined);
|
||||
|
||||
materialBuffers.push({
|
||||
buffer: materialBuffer,
|
||||
material: material,
|
||||
|
@ -21,9 +21,19 @@ export class ObjImporter extends IImporter {
|
||||
private _uvs: UV[] = [];
|
||||
private _tris: Tri[] = [];
|
||||
|
||||
private _materials: { [key: string]: (SolidMaterial | TexturedMaterial) } = {
|
||||
'DEFAULT_UNASSIGNED': { type: MaterialType.solid, colour: RGBAColours.WHITE, edited: true, canBeTextured: false, set: true, open: false },
|
||||
};
|
||||
private _materials: Map<string, SolidMaterial | TexturedMaterial>;
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
this._materials = new Map();
|
||||
this._materials.set('DEFAULT_UNASSIGNED', {
|
||||
type: MaterialType.solid,
|
||||
colour: RGBAColours.WHITE,
|
||||
canBeTextured: false,
|
||||
needsAttention: false,
|
||||
});
|
||||
}
|
||||
|
||||
private _mtlLibs: string[] = [];
|
||||
private _currentMaterialName: string = 'DEFAULT_UNASSIGNED';
|
||||
|
||||
@ -392,20 +402,19 @@ export class ObjImporter extends IImporter {
|
||||
private _addCurrentMaterial() {
|
||||
if (this._materialReady && this._currentMaterialName !== '') {
|
||||
if (this._currentTexture !== '') {
|
||||
this._materials[this._currentMaterialName] = {
|
||||
this._materials.set(this._currentMaterialName, {
|
||||
type: MaterialType.textured,
|
||||
path: this._currentTexture,
|
||||
alphaPath: this._currentTransparencyTexture === '' ? undefined : this._currentTransparencyTexture,
|
||||
alphaFactor: this._currentAlpha,
|
||||
edited: false,
|
||||
canBeTextured: true,
|
||||
extension: 'repeat',
|
||||
interpolation: 'linear',
|
||||
open: false,
|
||||
};
|
||||
needsAttention: false,
|
||||
});
|
||||
this._currentTransparencyTexture = '';
|
||||
} else {
|
||||
this._materials[this._currentMaterialName] = {
|
||||
this._materials.set(this._currentMaterialName, {
|
||||
type: MaterialType.solid,
|
||||
colour: {
|
||||
r: this._currentColour.r,
|
||||
@ -413,11 +422,9 @@ export class ObjImporter extends IImporter {
|
||||
b: this._currentColour.b,
|
||||
a: this._currentAlpha,
|
||||
},
|
||||
edited: false,
|
||||
canBeTextured: false,
|
||||
set: true,
|
||||
open: false,
|
||||
};
|
||||
needsAttention: false,
|
||||
});
|
||||
}
|
||||
this._currentAlpha = 1.0;
|
||||
}
|
||||
|
48
src/material-map.ts
Normal file
48
src/material-map.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { RGBAColours, RGBAUtil } from './colour';
|
||||
import { MaterialMap, MaterialType } from './mesh';
|
||||
import { ASSERT } from './util/error_util';
|
||||
import { AppPaths, PathUtil } from './util/path_util';
|
||||
|
||||
export class MaterialMapManager {
|
||||
public materials: MaterialMap;
|
||||
|
||||
public constructor(materials: MaterialMap) {
|
||||
this.materials = materials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a material to a new type, i.e. textured to solid.
|
||||
* Will return if the material is already the given type.
|
||||
*/
|
||||
public changeMaterialType(materialName: string, newMaterialType: MaterialType) {
|
||||
const currentMaterial = this.materials.get(materialName);
|
||||
ASSERT(currentMaterial !== undefined, 'Cannot change material type of non-existent material');
|
||||
|
||||
if (currentMaterial.type === newMaterialType) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (newMaterialType) {
|
||||
case MaterialType.solid:
|
||||
this.materials.set(materialName, {
|
||||
type: MaterialType.solid,
|
||||
colour: RGBAColours.MAGENTA,
|
||||
canBeTextured: currentMaterial.canBeTextured,
|
||||
needsAttention: true,
|
||||
});
|
||||
break;
|
||||
case MaterialType.textured:
|
||||
this.materials.set(materialName, {
|
||||
type: MaterialType.textured,
|
||||
alphaFactor: 1.0,
|
||||
alphaPath: undefined,
|
||||
canBeTextured: currentMaterial.canBeTextured,
|
||||
extension: 'repeat',
|
||||
interpolation: 'linear',
|
||||
needsAttention: true,
|
||||
path: PathUtil.join(AppPaths.Get.static, 'debug.png'),
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
61
src/mesh.ts
61
src/mesh.ts
@ -29,15 +29,13 @@ export interface Tri {
|
||||
export enum MaterialType { solid, textured }
|
||||
/* eslint-enable */
|
||||
type BaseMaterial = {
|
||||
edited: boolean,
|
||||
canBeTextured: boolean,
|
||||
open: boolean, // TODO: Refactor, this is UI specific, shouldn't exist here
|
||||
needsAttention: boolean, // True if the user should make edits to this material
|
||||
}
|
||||
|
||||
export type SolidMaterial = BaseMaterial & {
|
||||
type: MaterialType.solid,
|
||||
colour: RGBA,
|
||||
set: boolean,
|
||||
}
|
||||
export type TexturedMaterial = BaseMaterial & {
|
||||
type: MaterialType.textured,
|
||||
@ -47,7 +45,7 @@ export type TexturedMaterial = BaseMaterial & {
|
||||
interpolation: TTexelInterpolation,
|
||||
extension: TTexelExtension,
|
||||
}
|
||||
export type MaterialMap = { [key: string]: (SolidMaterial | TexturedMaterial) };
|
||||
export type MaterialMap = Map<string, SolidMaterial | TexturedMaterial>;
|
||||
|
||||
export class Mesh {
|
||||
public readonly id: string;
|
||||
@ -159,7 +157,7 @@ export class Mesh {
|
||||
}
|
||||
|
||||
private _checkMaterials() {
|
||||
if (Object.keys(this._materials).length === 0) {
|
||||
if (this._materials.size === 0) {
|
||||
throw new AppError('Loaded mesh has no materials');
|
||||
}
|
||||
|
||||
@ -168,30 +166,26 @@ export class Mesh {
|
||||
const usedMaterials = new Set<string>();
|
||||
const missingMaterials = new Set<string>();
|
||||
for (const tri of this._tris) {
|
||||
if (!(tri.material in this._materials)) {
|
||||
if (!this._materials.has(tri.material)) {
|
||||
// This triangle makes use of a material we don't have info about
|
||||
// Try infer details about this material and add it to our materials
|
||||
|
||||
if (tri.texcoordIndices === undefined) {
|
||||
// No texcoords are defined, therefore make a solid material
|
||||
this._materials[tri.material] = {
|
||||
this._materials.set(tri.material, {
|
||||
type: MaterialType.solid,
|
||||
colour: RGBAColours.MAGENTA,
|
||||
edited: true,
|
||||
canBeTextured: false,
|
||||
set: false,
|
||||
open: false,
|
||||
};
|
||||
needsAttention: true,
|
||||
});
|
||||
} else {
|
||||
// Texcoords exist
|
||||
this._materials[tri.material] = {
|
||||
this._materials.set(tri.material, {
|
||||
type: MaterialType.solid,
|
||||
colour: RGBAUtil.random(),
|
||||
edited: true,
|
||||
canBeTextured: true,
|
||||
set: false,
|
||||
open: false,
|
||||
};
|
||||
needsAttention: true,
|
||||
});
|
||||
}
|
||||
|
||||
missingMaterials.add(tri.material);
|
||||
@ -201,14 +195,15 @@ export class Mesh {
|
||||
}
|
||||
|
||||
const materialsToRemove = new Set<string>();
|
||||
for (const materialName in this._materials) {
|
||||
this._materials.forEach((material, materialName) => {
|
||||
if (!usedMaterials.has(materialName)) {
|
||||
LOG_WARN(`'${materialName}' is not used by any triangles, removing...`);
|
||||
materialsToRemove.add(materialName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
materialsToRemove.forEach((materialName) => {
|
||||
delete this._materials[materialName];
|
||||
this._materials.delete(materialName);
|
||||
});
|
||||
|
||||
if (missingMaterials.size > 0) {
|
||||
@ -216,20 +211,17 @@ export class Mesh {
|
||||
}
|
||||
|
||||
// Check texture paths are absolute and exist
|
||||
for (const materialName in this._materials) {
|
||||
const material = this._materials[materialName];
|
||||
this._materials.forEach((material, materialName) => {
|
||||
if (material.type === MaterialType.textured) {
|
||||
ASSERT(path.isAbsolute(material.path), 'Material texture path not absolute');
|
||||
if (!fs.existsSync(material.path)) {
|
||||
LOG_WARN(`Could not find ${material.path} for material ${materialName}, changing to solid-white material`);
|
||||
this._materials[materialName] = {
|
||||
LOG_WARN(`Could not find ${material.path} for material ${materialName}, changing to solid material`);
|
||||
this._materials.set(materialName, {
|
||||
type: MaterialType.solid,
|
||||
colour: RGBAColours.WHITE,
|
||||
edited: true,
|
||||
colour: RGBAColours.MAGENTA,
|
||||
canBeTextured: true,
|
||||
set: false,
|
||||
open: false,
|
||||
};
|
||||
needsAttention: true,
|
||||
});
|
||||
} else {
|
||||
const parsedPath = path.parse(material.path);
|
||||
if (parsedPath.ext === '.tga') {
|
||||
@ -237,7 +229,7 @@ export class Mesh {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _centreMesh() {
|
||||
@ -266,7 +258,9 @@ export class Mesh {
|
||||
private _loadTextures() {
|
||||
this._loadedTextures = {};
|
||||
for (const tri of this._tris) {
|
||||
const material = this._materials[tri.material];
|
||||
const material = this._materials.get(tri.material);
|
||||
ASSERT(material !== undefined, 'Triangle uses a material that doesn\'t exist in the material map');
|
||||
|
||||
if (material.type == MaterialType.textured) {
|
||||
if (!(tri.material in this._loadedTextures)) {
|
||||
this._loadedTextures[tri.material] = new Texture(material.path, material.alphaPath);
|
||||
@ -348,7 +342,7 @@ export class Mesh {
|
||||
}
|
||||
|
||||
public getMaterialByName(materialName: string) {
|
||||
return this._materials[materialName];
|
||||
return this._materials.get(materialName);
|
||||
}
|
||||
|
||||
public setMaterials(materialMap: MaterialMap) {
|
||||
@ -361,9 +355,8 @@ export class Mesh {
|
||||
}
|
||||
|
||||
public sampleTextureMaterial(materialName: string, uv: UV): RGBA {
|
||||
ASSERT(materialName in this._materials, `Sampling material that does not exist: ${materialName}`);
|
||||
|
||||
const material = this._materials[materialName];
|
||||
const material = this._materials.get(materialName);
|
||||
ASSERT(material !== undefined, `Sampling material that does not exist: ${materialName}`);
|
||||
ASSERT(material.type === MaterialType.textured, 'Sampling texture material of non-texture material');
|
||||
|
||||
ASSERT(materialName in this._loadedTextures, 'Sampling texture that is not loaded');
|
||||
|
@ -198,7 +198,12 @@ export class Renderer {
|
||||
if (material.type === MaterialType.solid) {
|
||||
this._materialBuffers.set(materialName, {
|
||||
buffer: oldBuffer.buffer,
|
||||
material: material,
|
||||
material: {
|
||||
type: MaterialType.solid,
|
||||
colour: RGBAUtil.copy(material.colour),
|
||||
needsAttention: material.needsAttention,
|
||||
canBeTextured: material.canBeTextured,
|
||||
},
|
||||
numElements: oldBuffer.numElements,
|
||||
materialName: materialName,
|
||||
});
|
||||
@ -208,7 +213,6 @@ export class Renderer {
|
||||
material: {
|
||||
type: MaterialType.textured,
|
||||
path: material.path,
|
||||
edited: material.edited,
|
||||
canBeTextured: material.canBeTextured,
|
||||
interpolation: material.interpolation,
|
||||
extension: material.extension,
|
||||
@ -226,7 +230,7 @@ export class Renderer {
|
||||
wrap: material.extension === 'clamp' ? this._gl.CLAMP_TO_EDGE : this._gl.REPEAT,
|
||||
}) : undefined,
|
||||
useAlphaChannel: material.alphaPath ? new Texture(material.path, material.alphaPath)._useAlphaChannel() : undefined,
|
||||
open: material.open,
|
||||
needsAttention: material.needsAttention,
|
||||
},
|
||||
numElements: oldBuffer.numElements,
|
||||
materialName: materialName,
|
||||
@ -240,7 +244,6 @@ export class Renderer {
|
||||
buffer.material = {
|
||||
type: MaterialType.textured,
|
||||
path: material.path,
|
||||
edited: material.edited,
|
||||
interpolation: material.interpolation,
|
||||
extension: material.extension,
|
||||
canBeTextured: material.canBeTextured,
|
||||
@ -258,7 +261,7 @@ export class Renderer {
|
||||
wrap: material.extension === 'clamp' ? this._gl.CLAMP_TO_EDGE : this._gl.REPEAT,
|
||||
}) : undefined,
|
||||
useAlphaChannel: material.alphaPath ? new Texture(material.path, material.alphaPath)._useAlphaChannel() : undefined,
|
||||
open: material.open,
|
||||
needsAttention: material.needsAttention,
|
||||
};
|
||||
return;
|
||||
}
|
||||
@ -281,7 +284,6 @@ export class Renderer {
|
||||
this._materialBuffers.set(materialName, {
|
||||
buffer: twgl.createBufferInfoFromArrays(this._gl, buffer),
|
||||
material: {
|
||||
edited: material.edited,
|
||||
canBeTextured: material.canBeTextured,
|
||||
type: MaterialType.textured,
|
||||
interpolation: material.interpolation,
|
||||
@ -301,7 +303,7 @@ export class Renderer {
|
||||
wrap: material.extension === 'clamp' ? this._gl.CLAMP_TO_EDGE : this._gl.REPEAT,
|
||||
}) : undefined,
|
||||
useAlphaChannel: material.alphaPath ? new Texture(material.path, material.alphaPath)._useAlphaChannel() : undefined,
|
||||
open: material.open,
|
||||
needsAttention: material.needsAttention,
|
||||
},
|
||||
numElements: numElements,
|
||||
materialName: materialName,
|
||||
|
@ -60,6 +60,8 @@ export class StatusHandler {
|
||||
switch (action) {
|
||||
case EAction.Import:
|
||||
return '[Importer]: Loaded';
|
||||
case EAction.Materials:
|
||||
return '[Materials]: Updated';
|
||||
case EAction.Voxelise:
|
||||
return '[Voxeliser]: Succeeded';
|
||||
case EAction.Assign:
|
||||
@ -67,7 +69,7 @@ export class StatusHandler {
|
||||
case EAction.Export:
|
||||
return '[Exporter]: Saved';
|
||||
default:
|
||||
ASSERT(false);
|
||||
ASSERT(false, 'Unknown action');
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +77,8 @@ export class StatusHandler {
|
||||
switch (action) {
|
||||
case EAction.Import:
|
||||
return '[Importer]: Failed';
|
||||
case EAction.Materials:
|
||||
return '[Materials]: Failed';
|
||||
case EAction.Voxelise:
|
||||
return '[Voxeliser]: Failed';
|
||||
case EAction.Assign:
|
||||
@ -82,7 +86,7 @@ export class StatusHandler {
|
||||
case EAction.Export:
|
||||
return '[Exporter]: Failed';
|
||||
default:
|
||||
ASSERT(false);
|
||||
ASSERT(false, 'Unknown action');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,12 +36,26 @@ export class ComboBoxElement<T> extends ConfigUIElement<T, HTMLSelectElement> {
|
||||
}
|
||||
|
||||
public override registerEvents(): void {
|
||||
this._getElement().addEventListener('onchange', (e: Event) => {
|
||||
this._getElement().addEventListener('change', (e: Event) => {
|
||||
const selectedValue = this._items[this._getElement().selectedIndex].payload;
|
||||
this._setValue(selectedValue);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
public override setDefaultValue(value: T): this {
|
||||
super.setDefaultValue(value);
|
||||
|
||||
const element = this._getElement();
|
||||
|
||||
const newSelectedIndex = this._items.findIndex((item) => item.payload === value);
|
||||
ASSERT(newSelectedIndex !== -1, 'Invalid selected index');
|
||||
element.selectedIndex = newSelectedIndex;
|
||||
|
||||
return this;
|
||||
}
|
||||
*/
|
||||
|
||||
public override _generateInnerHTML() {
|
||||
ASSERT(this._items.length > 0);
|
||||
|
||||
@ -59,10 +73,19 @@ export class ComboBoxElement<T> extends ConfigUIElement<T, HTMLSelectElement> {
|
||||
|
||||
protected override _onEnabledChanged() {
|
||||
super._onEnabledChanged();
|
||||
|
||||
this._getElement().disabled = !this.getEnabled();
|
||||
}
|
||||
|
||||
// TODO: Subproperty combo boxes are not updating values when changed!!!
|
||||
|
||||
protected override _onValueChanged(): void {
|
||||
}
|
||||
|
||||
public override finalise(): void {
|
||||
const selectedIndex = this._items.findIndex((item) => item.payload === this.getValue());
|
||||
const element = this._getElement();
|
||||
|
||||
ASSERT(selectedIndex !== -1, 'Invalid selected index');
|
||||
element.selectedIndex = selectedIndex;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ export abstract class ConfigUIElement<T, F> extends BaseUIElement<F> {
|
||||
private _label: string;
|
||||
private _description?: string;
|
||||
private _labelElement: LabelElement;
|
||||
private _hasLabel: boolean;
|
||||
private _value?: T;
|
||||
private _cachedValue?: T;
|
||||
private _onValueChangedListeners: Array<(newValue: T) => void>;
|
||||
@ -18,6 +19,7 @@ export abstract class ConfigUIElement<T, F> extends BaseUIElement<F> {
|
||||
super();
|
||||
this._value = defaultValue;
|
||||
this._label = 'unknown';
|
||||
this._hasLabel = false;
|
||||
this._labelElement = new LabelElement(this._label, this._description);
|
||||
this._onValueChangedListeners = [];
|
||||
}
|
||||
@ -28,6 +30,7 @@ export abstract class ConfigUIElement<T, F> extends BaseUIElement<F> {
|
||||
}
|
||||
|
||||
public setLabel(label: string) {
|
||||
this._hasLabel = true;
|
||||
this._label = label;
|
||||
this._labelElement = new LabelElement(this._label, this._description);
|
||||
return this;
|
||||
@ -92,7 +95,9 @@ export abstract class ConfigUIElement<T, F> extends BaseUIElement<F> {
|
||||
protected abstract _generateInnerHTML(): string;
|
||||
|
||||
protected override _onEnabledChanged() {
|
||||
this._labelElement.setEnabled(this.getEnabled());
|
||||
if (this._hasLabel) {
|
||||
this._labelElement.setEnabled(this.getEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,6 +80,8 @@ export class ImageElement extends ConfigUIElement<string, HTMLDivElement> {
|
||||
}
|
||||
|
||||
public override finalise(): void {
|
||||
super.finalise();
|
||||
|
||||
this._onValueChanged();
|
||||
}
|
||||
}
|
||||
|
@ -1,227 +0,0 @@
|
||||
import { remote } from 'electron';
|
||||
import path from 'path';
|
||||
|
||||
import { AppContext } from '../../app_context';
|
||||
import { RGBAUtil } from '../../colour';
|
||||
import { SolidMaterial, TexturedMaterial } from '../../mesh';
|
||||
import { getRandomID } from '../../util';
|
||||
import { ASSERT } from '../../util/error_util';
|
||||
import { FileUtil } from '../../util/file_util';
|
||||
|
||||
export abstract class MaterialUIElement {
|
||||
protected readonly _materialName: string;
|
||||
protected readonly _appContext: AppContext;
|
||||
private _actions: { text: string, onClick: () => void, id: string }[];
|
||||
private _metadata: string[];
|
||||
|
||||
public constructor(materialName: string, appContext: AppContext) {
|
||||
this._materialName = materialName;
|
||||
this._appContext = appContext;
|
||||
this._actions = [];
|
||||
this._metadata = [];
|
||||
}
|
||||
|
||||
public hasWarning() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public buildHTML(): string {
|
||||
let html = `<div class="material-container">`;
|
||||
html += this.buildChildHTML();
|
||||
this._metadata.forEach((data) => {
|
||||
html += `<br>${data}`;
|
||||
});
|
||||
this._actions.forEach((action) => {
|
||||
html += `<br><a id="${action.id}">[${action.text}]</a>`;
|
||||
});
|
||||
html += `</div>`;
|
||||
return html;
|
||||
}
|
||||
|
||||
public registerEvents() {
|
||||
this._actions.forEach((action) => {
|
||||
const element = document.getElementById(action.id);
|
||||
if (element !== null) {
|
||||
element.addEventListener('click', () => {
|
||||
action.onClick();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public addAction(text: string, onClick: () => void) {
|
||||
this._actions.push({ text: `<b>${text}</b>`, onClick: onClick, id: getRandomID() });
|
||||
}
|
||||
|
||||
public addMetadata(text: string) {
|
||||
this._metadata.push(text);
|
||||
}
|
||||
|
||||
public addKeyValueMetadata(key: string, value: string) {
|
||||
this.addMetadata(`${key}: <div class="metadata-value">${value}</div>`);
|
||||
}
|
||||
|
||||
protected abstract buildChildHTML(): string
|
||||
}
|
||||
|
||||
export class TextureMaterialUIElement extends MaterialUIElement {
|
||||
private _material: TexturedMaterial;
|
||||
private _diffuseImageId: string;
|
||||
private _alphaImageId: string;
|
||||
|
||||
public constructor(materialName: string, appContext: AppContext, material: TexturedMaterial) {
|
||||
super(materialName, appContext);
|
||||
this._material = material;
|
||||
this._diffuseImageId = getRandomID();
|
||||
this._alphaImageId = getRandomID();
|
||||
|
||||
const parsedPath = path.parse(material.path);
|
||||
const isMissingTexture = parsedPath.base === 'debug.png';
|
||||
const hasAlphaTexture = material.alphaPath !== undefined;
|
||||
|
||||
// Actions
|
||||
super.addAction(isMissingTexture ? 'Find texture' : 'Replace texture', () => {
|
||||
const files = remote.dialog.showOpenDialogSync({
|
||||
title: 'Load',
|
||||
buttonLabel: 'Load',
|
||||
filters: [{
|
||||
name: 'Images',
|
||||
extensions: ['png', 'jpeg', 'jpg', 'tga'],
|
||||
}],
|
||||
});
|
||||
if (files && files[0]) {
|
||||
this._appContext.onMaterialTextureReplace(materialName, files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
super.addAction('Switch to colour', () => {
|
||||
this._appContext.onMaterialTypeSwitched(materialName);
|
||||
});
|
||||
|
||||
super.addAction('Switch interpolation', () => {
|
||||
this._appContext.onMaterialInterpolationChanged(materialName);
|
||||
});
|
||||
|
||||
super.addAction('Switch extension', () => {
|
||||
this._appContext.onMaterialExtensionChanged(materialName);
|
||||
});
|
||||
|
||||
// Metadata
|
||||
super.addKeyValueMetadata('Alpha multiplier', this._material.alphaFactor.toLocaleString(undefined, { minimumFractionDigits: 1 }));
|
||||
if (this._material.alphaPath !== undefined) {
|
||||
super.addKeyValueMetadata('Alpha texture', this._material.alphaPath);
|
||||
}
|
||||
|
||||
super.addKeyValueMetadata('Interpolation', this._material.interpolation === 'nearest' ? 'Nearest' : 'Linear');
|
||||
|
||||
super.addKeyValueMetadata('Extension', this._material.extension === 'clamp' ? 'Clamp' : 'Repeat');
|
||||
}
|
||||
|
||||
private _isMissingTexture() {
|
||||
const parsedPath = path.parse(this._material.path);
|
||||
const isMissingTexture = parsedPath.base === 'debug.png';
|
||||
return isMissingTexture;
|
||||
}
|
||||
|
||||
public hasWarning(): boolean {
|
||||
return this._isMissingTexture();
|
||||
}
|
||||
|
||||
protected buildChildHTML(): string {
|
||||
let html = `<img id="${this._diffuseImageId}" class="texture-preview" src="${this._material.path}" width="75%" loading="lazy"></img>`;
|
||||
if (this._material.alphaPath !== undefined) {
|
||||
html += `<br><img id="${this._alphaImageId}" class="texture-preview" src="${this._material.alphaPath}" width="75%" loading="lazy"></img>`;
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
public registerEvents(): void {
|
||||
super.registerEvents();
|
||||
|
||||
{
|
||||
const element = document.getElementById(this._diffuseImageId) as HTMLLinkElement;
|
||||
if (element) {
|
||||
if (!this._isMissingTexture()) {
|
||||
element.addEventListener('mouseover', () => {
|
||||
element.classList.add('texture-hover');
|
||||
});
|
||||
element.addEventListener('mouseleave', () => {
|
||||
element.classList.remove('texture-hover');
|
||||
});
|
||||
element.addEventListener('click', () => {
|
||||
FileUtil.openDir(this._material.path);
|
||||
});
|
||||
} else {
|
||||
element.classList.add('texture-preview-missing');
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
const element = document.getElementById(this._alphaImageId) as HTMLLinkElement;
|
||||
if (element) {
|
||||
if (!this._isMissingTexture()) {
|
||||
element.addEventListener('mouseover', () => {
|
||||
element.classList.add('texture-hover');
|
||||
});
|
||||
element.addEventListener('mouseleave', () => {
|
||||
element.classList.remove('texture-hover');
|
||||
});
|
||||
element.addEventListener('click', () => {
|
||||
ASSERT(this._material.alphaPath !== undefined);
|
||||
FileUtil.openDir(this._material.alphaPath);
|
||||
});
|
||||
} else {
|
||||
element.classList.add('texture-preview-missing');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolidMaterialUIElement extends MaterialUIElement {
|
||||
private _material: SolidMaterial;
|
||||
private _colourId: string;
|
||||
|
||||
public constructor(materialName: string, appContext: AppContext, material: SolidMaterial) {
|
||||
super(materialName, appContext);
|
||||
this._material = material;
|
||||
this._colourId = getRandomID();
|
||||
|
||||
if (material.canBeTextured) {
|
||||
super.addAction('Switch to texture', () => {
|
||||
this._appContext.onMaterialTypeSwitched(materialName);
|
||||
});
|
||||
}
|
||||
|
||||
this.addMetadata(`Alpha multiplier: ${this._material.colour.a}`);
|
||||
}
|
||||
|
||||
protected buildChildHTML(): string {
|
||||
return `<input class="colour-swatch" type="color" id="${this._colourId}" value="${RGBAUtil.toHexString(this._material.colour)}">`;
|
||||
}
|
||||
|
||||
public hasWarning(): boolean {
|
||||
return !this._material.set;
|
||||
}
|
||||
|
||||
public registerEvents(): void {
|
||||
super.registerEvents();
|
||||
|
||||
const colourElement = document.getElementById(this._colourId) as (HTMLInputElement | null);
|
||||
if (colourElement !== null) {
|
||||
colourElement.addEventListener('change', () => {
|
||||
const newColour = RGBAUtil.fromHexString(colourElement.value);
|
||||
newColour.a = this._material.colour.a;
|
||||
this._appContext.onMaterialColourChanged(this._materialName, newColour);
|
||||
});
|
||||
|
||||
colourElement.addEventListener('mouseenter', () => {
|
||||
colourElement.classList.add('colour-swatch-hover');
|
||||
});
|
||||
|
||||
colourElement.addEventListener('mouseleave', () => {
|
||||
colourElement.classList.remove('colour-swatch-hover');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
import { ASSERT } from '../../../tools/misc';
|
||||
import { MaterialType } from '../../mesh';
|
||||
import { ConfigUIElement } from './config_element';
|
||||
import { ToolbarItemElement } from './toolbar_item';
|
||||
@ -12,7 +11,7 @@ export class MaterialTypeElement extends ConfigUIElement<MaterialType, HTMLDivEl
|
||||
.setSmall()
|
||||
.setLabel('Switch')
|
||||
.onClick(() => {
|
||||
ASSERT(false, 'UNIMPLEMENTED!!!'); // TODO
|
||||
this._onClickChangeTypeDelegate?.();
|
||||
});
|
||||
}
|
||||
|
||||
@ -44,4 +43,10 @@ export class MaterialTypeElement extends ConfigUIElement<MaterialType, HTMLDivEl
|
||||
|
||||
protected override _onValueChanged(): void {
|
||||
}
|
||||
|
||||
private _onClickChangeTypeDelegate?: () => void;
|
||||
public onClickChangeTypeDelegate(delegate: () => void) {
|
||||
this._onClickChangeTypeDelegate = delegate;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,35 @@
|
||||
import { RGBAUtil } from '../../colour';
|
||||
import { SolidMaterial } from '../../mesh';
|
||||
import { MaterialType, SolidMaterial } from '../../mesh';
|
||||
import { getRandomID } from '../../util';
|
||||
import { UIUtil } from '../../util/ui_util';
|
||||
import { ConfigUIElement } from './config_element';
|
||||
import { MaterialTypeElement } from './material_type_element';
|
||||
|
||||
export class SolidMaterialElement extends ConfigUIElement<SolidMaterial, HTMLDivElement> {
|
||||
private _materialName: string;
|
||||
private _colourId: string;
|
||||
private _typeElement: MaterialTypeElement;
|
||||
|
||||
public constructor(materialName: string, material: SolidMaterial) {
|
||||
super(material);
|
||||
this._materialName = materialName;
|
||||
this._colourId = getRandomID();
|
||||
|
||||
this._typeElement = new MaterialTypeElement(MaterialType.solid);
|
||||
}
|
||||
|
||||
public override registerEvents(): void {
|
||||
this._typeElement.registerEvents();
|
||||
|
||||
this._typeElement.onClickChangeTypeDelegate(() => {
|
||||
this._onChangeTypeDelegate?.();
|
||||
});
|
||||
|
||||
const swatchElement = UIUtil.getElementById(this._colourId) as HTMLInputElement;
|
||||
swatchElement.addEventListener('change', () => {
|
||||
const material = this.getValue();
|
||||
material.colour = RGBAUtil.fromHexString(swatchElement.value);
|
||||
});
|
||||
}
|
||||
|
||||
protected override _generateInnerHTML(): string {
|
||||
@ -33,7 +49,7 @@ export class SolidMaterialElement extends ConfigUIElement<SolidMaterial, HTMLDiv
|
||||
`);
|
||||
};
|
||||
|
||||
addSubproperty('Type', `Solid`);
|
||||
addSubproperty('Type', this._typeElement._generateInnerHTML());
|
||||
addSubproperty('Colour', `<input class="colour-swatch" type="color" id="${this._colourId}" value="${RGBAUtil.toHexString(material.colour)}">`);
|
||||
addSubproperty('Alpha', `${material.colour.a.toFixed(4)}`);
|
||||
|
||||
@ -50,4 +66,10 @@ export class SolidMaterialElement extends ConfigUIElement<SolidMaterial, HTMLDiv
|
||||
protected override _onEnabledChanged(): void {
|
||||
super._onEnabledChanged();
|
||||
}
|
||||
|
||||
private _onChangeTypeDelegate?: () => void;
|
||||
public onChangeTypeDelegate(delegate: () => void) {
|
||||
this._onChangeTypeDelegate = delegate;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -21,12 +21,14 @@ export class TexturedMaterialElement extends ConfigUIElement<TexturedMaterial, H
|
||||
this._filteringElement = new ComboBoxElement<'linear' | 'nearest'>()
|
||||
.addItem({ payload: 'linear', displayText: 'Linear' })
|
||||
.addItem({ payload: 'nearest', displayText: 'Nearest' })
|
||||
.setSmall();
|
||||
.setSmall()
|
||||
.setDefaultValue(material.interpolation);
|
||||
|
||||
this._wrapElement = new ComboBoxElement<'clamp' | 'repeat'>()
|
||||
.addItem({ payload: 'clamp', displayText: 'Clamp' })
|
||||
.addItem({ payload: 'repeat', displayText: 'Repeat' })
|
||||
.setSmall();
|
||||
.setSmall()
|
||||
.setDefaultValue(material.extension);
|
||||
|
||||
this._imageElement = new ImageElement(material.path);
|
||||
|
||||
@ -38,6 +40,25 @@ export class TexturedMaterialElement extends ConfigUIElement<TexturedMaterial, H
|
||||
this._typeElement.registerEvents();
|
||||
this._filteringElement.registerEvents();
|
||||
this._wrapElement.registerEvents();
|
||||
|
||||
this._imageElement.addValueChangedListener((newPath) => {
|
||||
const material = this.getValue();
|
||||
material.path = newPath;
|
||||
});
|
||||
|
||||
this._filteringElement.addValueChangedListener((newFiltering) => {
|
||||
const material = this.getValue();
|
||||
material.interpolation = newFiltering;
|
||||
});
|
||||
|
||||
this._wrapElement.addValueChangedListener((newWrap) => {
|
||||
const material = this.getValue();
|
||||
material.extension = newWrap;
|
||||
});
|
||||
|
||||
this._typeElement.onClickChangeTypeDelegate(() => {
|
||||
this._onChangeTypeDelegate?.();
|
||||
});
|
||||
}
|
||||
|
||||
protected override _generateInnerHTML(): string {
|
||||
@ -78,6 +99,17 @@ export class TexturedMaterialElement extends ConfigUIElement<TexturedMaterial, H
|
||||
}
|
||||
|
||||
public override finalise(): void {
|
||||
super.finalise();
|
||||
|
||||
this._imageElement.finalise();
|
||||
this._typeElement.finalise();
|
||||
this._filteringElement.finalise();
|
||||
this._wrapElement.finalise();
|
||||
}
|
||||
|
||||
private _onChangeTypeDelegate?: () => void;
|
||||
public onChangeTypeDelegate(delegate: () => void) {
|
||||
this._onChangeTypeDelegate = delegate;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ export abstract class IVoxeliser {
|
||||
*/
|
||||
protected _getVoxelColour(mesh: Mesh, triangle: UVTriangle, materialName: string, location: Vector3, multisample: boolean): RGBA {
|
||||
const material = mesh.getMaterialByName(materialName);
|
||||
ASSERT(material !== undefined);
|
||||
|
||||
if (material.type === MaterialType.solid) {
|
||||
return RGBAUtil.copy(material.colour);
|
||||
}
|
||||
@ -49,7 +51,7 @@ export abstract class IVoxeliser {
|
||||
|
||||
private _internalGetVoxelColour(mesh: Mesh, triangle: UVTriangle, materialName: string, location: Vector3) {
|
||||
const material = mesh.getMaterialByName(materialName);
|
||||
ASSERT(material.type === MaterialType.textured);
|
||||
ASSERT(material !== undefined && material.type === MaterialType.textured);
|
||||
|
||||
const area01 = new Triangle(triangle.v0, triangle.v1, location).getArea();
|
||||
const area12 = new Triangle(triangle.v1, triangle.v2, location).getArea();
|
||||
|
@ -90,7 +90,7 @@ export class WorkerClient {
|
||||
|
||||
return {
|
||||
materials: this._loadedMesh.getMaterials(),
|
||||
materialsChanged: Object.keys(params.materials), // TODO: Change to actual materials changed
|
||||
materialsChanged: Array.from(params.materials.keys()), // TODO: Change to actual materials changed
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user