diff --git a/FEATURES.md b/FEATURES.md index 65476a93..6be91fc8 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -151,7 +151,6 @@ Features that are in ***bold italics*** are planned but not yet implemented. ## Bugs -- [NPC/Object Manipulation](#npcobject-manipulation): Entities in NPCs/Objects panels with additive blending aren't rendered correctly. - [Load Quest](#load-quest): Can't parse quest 125 White Day - [Script Assembly Editor](#script-assembly-editor): Go to definition doesn't work in RT (#231) - When a modal dialog is open, global keybindings should be disabled diff --git a/src/application/index.test.ts b/src/application/index.test.ts index 0fdb5a21..5bb3826f 100644 --- a/src/application/index.test.ts +++ b/src/application/index.test.ts @@ -2,10 +2,10 @@ import { initialize_application } from "./index"; import { LogHandler, LogManager } from "../core/Logger"; import { FileSystemHttpClient } from "../../test/src/core/FileSystemHttpClient"; import { timeout } from "../../test/src/utils"; -import { StubThreeRenderer } from "../../test/src/core/rendering/StubThreeRenderer"; import { Random } from "../core/Random"; import { Severity } from "../core/Severity"; import { StubClock } from "../../test/src/core/StubClock"; +import { STUB_THREE_RENDERER } from "../../test/src/core/rendering/StubThreeRenderer"; for (const path of [undefined, "/viewer", "/quest_editor", "/hunt_optimizer"]) { const with_path = path == undefined ? "without specific path" : `with path ${path}`; @@ -28,7 +28,7 @@ for (const path of [undefined, "/viewer", "/quest_editor", "/hunt_optimizer"]) { new FileSystemHttpClient(), new Random(() => 0.27), new StubClock(new Date("2020-01-01T15:40:20Z")), - () => new StubThreeRenderer(), + () => STUB_THREE_RENDERER, ); expect(app).toBeDefined(); diff --git a/src/core/rendering/ThreeRenderer.ts b/src/core/rendering/ThreeRenderer.ts index 8fba6fe2..7deb3eef 100644 --- a/src/core/rendering/ThreeRenderer.ts +++ b/src/core/rendering/ThreeRenderer.ts @@ -22,7 +22,7 @@ CameraControls.install({ }, }); -export interface DisposableThreeRenderer extends THREE.Renderer, Disposable {} +export interface DisposableThreeRenderer extends THREE.WebGLRenderer, Disposable {} /** * Uses THREE.js for rendering. diff --git a/src/quest_editor/rendering/EntityImageRenderer.ts b/src/quest_editor/rendering/EntityImageRenderer.ts index d3a819a3..5929c0d7 100644 --- a/src/quest_editor/rendering/EntityImageRenderer.ts +++ b/src/quest_editor/rendering/EntityImageRenderer.ts @@ -1,4 +1,11 @@ -import { HemisphereLight, PerspectiveCamera, Scene, Vector3 } from "three"; +import { + DoubleSide, + HemisphereLight, + MeshBasicMaterial, + PerspectiveCamera, + Scene, + Vector3, +} from "three"; import { EntityType } from "../../core/data_formats/parsing/quest/entities"; import { create_entity_type_mesh } from "./conversion/entities"; import { sequential } from "../../core/sequential"; @@ -40,19 +47,44 @@ export class EntityImageRenderer implements Disposable { (entity: EntityType): DisposablePromise => this.entity_asset_loader.load_geometry(entity).then(geometry => this.entity_asset_loader.load_textures(entity).then(textures => { + // First render a flat version of the model with the same color as the + // background. Then render the final version on top of that. We do this to + // somewhat fix issues with additive alpha blending on a transparent background. + scene.remove(...scene.children); scene.add(light); - const entity_model = create_entity_type_mesh(entity, geometry, textures); - scene.add(entity_model); + // Render the flat model. - const b_sphere = entity_model.geometry.boundingSphere!; + const entity_model_bg = create_entity_type_mesh( + entity, + geometry, + [], + new MeshBasicMaterial({ + color: 0x262626, + side: DoubleSide, + }), + ); + scene.add(entity_model_bg); + + const b_sphere = entity_model_bg.geometry.boundingSphere!; camera.position.copy(camera_position); camera.position.multiplyScalar(b_sphere.radius * camera_dist_factor); camera.lookAt(b_sphere.center); this.renderer.render(scene, camera); + scene.remove(entity_model_bg); + + // Render the textured model. + + const entity_model = create_entity_type_mesh(entity, geometry, textures); + scene.add(entity_model); + + this.renderer.autoClearColor = false; + this.renderer.render(scene, camera); + this.renderer.autoClearColor = true; + return this.renderer.domElement.toDataURL(); }), ), diff --git a/src/quest_editor/rendering/conversion/entities.ts b/src/quest_editor/rendering/conversion/entities.ts index f334eef5..5af16f0d 100644 --- a/src/quest_editor/rendering/conversion/entities.ts +++ b/src/quest_editor/rendering/conversion/entities.ts @@ -1,5 +1,5 @@ import { QuestEntityModel } from "../../model/QuestEntityModel"; -import { BufferGeometry, DoubleSide, Mesh, MeshLambertMaterial, Texture } from "three"; +import { BufferGeometry, DoubleSide, Material, Mesh, MeshLambertMaterial, Texture } from "three"; import { create_mesh } from "../../../core/rendering/conversion/create_mesh"; import { entity_type_to_string, @@ -31,12 +31,11 @@ export function create_entity_type_mesh( type: EntityType, geometry: BufferGeometry, textures: Texture[], -): Mesh { - const default_material = new MeshLambertMaterial({ + default_material: Material = new MeshLambertMaterial({ color: is_npc_type(type) ? NPC_COLORS[ColorType.Normal] : OBJECT_COLORS[ColorType.Normal], side: DoubleSide, - }); - + }), +): Mesh { const mesh = create_mesh(geometry, textures, default_material, false); mesh.name = entity_type_to_string(type); return mesh; diff --git a/src/viewer/gui/model/ModelView.test.ts b/src/viewer/gui/model/ModelView.test.ts index 5db80616..36c99047 100644 --- a/src/viewer/gui/model/ModelView.test.ts +++ b/src/viewer/gui/model/ModelView.test.ts @@ -4,7 +4,7 @@ import { CharacterClassAssetLoader } from "../../loading/CharacterClassAssetLoad import { FileSystemHttpClient } from "../../../../test/src/core/FileSystemHttpClient"; import { ModelView } from "./ModelView"; import { ModelRenderer } from "../../rendering/ModelRenderer"; -import { StubThreeRenderer } from "../../../../test/src/core/rendering/StubThreeRenderer"; +import { STUB_THREE_RENDERER } from "../../../../test/src/core/rendering/StubThreeRenderer"; import { Random } from "../../../core/Random"; import { ModelStore } from "../../stores/ModelStore"; import { ModelToolBarView } from "./ModelToolBarView"; @@ -26,8 +26,9 @@ test("Renders correctly.", () => disposer.add(new ModelController(store)), new ModelToolBarView(disposer.add(new ModelToolBarController(store))), new CharacterClassOptionsView(disposer.add(new CharacterClassOptionsController(store))), - new ModelRenderer(store, new StubThreeRenderer()), + new ModelRenderer(store, STUB_THREE_RENDERER), ); expect(view.element).toMatchSnapshot(); - })); + }) +); diff --git a/test/src/core/rendering/StubThreeRenderer.ts b/test/src/core/rendering/StubThreeRenderer.ts index 1c8c8596..fbf73c84 100644 --- a/test/src/core/rendering/StubThreeRenderer.ts +++ b/test/src/core/rendering/StubThreeRenderer.ts @@ -1,11 +1,11 @@ import { DisposableThreeRenderer } from "../../../../src/core/rendering/ThreeRenderer"; -export class StubThreeRenderer implements DisposableThreeRenderer { - domElement: HTMLCanvasElement = document.createElement("canvas"); +export const STUB_THREE_RENDERER: DisposableThreeRenderer = { + domElement: document.createElement("canvas"), - dispose(): void {} // eslint-disable-line + dispose(): void {}, // eslint-disable-line - render(): void {} // eslint-disable-line + render(): void {}, // eslint-disable-line - setSize(): void {} // eslint-disable-line -} + setSize(): void {}, // eslint-disable-line +} as any;