Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions demo/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { mat4 } from 'gl-matrix'
import type { ItemRendererResources, ItemRenderingContext, NbtTag, Resources, Voxel } from '../src/index.js'
import { BlockDefinition, BlockModel, Identifier, ItemRenderer, ItemStack, jsonToNbt, NormalNoise, Structure, StructureRenderer, TextureAtlas, upperPowerOfTwo, VoxelRenderer, XoroshiroRandom } from '../src/index.js'
import { BlockDefinition, BlockModel, Identifier, ItemRenderer, ItemStack, NormalNoise, Structure, StructureRenderer, TextureAtlas, VoxelRenderer, XoroshiroRandom, jsonToNbt, upperPowerOfTwo } from '../src/index.js'
import { } from '../src/nbt/Util.js'
import { ItemModel } from '../src/render/ItemModel.js'

Expand Down Expand Up @@ -136,6 +136,7 @@ Promise.all([
getBlockModel(id) { return blockModels[id.toString()] },
getTextureUV(id) { return textureAtlas.getTextureUV(id) },
getTextureAtlas() { return textureAtlas.getTextureAtlas() },
getPixelSize() { return textureAtlas.getPixelSize() },
getBlockFlags(id) { return { opaque: false } },
getBlockProperties(id) { return null },
getDefaultBlockProperties(id) { return null },
Expand Down Expand Up @@ -174,13 +175,14 @@ Promise.all([

// === Structure rendering ===

const structure = new Structure([3, 2, 1])
const structure = new Structure([3, 2, 2])
const size = structure.getSize()
structure.addBlock([1, 0, 0], 'minecraft:grass_block', { snowy: 'false' })
structure.addBlock([2, 0, 0], 'minecraft:stone')
structure.addBlock([1, 1, 0], 'minecraft:skeleton_skull', { rotation: '15' })
structure.addBlock([2, 1, 0], 'minecraft:acacia_fence', { waterlogged: 'true', north: 'true' })
structure.addBlock([0, 0, 0], 'minecraft:wall_torch', { facing: 'west' })
structure.addBlock([1, 0, 1], 'minecraft:oak_trapdoor', { facing: 'south', half: 'bottom', open: 'true', powered: 'false', waterlogged: 'false' })

const structureCanvas = document.getElementById('structure-display') as HTMLCanvasElement
const structureGl = structureCanvas.getContext('webgl')!
Expand Down
19 changes: 6 additions & 13 deletions src/render/BlockModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ export class BlockModel {
private static readonly BUILTIN_GENERATED = Identifier.create('builtin/generated')
private static readonly GENERATED_LAYERS = ['layer0', 'layer1', 'layer2', 'layer3', 'layer4']
private generationMarker = false
private uvEpsilon = 1/16

constructor(
private parent: Identifier | undefined,
Expand Down Expand Up @@ -135,18 +134,17 @@ export class BlockModel {
const [u0, v0, u1, v1] = atlas.getTextureUV(this.getTexture(face.texture))
const du = (u1 - u0) / 16
const dv = (v1 - v0) / 16
const duu = du * this.uvEpsilon
const dvv = dv * this.uvEpsilon
uv[0] = (face.uv?.[0] ?? uv[0]) * du + duu
uv[1] = (face.uv?.[1] ?? uv[1]) * dv + dvv
uv[2] = (face.uv?.[2] ?? uv[2]) * du - duu
uv[3] = (face.uv?.[3] ?? uv[3]) * dv - dvv
uv[0] = (face.uv?.[0] ?? uv[0]) * du
uv[1] = (face.uv?.[1] ?? uv[1]) * dv
uv[2] = (face.uv?.[2] ?? uv[2]) * du
uv[3] = (face.uv?.[3] ?? uv[3]) * dv
const r = faceRotations[face.rotation ?? 0]
quad.setTexture([
u0 + uv[r[0]], v0 + uv[r[1]],
u0 + uv[r[2]], v0 + uv[r[3]],
u0 + uv[r[4]], v0 + uv[r[5]],
u0 + uv[r[6]], v0 + uv[r[7]]])
u0 + uv[r[6]], v0 + uv[r[7]],
], [u0 + Math.min(uv[0], uv[2]), v0 + Math.min(uv[1], uv[3]), u0 + Math.max(uv[0], uv[2]), v0 + Math.max(uv[1], uv[3])])
mesh.quads.push(quad)
}

Expand Down Expand Up @@ -196,11 +194,6 @@ export class BlockModel {
return Identifier.parse(textureRef)
}

public withUvEpsilon(epsilon: number) {
this.uvEpsilon = epsilon
return this
}

public flatten(accessor: BlockModelProvider) {
if (!this.parent) {
return
Expand Down
4 changes: 2 additions & 2 deletions src/render/ItemRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { mat4 } from 'gl-matrix'
import { Identifier } from '../core/index.js'
import type { ItemComponentsProvider, ItemStack } from '../core/ItemStack.js'
import { Identifier } from '../core/index.js'
import type { Color } from '../index.js'
import type { BlockModelProvider, Display } from './BlockModel.js'
import type { ItemModelProvider } from './ItemModel.js'
Expand Down Expand Up @@ -90,7 +90,7 @@ export class ItemRenderer extends Renderer {
mat4.translate(view, view, [0, 0, -32])

this.setShader(this.shaderProgram)
this.setTexture(this.atlasTexture)
this.setTexture(this.atlasTexture, this.resources.getPixelSize?.())
this.prepareDraw(view)
this.drawMesh(this.mesh, { pos: true, color: true, texture: true, normal: true })
}
Expand Down
2 changes: 2 additions & 0 deletions src/render/Mesh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class Mesh {
public posBuffer: WebGLBuffer | undefined
public colorBuffer: WebGLBuffer | undefined
public textureBuffer: WebGLBuffer | undefined
public textureLimitBuffer: WebGLBuffer | undefined
public normalBuffer: WebGLBuffer | undefined
public blockPosBuffer: WebGLBuffer | undefined
public indexBuffer: WebGLBuffer | undefined
Expand Down Expand Up @@ -126,6 +127,7 @@ export class Mesh {
}
if (options.texture) {
this.textureBuffer = rebuildBufferV(this.quads, this.textureBuffer, v => v.texture)
this.textureLimitBuffer = rebuildBufferV(this.quads, this.textureLimitBuffer, v => v.textureLimit)
}
if (options.normal) {
this.normalBuffer = rebuildBufferV(this.quads, this.normalBuffer, v => v.normal?.components())
Expand Down
7 changes: 6 additions & 1 deletion src/render/Quad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ export class Quad {
return this
}

public setTexture(texture: number[]) {
public setTexture(texture: number[], textureLimit?: [number, number, number, number]) {
this.v1.textureLimit = textureLimit
this.v2.textureLimit = textureLimit
this.v3.textureLimit = textureLimit
this.v4.textureLimit = textureLimit

this.v1.texture = [texture[0], texture[1]]
this.v2.texture = [texture[2], texture[3]]
this.v3.texture = [texture[4], texture[5]]
Expand Down
23 changes: 19 additions & 4 deletions src/render/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@ import { ShaderProgram } from './ShaderProgram.js'
const vsSource = `
attribute vec4 vertPos;
attribute vec2 texCoord;
attribute vec4 texLimit;
attribute vec3 vertColor;
attribute vec3 normal;

uniform mat4 mView;
uniform mat4 mProj;

varying highp vec2 vTexCoord;
varying highp vec4 vTexLimit;
varying highp vec3 vTintColor;
varying highp float vLighting;

void main(void) {
gl_Position = mProj * mView * vertPos;
vTexCoord = texCoord;
vTexLimit = texLimit;
vTintColor = vertColor;
vLighting = normal.y * 0.2 + abs(normal.z) * 0.1 + 0.8;
}
Expand All @@ -26,13 +29,18 @@ const vsSource = `
const fsSource = `
precision highp float;
varying highp vec2 vTexCoord;
varying highp vec4 vTexLimit;
varying highp vec3 vTintColor;
varying highp float vLighting;

uniform sampler2D sampler;
uniform highp float pixelSize;

void main(void) {
vec4 texColor = texture2D(sampler, vTexCoord);
vec4 texColor = texture2D(sampler, clamp(vTexCoord,
vTexLimit.xy + vec2(0.5, 0.5) * pixelSize,
vTexLimit.zw - vec2(0.5, 0.5) * pixelSize
));
if(texColor.a < 0.01) discard;
gl_FragColor = vec4(texColor.xyz * vTintColor * vLighting, texColor.a);
}
Expand All @@ -43,9 +51,10 @@ export class Renderer {

protected projMatrix: mat4
private activeShader: WebGLProgram
private pixelSize: number = 0

constructor(
protected readonly gl: WebGLRenderingContext,
protected readonly gl: WebGLRenderingContext
) {
this.shaderProgram = new ShaderProgram(gl, vsSource, fsSource).getProgram()
this.activeShader = this.shaderProgram
Expand Down Expand Up @@ -95,9 +104,10 @@ export class Renderer {
this.gl.uniformMatrix4fv(location, false, value)
}

protected setTexture(texture: WebGLTexture) {
protected setTexture(texture: WebGLTexture, pixelSize?: number) {
this.gl.activeTexture(this.gl.TEXTURE0)
this.gl.bindTexture(this.gl.TEXTURE_2D, texture)
this.pixelSize = pixelSize ?? 0
}

protected createAtlasTexture(image: ImageData) {
Expand All @@ -112,13 +122,18 @@ export class Renderer {
protected prepareDraw(viewMatrix: mat4) {
this.setUniform('mView', viewMatrix)
this.setUniform('mProj', this.projMatrix)
const location = this.gl.getUniformLocation(this.activeShader, 'pixelSize')
this.gl.uniform1f(location, this.pixelSize)
}

protected drawMesh(mesh: Mesh, options: { pos?: boolean, color?: boolean, texture?: boolean, normal?: boolean, blockPos?: boolean }) {
if (mesh.quadVertices() > 0) {
if (options.pos) this.setVertexAttr('vertPos', 3, mesh.posBuffer)
if (options.color) this.setVertexAttr('vertColor', 3, mesh.colorBuffer)
if (options.texture) this.setVertexAttr('texCoord', 2, mesh.textureBuffer)
if (options.texture){
this.setVertexAttr('texCoord', 2, mesh.textureBuffer)
this.setVertexAttr('texLimit', 4, mesh.textureLimitBuffer)
}
if (options.normal) this.setVertexAttr('normal', 3, mesh.normalBuffer)
if (options.blockPos) this.setVertexAttr('blockPos', 3, mesh.blockPosBuffer)

Expand Down
24 changes: 12 additions & 12 deletions src/render/SpecialRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export namespace SpecialRenderers {
down: {uv: [7.5, 0, 7.375, 0.25], texture: '#0'},
},
},
]).withUvEpsilon(1/256).getMesh(atlas, Cull.none()).transform(transformation)
]).getMesh(atlas, Cull.none()).transform(transformation)
}
}

Expand Down Expand Up @@ -355,7 +355,7 @@ export namespace SpecialRenderers {
down: {uv: [14.25, 1.5, 14, 2.5], texture: '#0'},
},
},
]).withUvEpsilon(1/128).getMesh(atlas, Cull.none())
]).getMesh(atlas, Cull.none())
}
}

Expand Down Expand Up @@ -388,7 +388,7 @@ export namespace SpecialRenderers {
down: {uv: [1.5, 7, 1, 8], texture: '#0'},
},
},
]).withUvEpsilon(1/128).getMesh(atlas, Cull.none())
]).getMesh(atlas, Cull.none())
}
}

Expand All @@ -409,7 +409,7 @@ export namespace SpecialRenderers {
down: {uv: [12.5, 0, 6.5, 1], texture: '#0'},
},
},
]).withUvEpsilon(1/128).getMesh(atlas, Cull.none())
]).getMesh(atlas, Cull.none())
}
}

Expand Down Expand Up @@ -439,7 +439,7 @@ export namespace SpecialRenderers {
south: {uv: [3.5, 3, 6.5, 6], texture: '#0'},
},
},
]).withUvEpsilon(1/128).getMesh(atlas, Cull.none())
]).getMesh(atlas, Cull.none())
}
return new BlockModel(undefined, {
0: texture.withPrefix('entity/signs/hanging/').toString(),
Expand Down Expand Up @@ -492,7 +492,7 @@ export namespace SpecialRenderers {
west: {uv: [1.5, 3, 2.25, 6], texture: '#0'},
},
},
]).withUvEpsilon(1/128).getMesh(atlas, Cull.none())
]).getMesh(atlas, Cull.none())
}
}

Expand Down Expand Up @@ -561,7 +561,7 @@ export namespace SpecialRenderers {
west: {uv: [1.5, 3, 2.25, 6], texture: '#0'},
},
},
]).withUvEpsilon(1/128).getMesh(atlas, Cull.none())
]).getMesh(atlas, Cull.none())
}
}

Expand All @@ -581,7 +581,7 @@ export namespace SpecialRenderers {
down: {uv: [9, 0, 6, 6], texture: '#0'},
},
},
]).withUvEpsilon(1/128).getMesh(atlas, Cull.none())
]).getMesh(atlas, Cull.none())
}

export function shulkerBoxRenderer(texture: Identifier) {
Expand Down Expand Up @@ -613,7 +613,7 @@ export namespace SpecialRenderers {
down: {uv: [12, 0, 8, 4], texture: '#0'},
},
},
]).withUvEpsilon(1/128).getMesh(atlas, Cull.none())
]).getMesh(atlas, Cull.none())
}
}

Expand Down Expand Up @@ -740,7 +740,7 @@ export namespace SpecialRenderers {
down: {uv: [12, 6.5, 8, 10.5], texture: '#0'},
},
},
]).withUvEpsilon(1 / 64).getMesh(atlas, Cull.none())
]).getMesh(atlas, Cull.none())
}

export function bedRenderer(texture: Identifier) {
Expand Down Expand Up @@ -784,7 +784,7 @@ export namespace SpecialRenderers {
down: {uv: [14, 3, 14.75, 3.75], texture: '#0'},
},
},
]).withUvEpsilon(1/128).getMesh(atlas, Cull.none())
]).getMesh(atlas, Cull.none())
}
return new BlockModel(undefined, {
0: texture.withPrefix('entity/bed/').toString(),
Expand Down Expand Up @@ -824,7 +824,7 @@ export namespace SpecialRenderers {
down: {uv: [14, 1.5, 14.75, 2.25], texture: '#0'},
},
},
]).withUvEpsilon(1/128).getMesh(atlas, Cull.none())
]).getMesh(atlas, Cull.none())
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/render/StructureRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export class StructureRenderer extends Renderer {

public drawStructure(viewMatrix: mat4) {
this.setShader(this.shaderProgram)
this.setTexture(this.atlasTexture)
this.setTexture(this.atlasTexture, this.resources.getPixelSize?.())
this.prepareDraw(viewMatrix)

this.chunkBuilder.getMeshes().forEach(mesh => {
Expand Down
5 changes: 5 additions & 0 deletions src/render/TextureAtlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type UV = [number, number, number, number]
export interface TextureAtlasProvider {
getTextureAtlas(): ImageData
getTextureUV(texture: Identifier): UV
getPixelSize?(): number;
}

export class TextureAtlas implements TextureAtlasProvider {
Expand All @@ -29,6 +30,10 @@ export class TextureAtlas implements TextureAtlasProvider {
return this.idMap[id.toString()] ?? [0, 0, this.part, this.part]
}

public getPixelSize() {
return this.part / 16
}

public static async fromBlobs(textures: { [id: string]: Blob }): Promise<TextureAtlas> {
const initialWidth = Math.sqrt(Object.keys(textures).length + 1)
const width = upperPowerOfTwo(initialWidth)
Expand Down
3 changes: 2 additions & 1 deletion src/render/Vertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export class Vertex {
public pos: Vector,
public color: Color,
public texture: [number, number] | undefined,
public textureLimit: [number, number, number, number] | undefined,
public normal: Vector | undefined,
public blockPos: Vector | undefined,
) {}
Expand All @@ -24,6 +25,6 @@ export class Vertex {
}

public static fromPos(pos: Vector) {
return new Vertex(pos, [0, 0, 0], [0, 0], undefined, undefined)
return new Vertex(pos, [0, 0, 0], [0, 0], [0, 0, 0, 0], undefined, undefined)
}
}