mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2025-12-11 20:15:30 +01:00
637 lines
25 KiB
TypeScript
637 lines
25 KiB
TypeScript
import * as twgl from 'twgl.js';
|
|
|
|
import VANILLA_TEXTURE from '../res/atlases/vanilla.png';
|
|
import { Bounds } from './bounds';
|
|
import { ArcballCamera } from './camera';
|
|
import { RGBA, RGBAUtil } from './colour';
|
|
import { AppConfig } from './config';
|
|
import { DebugGeometryTemplates } from './geometry';
|
|
import { MaterialType, SolidMaterial, TexturedMaterial } from './mesh';
|
|
import { RenderBuffer } from './render_buffer';
|
|
import { ShaderManager } from './shaders';
|
|
import { EImageChannel } from './texture';
|
|
import { ASSERT } from './util/error_util';
|
|
import { Vector3 } from './vector';
|
|
import { RenderMeshParams, RenderNextBlockMeshChunkParams, RenderNextVoxelMeshChunkParams } from './worker_types';
|
|
import { UIUtil } from './util/ui_util';
|
|
|
|
/* eslint-disable */
|
|
export enum MeshType {
|
|
None,
|
|
TriangleMesh,
|
|
VoxelMesh,
|
|
BlockMesh
|
|
}
|
|
/* eslint-enable */
|
|
|
|
/* eslint-disable */
|
|
enum EDebugBufferComponents {
|
|
Wireframe,
|
|
Normals,
|
|
Bounds,
|
|
Dev,
|
|
}
|
|
/* eslint-enable */
|
|
|
|
/**
|
|
* Dedicated type for passing to shaders for solid materials
|
|
*/
|
|
type InternalSolidMaterial = {
|
|
type: MaterialType.solid,
|
|
colourArray: number[],
|
|
}
|
|
|
|
/**
|
|
* Dedicated type for passing to shaders for textured materials
|
|
*/
|
|
type InternalTextureMaterial = {
|
|
type: MaterialType.textured,
|
|
diffuseTexture: WebGLTexture,
|
|
// The texture to sample alpha values from (if is using a texture map)
|
|
alphaTexture: WebGLTexture,
|
|
// What texture channel to sample the alpha value from
|
|
alphaChannel: EImageChannel,
|
|
// What alpha value to use (only used if using constant transparency mode)
|
|
alphaValue: number,
|
|
};
|
|
|
|
export class Renderer {
|
|
public _gl: WebGLRenderingContext;
|
|
|
|
private _backgroundColour: RGBA = { r: 0.125, g: 0.125, b: 0.125, a: 1.0 };
|
|
private _atlasTexture?: WebGLTexture;
|
|
|
|
private _atlasSize: number = 1.0;
|
|
private _meshToUse: MeshType = MeshType.None;
|
|
private _voxelSize: number = 1.0;
|
|
private _gridOffset: Vector3 = new Vector3(0, 0, 0);
|
|
private _sliceHeight: number = 0.0;
|
|
|
|
private _modelsAvailable: number;
|
|
|
|
private _materialBuffers: Map<string, {
|
|
material: InternalSolidMaterial | InternalTextureMaterial,
|
|
buffer: twgl.BufferInfo,
|
|
numElements: number,
|
|
materialName: string,
|
|
}>;
|
|
public _voxelBuffer?: twgl.BufferInfo[];
|
|
private _blockBuffer?: twgl.BufferInfo[];
|
|
private _blockBounds: Bounds;
|
|
private _debugBuffers: { [meshType: string]: { [bufferComponent: string]: RenderBuffer } };
|
|
private _axisBuffer: RenderBuffer;
|
|
|
|
private _isGridComponentEnabled: { [bufferComponent: string]: boolean };
|
|
private _axesEnabled: boolean;
|
|
private _nightVisionEnabled: boolean;
|
|
private _sliceViewEnabled: boolean;
|
|
|
|
private _gridBuffers: {
|
|
x: { [meshType: string]: RenderBuffer };
|
|
y: { [meshType: string]: RenderBuffer };
|
|
z: { [meshType: string]: RenderBuffer };
|
|
};
|
|
private _gridEnabled: boolean;
|
|
|
|
private static _instance: Renderer;
|
|
public static get Get() {
|
|
return this._instance || (this._instance = new this());
|
|
}
|
|
|
|
private constructor() {
|
|
this._gl = (<HTMLCanvasElement>document.getElementById('canvas')).getContext('webgl', {
|
|
alpha: false,
|
|
})!;
|
|
twgl.addExtensionsToContext(this._gl);
|
|
|
|
this._backgroundColour = AppConfig.Get.VIEWPORT_BACKGROUND_COLOUR;
|
|
|
|
this._modelsAvailable = 0;
|
|
this._materialBuffers = new Map();
|
|
|
|
this._gridBuffers = { x: {}, y: {}, z: {} };
|
|
this._gridEnabled = false;
|
|
|
|
this._debugBuffers = {};
|
|
this._debugBuffers[MeshType.None] = {};
|
|
this._debugBuffers[MeshType.TriangleMesh] = {};
|
|
this._debugBuffers[MeshType.VoxelMesh] = {};
|
|
this._debugBuffers[MeshType.BlockMesh] = {};
|
|
|
|
this._isGridComponentEnabled = {};
|
|
this._axesEnabled = true;
|
|
this._nightVisionEnabled = true;
|
|
this._sliceViewEnabled = false;
|
|
|
|
this._blockBounds = new Bounds(new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
|
|
this._axisBuffer = new RenderBuffer([
|
|
{ name: 'position', numComponents: 3 },
|
|
{ name: 'colour', numComponents: 4 },
|
|
]);
|
|
this._axisBuffer.add(DebugGeometryTemplates.arrow(new Vector3(0, 0, 0), new Vector3(1, 0, 0), { r: 0.96, g: 0.21, b: 0.32, a: 1.0 }));
|
|
this._axisBuffer.add(DebugGeometryTemplates.arrow(new Vector3(0, 0, 0), new Vector3(0, 1, 0), { r: 0.44, g: 0.64, b: 0.11, a: 1.0 }));
|
|
this._axisBuffer.add(DebugGeometryTemplates.arrow(new Vector3(0, 0, 0), new Vector3(0, 0, 1), { r: 0.18, g: 0.52, b: 0.89, a: 1.0 }));
|
|
|
|
const resizeObserver = new ResizeObserver(() => {
|
|
this.forceRedraw();
|
|
});
|
|
|
|
resizeObserver.observe(UIUtil.getElementById('canvas'));
|
|
}
|
|
|
|
public update() {
|
|
ArcballCamera.Get.updateCamera();
|
|
}
|
|
|
|
private _redraw = true;
|
|
public forceRedraw() {
|
|
this._redraw = true;
|
|
}
|
|
|
|
public draw() {
|
|
if (this._redraw || ArcballCamera.Get.isUserRotating || ArcballCamera.Get.isUserTranslating) {
|
|
this._setupScene();
|
|
|
|
switch (this._meshToUse) {
|
|
case MeshType.TriangleMesh:
|
|
this._drawMesh();
|
|
break;
|
|
case MeshType.VoxelMesh:
|
|
this._drawVoxelMesh();
|
|
break;
|
|
case MeshType.BlockMesh:
|
|
this._drawBlockMesh();
|
|
break;
|
|
};
|
|
|
|
this._drawDebug();
|
|
this._redraw = false;
|
|
}
|
|
}
|
|
|
|
// /////////////////////////////////////////////////////////////////////////
|
|
|
|
public isSliceViewerEnabled() {
|
|
return this._sliceViewEnabled && this._meshToUse === MeshType.BlockMesh;
|
|
}
|
|
|
|
public toggleSliceViewerEnabled() {
|
|
this._sliceViewEnabled = !this._sliceViewEnabled;
|
|
this.forceRedraw();
|
|
}
|
|
|
|
public canIncrementSliceHeight() {
|
|
return this._blockBounds.max.y > this._sliceHeight;
|
|
|
|
}
|
|
|
|
public canDecrementSliceHeight() {
|
|
return this._blockBounds.min.y < this._sliceHeight;
|
|
}
|
|
|
|
public incrementSliceHeight() {
|
|
if (this.canIncrementSliceHeight()) {
|
|
++this._sliceHeight;
|
|
this.forceRedraw();
|
|
}
|
|
}
|
|
|
|
public decrementSliceHeight() {
|
|
if (this.canDecrementSliceHeight()) {
|
|
--this._sliceHeight;
|
|
this.forceRedraw();
|
|
}
|
|
}
|
|
|
|
private _lightingAvailable: boolean = false;
|
|
public setLightingAvailable(isAvailable: boolean) {
|
|
this._lightingAvailable = isAvailable;
|
|
if (!isAvailable) {
|
|
this._nightVisionEnabled = true;
|
|
}
|
|
}
|
|
|
|
public toggleIsGridEnabled() {
|
|
this._gridEnabled = !this._gridEnabled;
|
|
this.forceRedraw();
|
|
}
|
|
|
|
public isGridEnabled() {
|
|
return this._gridEnabled;
|
|
}
|
|
|
|
public isAxesEnabled() {
|
|
return this._axesEnabled;
|
|
}
|
|
|
|
public toggleIsAxesEnabled() {
|
|
this._axesEnabled = !this._axesEnabled;
|
|
this.forceRedraw();
|
|
}
|
|
|
|
public canToggleNightVision() {
|
|
return this._lightingAvailable;
|
|
}
|
|
|
|
public toggleIsNightVisionEnabled() {
|
|
this._nightVisionEnabled = !this._nightVisionEnabled;
|
|
if (!this._lightingAvailable) {
|
|
this._nightVisionEnabled = true;
|
|
}
|
|
this.forceRedraw();
|
|
}
|
|
|
|
public isNightVisionEnabled() {
|
|
return this._nightVisionEnabled;
|
|
}
|
|
|
|
public toggleIsWireframeEnabled() {
|
|
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Wireframe];
|
|
this._isGridComponentEnabled[EDebugBufferComponents.Wireframe] = isEnabled;
|
|
this.forceRedraw();
|
|
}
|
|
|
|
public toggleIsNormalsEnabled() {
|
|
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Normals];
|
|
this._isGridComponentEnabled[EDebugBufferComponents.Normals] = isEnabled;
|
|
this.forceRedraw();
|
|
}
|
|
|
|
public toggleIsDevDebugEnabled() {
|
|
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Dev];
|
|
this._isGridComponentEnabled[EDebugBufferComponents.Dev] = isEnabled;
|
|
this.forceRedraw();
|
|
}
|
|
|
|
public clearMesh() {
|
|
this._materialBuffers = new Map();
|
|
|
|
this._modelsAvailable = 0;
|
|
this.setModelToUse(MeshType.None);
|
|
}
|
|
|
|
private _createInternalMaterial(material: SolidMaterial | TexturedMaterial): (InternalSolidMaterial | InternalTextureMaterial) {
|
|
if (material.type === MaterialType.solid) {
|
|
return {
|
|
type: MaterialType.solid,
|
|
colourArray: RGBAUtil.toArray(material.colour),
|
|
};
|
|
} else {
|
|
const blankTexture = twgl.createTexture(this._gl, {
|
|
min: this._gl.NEAREST,
|
|
mag: this._gl.NEAREST,
|
|
src: [
|
|
255, 0, 255, 255,
|
|
],
|
|
}, () => {
|
|
this.forceRedraw();
|
|
});
|
|
|
|
let diffuseTexture = blankTexture;
|
|
let alphaTexture = diffuseTexture;
|
|
|
|
if (material.diffuse !== undefined && material.diffuse.raw !== '') {
|
|
diffuseTexture = twgl.createTexture(this._gl, {
|
|
src: material.diffuse?.raw, // TODO Unimplemented
|
|
min: material.interpolation === 'linear' ? this._gl.LINEAR : this._gl.NEAREST,
|
|
mag: material.interpolation === 'linear' ? this._gl.LINEAR : this._gl.NEAREST,
|
|
wrap: material.extension === 'clamp' ? this._gl.CLAMP_TO_EDGE : this._gl.REPEAT,
|
|
}, () => {
|
|
this.forceRedraw();
|
|
});
|
|
}
|
|
|
|
if (material.transparency.type === 'UseAlphaMap') {
|
|
alphaTexture = blankTexture;
|
|
if (material.transparency.alpha !== undefined && material.transparency.alpha.raw !== '') {
|
|
alphaTexture = twgl.createTexture(this._gl, {
|
|
src: material.transparency.alpha.raw, // TODO Unimplemented
|
|
min: material.interpolation === 'linear' ? this._gl.LINEAR : this._gl.NEAREST,
|
|
mag: material.interpolation === 'linear' ? this._gl.LINEAR : this._gl.NEAREST,
|
|
wrap: material.extension === 'clamp' ? this._gl.CLAMP_TO_EDGE : this._gl.REPEAT,
|
|
}, () => {
|
|
this.forceRedraw();
|
|
});
|
|
}
|
|
}
|
|
|
|
const alphaValue = material.transparency.type === 'UseAlphaValue' ?
|
|
material.transparency.alpha : 1.0;
|
|
|
|
let alphaChannel: EImageChannel = EImageChannel.MAX;
|
|
switch (material.transparency.type) {
|
|
case 'UseAlphaValue':
|
|
alphaChannel = EImageChannel.MAX;
|
|
break;
|
|
case 'UseDiffuseMapAlphaChannel':
|
|
alphaChannel = EImageChannel.A;
|
|
break;
|
|
case 'UseAlphaMap':
|
|
alphaChannel = material.transparency.channel;
|
|
break;
|
|
}
|
|
|
|
return {
|
|
type: MaterialType.textured,
|
|
diffuseTexture: diffuseTexture,
|
|
alphaTexture: alphaTexture,
|
|
alphaValue: alphaValue,
|
|
alphaChannel: alphaChannel,
|
|
};
|
|
}
|
|
}
|
|
|
|
public recreateMaterialBuffer(materialName: string, material: SolidMaterial | TexturedMaterial) {
|
|
const oldBuffer = this._materialBuffers.get(materialName);
|
|
ASSERT(oldBuffer !== undefined);
|
|
|
|
const internalMaterial = this._createInternalMaterial(material);
|
|
this._materialBuffers.set(materialName, {
|
|
buffer: oldBuffer.buffer,
|
|
material: internalMaterial,
|
|
numElements: oldBuffer.numElements,
|
|
materialName: materialName,
|
|
});
|
|
}
|
|
|
|
public useMesh(params: RenderMeshParams.Output) {
|
|
this._materialBuffers = new Map();
|
|
|
|
for (const { material, buffer, numElements, materialName } of params.buffers) {
|
|
const internalMaterial = this._createInternalMaterial(material);
|
|
this._materialBuffers.set(materialName, {
|
|
buffer: twgl.createBufferInfoFromArrays(this._gl, buffer),
|
|
material: internalMaterial,
|
|
numElements: numElements,
|
|
materialName: materialName,
|
|
});
|
|
}
|
|
|
|
this._gridBuffers.x[MeshType.TriangleMesh] = DebugGeometryTemplates.gridX(params.dimensions);
|
|
this._gridBuffers.y[MeshType.TriangleMesh] = DebugGeometryTemplates.gridY(params.dimensions);
|
|
this._gridBuffers.z[MeshType.TriangleMesh] = DebugGeometryTemplates.gridZ(params.dimensions);
|
|
|
|
this._modelsAvailable = 1;
|
|
this.setModelToUse(MeshType.TriangleMesh);
|
|
}
|
|
|
|
private _allVoxelChunks = false;
|
|
public useVoxelMeshChunk(params: RenderNextVoxelMeshChunkParams.Output) {
|
|
if (params.isFirstChunk) {
|
|
this._voxelBuffer = [];
|
|
}
|
|
|
|
this._allVoxelChunks = !params.moreVoxelsToBuffer;
|
|
|
|
this._voxelBuffer?.push(twgl.createBufferInfoFromArrays(this._gl, params.buffer.buffer));
|
|
this._voxelSize = params.voxelSize;
|
|
|
|
if (params.isFirstChunk) {
|
|
const voxelSize = this._voxelSize;
|
|
const dimensions = new Vector3(0, 0, 0);
|
|
dimensions.setFrom(params.dimensions);
|
|
|
|
this._gridOffset = new Vector3(
|
|
dimensions.x % 2 === 0 ? 0 : -0.5,
|
|
dimensions.y % 2 === 0 ? 0 : -0.5,
|
|
dimensions.z % 2 === 0 ? 0 : -0.5,
|
|
);
|
|
dimensions.add(1);
|
|
|
|
this._gridBuffers.x[MeshType.VoxelMesh] = DebugGeometryTemplates.gridX(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
|
this._gridBuffers.y[MeshType.VoxelMesh] = DebugGeometryTemplates.gridY(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
|
this._gridBuffers.z[MeshType.VoxelMesh] = DebugGeometryTemplates.gridZ(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
|
|
|
this._modelsAvailable = 2;
|
|
this.setModelToUse(MeshType.VoxelMesh);
|
|
}
|
|
}
|
|
|
|
/*
|
|
public useVoxelMesh(params: RenderNextVoxelMeshChunkParams.Output) {
|
|
this._voxelBuffer?.push(twgl.createBufferInfoFromArrays(this._gl, params.buffer.buffer));
|
|
this._voxelSize = params.voxelSize;
|
|
|
|
const voxelSize = this._voxelSize;
|
|
const dimensions = new Vector3(0, 0, 0);
|
|
dimensions.setFrom(params.dimensions);
|
|
|
|
this._gridOffset = new Vector3(
|
|
dimensions.x % 2 === 0 ? 0 : -0.5,
|
|
dimensions.y % 2 === 0 ? 0 : -0.5,
|
|
dimensions.z % 2 === 0 ? 0 : -0.5,
|
|
);
|
|
dimensions.add(1);
|
|
|
|
this._gridBuffers.x[MeshType.VoxelMesh] = DebugGeometryTemplates.gridX(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
|
this._gridBuffers.y[MeshType.VoxelMesh] = DebugGeometryTemplates.gridY(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
|
this._gridBuffers.z[MeshType.VoxelMesh] = DebugGeometryTemplates.gridZ(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
|
|
|
this._modelsAvailable = 2;
|
|
this.setModelToUse(MeshType.VoxelMesh);
|
|
}
|
|
*/
|
|
|
|
public useBlockMeshChunk(params: RenderNextBlockMeshChunkParams.Output) {
|
|
if (params.isFirstChunk) {
|
|
this._blockBuffer = [];
|
|
|
|
// re-create objects, due to serialization.
|
|
const min = new Vector3(0, 0, 0);
|
|
const max = new Vector3(0, 0, 0);
|
|
min.setFrom(params.bounds['_min']);
|
|
max.setFrom(params.bounds['_max']);
|
|
|
|
this._blockBounds = new Bounds(min, max);
|
|
this._sliceHeight = this._blockBounds.min.y;
|
|
}
|
|
|
|
this._blockBuffer?.push(twgl.createBufferInfoFromArrays(this._gl, params.buffer.buffer));
|
|
|
|
if (params.isFirstChunk) {
|
|
this._atlasTexture = twgl.createTexture(this._gl, {
|
|
src: VANILLA_TEXTURE,
|
|
mag: this._gl.NEAREST,
|
|
}, () => {
|
|
this.forceRedraw();
|
|
});
|
|
|
|
this._atlasSize = params.atlasSize;
|
|
|
|
this._gridBuffers.y[MeshType.BlockMesh] = this._gridBuffers.y[MeshType.VoxelMesh];
|
|
|
|
this._modelsAvailable = 3;
|
|
this.setModelToUse(MeshType.BlockMesh);
|
|
}
|
|
}
|
|
|
|
// /////////////////////////////////////////////////////////////////////////
|
|
|
|
private _drawDebug() {
|
|
/*
|
|
const debugComponents = [EDebugBufferComponents.GridY];
|
|
for (const debugComp of debugComponents) {
|
|
if (this._isGridComponentEnabled[debugComp]) {
|
|
ASSERT(this._debugBuffers[this._meshToUse]);
|
|
const buffer = this._debugBuffers[this._meshToUse][debugComp];
|
|
if (buffer) {
|
|
if (debugComp === EDebugBufferComponents.Dev) {
|
|
this._gl.disable(this._gl.DEPTH_TEST);
|
|
}
|
|
if (debugComp === EDebugBufferComponents.GridY && !ArcballCamera.Get.isAlignedWithAxis('y')) {
|
|
continue;
|
|
}
|
|
this._drawBuffer(this._gl.LINES, buffer.getWebGLBuffer(), ShaderManager.Get.debugProgram, {
|
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
|
});
|
|
this._gl.enable(this._gl.DEPTH_TEST);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
// Draw grid
|
|
if (this._gridEnabled) {
|
|
if (ArcballCamera.Get.isAlignedWithAxis('x') && !ArcballCamera.Get.isAlignedWithAxis('y') && !ArcballCamera.Get.isUserRotating) {
|
|
const gridBuffer = this._gridBuffers.x[this._meshToUse];
|
|
if (gridBuffer !== undefined) {
|
|
this._drawBuffer(this._gl.LINES, gridBuffer.getWebGLBuffer(), ShaderManager.Get.debugProgram, {
|
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
|
});
|
|
}
|
|
} else if (ArcballCamera.Get.isAlignedWithAxis('z') && !ArcballCamera.Get.isAlignedWithAxis('y') && !ArcballCamera.Get.isUserRotating) {
|
|
const gridBuffer = this._gridBuffers.z[this._meshToUse];
|
|
if (gridBuffer !== undefined) {
|
|
this._drawBuffer(this._gl.LINES, gridBuffer.getWebGLBuffer(), ShaderManager.Get.debugProgram, {
|
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
|
});
|
|
}
|
|
} else {
|
|
const gridBuffer = this._gridBuffers.y[this._meshToUse];
|
|
if (gridBuffer !== undefined) {
|
|
this._drawBuffer(this._gl.LINES, gridBuffer.getWebGLBuffer(), ShaderManager.Get.debugProgram, {
|
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
|
u_worldOffset: [0, this._sliceViewEnabled ? this._sliceHeight * this._voxelSize: 0, 0],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw axis
|
|
if (this._axesEnabled) {
|
|
this._gl.disable(this._gl.DEPTH_TEST);
|
|
this._drawBuffer(this._gl.LINES, this._axisBuffer.getWebGLBuffer(), ShaderManager.Get.debugProgram, {
|
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
|
});
|
|
this._gl.enable(this._gl.DEPTH_TEST);
|
|
}
|
|
}
|
|
|
|
private _drawMesh() {
|
|
this._materialBuffers.forEach((materialBuffer, materialName) => {
|
|
if (materialBuffer.material.type === MaterialType.textured) {
|
|
this._drawMeshBuffer(materialBuffer.buffer, materialBuffer.numElements, ShaderManager.Get.textureTriProgram, {
|
|
u_lightWorldPos: ArcballCamera.Get.getCameraPosition(-Math.PI/4, 0.0).toArray(),
|
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
|
u_worldInverseTranspose: ArcballCamera.Get.getWorldInverseTranspose(),
|
|
u_texture: materialBuffer.material.diffuseTexture,
|
|
u_alpha: materialBuffer.material.alphaTexture ?? materialBuffer.material.diffuseTexture,
|
|
u_alphaChannel: materialBuffer.material.alphaChannel,
|
|
u_alphaFactor: materialBuffer.material.alphaValue,
|
|
u_cameraDir: ArcballCamera.Get.getCameraDirection().toArray(),
|
|
u_fresnelExponent: AppConfig.Get.FRESNEL_EXPONENT,
|
|
u_fresnelMix: AppConfig.Get.FRESNEL_MIX,
|
|
});
|
|
} else {
|
|
this._drawMeshBuffer(materialBuffer.buffer, materialBuffer.numElements, ShaderManager.Get.solidTriProgram, {
|
|
u_lightWorldPos: ArcballCamera.Get.getCameraPosition(-Math.PI/4, 0.0).toArray(),
|
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
|
u_worldInverseTranspose: ArcballCamera.Get.getWorldInverseTranspose(),
|
|
u_fillColour: materialBuffer.material.colourArray,
|
|
u_cameraDir: ArcballCamera.Get.getCameraDirection().toArray(),
|
|
u_fresnelExponent: AppConfig.Get.FRESNEL_EXPONENT,
|
|
u_fresnelMix: AppConfig.Get.FRESNEL_MIX,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
private _drawVoxelMesh() {
|
|
const shader = ShaderManager.Get.voxelProgram;
|
|
const uniforms = {
|
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
|
u_voxelSize: this._voxelSize,
|
|
u_gridOffset: this._gridOffset.toArray(),
|
|
u_ambientOcclusion: this._allVoxelChunks,
|
|
};
|
|
this._voxelBuffer?.forEach((buffer) => {
|
|
this._gl.useProgram(shader.program);
|
|
twgl.setBuffersAndAttributes(this._gl, shader, buffer);
|
|
twgl.setUniforms(shader, uniforms);
|
|
this._gl.drawElements(this._gl.TRIANGLES, buffer.numElements, this._gl.UNSIGNED_INT, 0);
|
|
});
|
|
}
|
|
|
|
private _drawBlockMesh() {
|
|
this._gl.enable(this._gl.CULL_FACE);
|
|
const shader = ShaderManager.Get.blockProgram;
|
|
const uniforms = {
|
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
|
u_texture: this._atlasTexture,
|
|
u_voxelSize: this._voxelSize,
|
|
u_atlasSize: this._atlasSize,
|
|
u_gridOffset: this._gridOffset.toArray(),
|
|
u_nightVision: this.isNightVisionEnabled(),
|
|
u_sliceHeight: this._sliceViewEnabled ? this._sliceHeight : Infinity,
|
|
};
|
|
this._blockBuffer?.forEach((buffer) => {
|
|
this._gl.useProgram(shader.program);
|
|
twgl.setBuffersAndAttributes(this._gl, shader, buffer);
|
|
twgl.setUniforms(shader, uniforms);
|
|
this._gl.drawElements(this._gl.TRIANGLES, buffer.numElements, this._gl.UNSIGNED_INT, 0);
|
|
});
|
|
this._gl.disable(this._gl.CULL_FACE);
|
|
}
|
|
|
|
// /////////////////////////////////////////////////////////////////////////
|
|
|
|
private _drawMeshBuffer(register: twgl.BufferInfo, numElements: number, shaderProgram: twgl.ProgramInfo, uniforms: any) {
|
|
this._drawBuffer(this._gl.TRIANGLES, { buffer: register, numElements: numElements }, shaderProgram, uniforms);
|
|
}
|
|
|
|
public setModelToUse(meshType: MeshType) {
|
|
const isModelAvailable = this._modelsAvailable >= meshType;
|
|
if (isModelAvailable) {
|
|
this._meshToUse = meshType;
|
|
}
|
|
this.forceRedraw();
|
|
}
|
|
|
|
private _setupScene() {
|
|
twgl.resizeCanvasToDisplaySize(<HTMLCanvasElement>this._gl.canvas);
|
|
this._gl.viewport(0, 0, this._gl.canvas.width, this._gl.canvas.height);
|
|
ArcballCamera.Get.setAspect(this._gl.canvas.width / this._gl.canvas.height);
|
|
this._gl.blendFuncSeparate(this._gl.SRC_ALPHA, this._gl.ONE_MINUS_SRC_ALPHA, this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA);
|
|
|
|
this._gl.enable(this._gl.DEPTH_TEST);
|
|
this._gl.enable(this._gl.BLEND);
|
|
this._gl.clearColor(this._backgroundColour.r, this._backgroundColour.g, this._backgroundColour.b, 1.0);
|
|
this._gl.clear(this._gl.COLOR_BUFFER_BIT | this._gl.DEPTH_BUFFER_BIT);
|
|
}
|
|
|
|
private _drawBuffer(drawMode: number, buffer: { numElements: number, buffer: twgl.BufferInfo }, shader: twgl.ProgramInfo, uniforms: any) {
|
|
this._gl.useProgram(shader.program);
|
|
twgl.setBuffersAndAttributes(this._gl, shader, buffer.buffer);
|
|
twgl.setUniforms(shader, uniforms);
|
|
this._gl.drawElements(drawMode, buffer.numElements, this._gl.UNSIGNED_INT, 0);
|
|
}
|
|
|
|
public getModelsAvailable() {
|
|
return this._modelsAvailable;
|
|
}
|
|
|
|
public getActiveMeshType() {
|
|
return this._meshToUse;
|
|
}
|
|
}
|