minecraft-renderer 0.1.74 → 0.1.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/minecraft-renderer.js +58 -58
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +428 -428
- package/package.json +1 -1
- package/src/three/chunkMeshManager.ts +10 -77
- package/src/three/signTextureCache.ts +64 -0
- package/src/three/tests/signTextureCache.test.ts +60 -39
- package/src/three/worldRendererThree.ts +1 -84
package/package.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
//@ts-nocheck
|
|
2
|
-
import PrismarineChatLoader from 'prismarine-chat'
|
|
3
2
|
import * as THREE from 'three'
|
|
4
3
|
import * as nbt from 'prismarine-nbt'
|
|
5
4
|
import { Vec3 } from 'vec3'
|
|
@@ -20,13 +19,12 @@ import {
|
|
|
20
19
|
sectionAabbIntersectsRay,
|
|
21
20
|
type ShaderSectionRaycastEntry,
|
|
22
21
|
} from './sectionRaycastAabb'
|
|
23
|
-
import { chunkPos } from '../lib/simpleUtils'
|
|
24
|
-
import { renderSign } from '../sign-renderer'
|
|
25
22
|
import { getMesh } from './entity/EntityMesh'
|
|
26
23
|
import type { WorldRendererThree } from './worldRendererThree'
|
|
27
24
|
import { armorModel } from './entity/armorModels'
|
|
28
25
|
import { disposeObject } from './threeJsUtils'
|
|
29
26
|
import { getBannerTexture, createBannerMesh, releaseBannerTexture } from './bannerRenderer'
|
|
27
|
+
import { getSignTexture, releaseSignTexture, disposeAllSignTextures } from './signTextureCache'
|
|
30
28
|
import { BlockEntityLightRegistry } from '../lib/blockEntityLightRegistry'
|
|
31
29
|
|
|
32
30
|
export interface ChunkMeshPool {
|
|
@@ -1348,8 +1346,12 @@ export class ChunkMeshManager {
|
|
|
1348
1346
|
sectionObject.shaderMesh = undefined
|
|
1349
1347
|
}
|
|
1350
1348
|
delete sectionObject.deferredShaderCubes
|
|
1351
|
-
//
|
|
1349
|
+
// Release shared sign textures (refcount) before disposing meshes
|
|
1352
1350
|
if (sectionObject.signsContainer) {
|
|
1351
|
+
for (const child of sectionObject.signsContainer.children) {
|
|
1352
|
+
const sign = child as THREE.Group & { signTexture?: THREE.Texture }
|
|
1353
|
+
if (sign.signTexture) releaseSignTexture(sign.signTexture)
|
|
1354
|
+
}
|
|
1353
1355
|
this.disposeContainer(sectionObject.signsContainer, false)
|
|
1354
1356
|
}
|
|
1355
1357
|
if (sectionObject.headsContainer) {
|
|
@@ -1473,16 +1475,6 @@ export class ChunkMeshManager {
|
|
|
1473
1475
|
}
|
|
1474
1476
|
}
|
|
1475
1477
|
|
|
1476
|
-
/**
|
|
1477
|
-
* Forward to {@link SignHeadsRenderer.cleanChunkTextures} so callers in
|
|
1478
|
-
* `WorldRendererThree` (which historically owned the sign-texture cache)
|
|
1479
|
-
* can invalidate cached sign textures when a section is marked dirty,
|
|
1480
|
-
* without reaching into the manager's private members.
|
|
1481
|
-
*/
|
|
1482
|
-
cleanSignChunkTextures (x: number, z: number) {
|
|
1483
|
-
this.signHeadsRenderer.cleanChunkTextures(x, z)
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
1478
|
/**
|
|
1487
1479
|
* Get mesh for section if it exists
|
|
1488
1480
|
*/
|
|
@@ -1923,21 +1915,12 @@ export class ChunkMeshManager {
|
|
|
1923
1915
|
}
|
|
1924
1916
|
|
|
1925
1917
|
|
|
1926
|
-
type SignTextureCacheEntry = { tex: THREE.Texture, signature: string }
|
|
1927
|
-
|
|
1928
1918
|
class SignHeadsRenderer {
|
|
1929
|
-
chunkTextures = new Map<string, { [pos: string]: SignTextureCacheEntry }>()
|
|
1930
|
-
|
|
1931
1919
|
constructor (public worldRendererThree: WorldRendererThree) {
|
|
1932
1920
|
}
|
|
1933
1921
|
|
|
1934
1922
|
dispose () {
|
|
1935
|
-
|
|
1936
|
-
for (const key of Object.keys(textures)) {
|
|
1937
|
-
textures[key]!.tex.dispose()
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
this.chunkTextures.clear()
|
|
1923
|
+
disposeAllSignTextures()
|
|
1941
1924
|
}
|
|
1942
1925
|
|
|
1943
1926
|
renderHead (position: Vec3, rotation: number, isWall: boolean, blockEntity) {
|
|
@@ -1981,18 +1964,10 @@ class SignHeadsRenderer {
|
|
|
1981
1964
|
}
|
|
1982
1965
|
|
|
1983
1966
|
renderSign (position: Vec3, rotation: number, isWall: boolean, isHanging: boolean, blockEntity) {
|
|
1984
|
-
const tex = this.
|
|
1967
|
+
const tex = getSignTexture(this.worldRendererThree, blockEntity, isHanging)
|
|
1985
1968
|
|
|
1986
1969
|
if (!tex) return
|
|
1987
1970
|
|
|
1988
|
-
// todo implement
|
|
1989
|
-
// const key = JSON.stringify({ position, rotation, isWall })
|
|
1990
|
-
// if (this.signsCache.has(key)) {
|
|
1991
|
-
// console.log('cached', key)
|
|
1992
|
-
// } else {
|
|
1993
|
-
// this.signsCache.set(key, tex)
|
|
1994
|
-
// }
|
|
1995
|
-
|
|
1996
1971
|
const mesh = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), new THREE.MeshBasicMaterial({ map: tex, transparent: true }))
|
|
1997
1972
|
mesh.renderOrder = 999
|
|
1998
1973
|
|
|
@@ -2008,13 +1983,14 @@ class SignHeadsRenderer {
|
|
|
2008
1983
|
mesh.position.set(0, 0, thickness / 2 + 0.0001)
|
|
2009
1984
|
}
|
|
2010
1985
|
|
|
2011
|
-
const group = new THREE.Group()
|
|
1986
|
+
const group = new THREE.Group() as THREE.Group & { signTexture?: THREE.Texture }
|
|
2012
1987
|
group.rotation.set(
|
|
2013
1988
|
0,
|
|
2014
1989
|
-THREE.MathUtils.degToRad(rotation * (isWall ? 90 : 45 / 2)),
|
|
2015
1990
|
0
|
|
2016
1991
|
)
|
|
2017
1992
|
group.add(mesh)
|
|
1993
|
+
group.signTexture = tex
|
|
2018
1994
|
const height = (isHanging ? 10 : 8) / 16
|
|
2019
1995
|
const heightOffset = (isHanging ? 0 : isWall ? 4.333 : 9.333) / 16
|
|
2020
1996
|
const textPosition = height / 2 + heightOffset
|
|
@@ -2022,47 +1998,4 @@ class SignHeadsRenderer {
|
|
|
2022
1998
|
group.position.set(position.x + 0.5, position.y + textPosition, position.z + 0.5)
|
|
2023
1999
|
return group
|
|
2024
2000
|
}
|
|
2025
|
-
|
|
2026
|
-
getSignTexture (position: Vec3, blockEntity, isHanging, backSide = false) {
|
|
2027
|
-
const chunk = chunkPos(position)
|
|
2028
|
-
let textures = this.chunkTextures.get(`${chunk[0]},${chunk[1]}`)
|
|
2029
|
-
if (!textures) {
|
|
2030
|
-
textures = {}
|
|
2031
|
-
this.chunkTextures.set(`${chunk[0]},${chunk[1]}`, textures)
|
|
2032
|
-
}
|
|
2033
|
-
const texturekey = `${position.x},${position.y},${position.z}`
|
|
2034
|
-
const signature = JSON.stringify(blockEntity) + '|' + isHanging + '|' + backSide
|
|
2035
|
-
const cached = textures[texturekey]
|
|
2036
|
-
if (cached && cached.signature === signature) return cached.tex
|
|
2037
|
-
|
|
2038
|
-
if (cached?.tex) cached.tex.dispose()
|
|
2039
|
-
|
|
2040
|
-
const PrismarineChat = PrismarineChatLoader(this.worldRendererThree.version)
|
|
2041
|
-
const canvas = renderSign(blockEntity, isHanging, PrismarineChat)
|
|
2042
|
-
if (!canvas) return
|
|
2043
|
-
const tex = new THREE.Texture(canvas)
|
|
2044
|
-
tex.magFilter = THREE.NearestFilter
|
|
2045
|
-
tex.minFilter = THREE.NearestFilter
|
|
2046
|
-
tex.needsUpdate = true
|
|
2047
|
-
textures[texturekey] = { tex, signature }
|
|
2048
|
-
return tex
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
|
-
/**
|
|
2052
|
-
* Dispose all cached sign textures for the chunk containing world coords
|
|
2053
|
-
* (x, z). Called from `WorldRendererThree.cleanChunkTextures` so that
|
|
2054
|
-
* re-meshes triggered by `setSectionDirty` (e.g. a player edits a sign)
|
|
2055
|
-
* pick up fresh block-entity NBT instead of returning the stale cached
|
|
2056
|
-
* texture from {@link SignHeadsRenderer.getSignTexture}.
|
|
2057
|
-
*/
|
|
2058
|
-
cleanChunkTextures (x: number, z: number) {
|
|
2059
|
-
const key = `${Math.floor(x / 16)},${Math.floor(z / 16)}`
|
|
2060
|
-
const textures = this.chunkTextures.get(key)
|
|
2061
|
-
if (!textures) return
|
|
2062
|
-
const disposedKeys = Object.keys(textures)
|
|
2063
|
-
for (const k of disposedKeys) {
|
|
2064
|
-
textures[k]!.tex.dispose()
|
|
2065
|
-
delete textures[k]
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
2001
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import * as THREE from 'three'
|
|
3
|
+
import PrismarineChatLoader from 'prismarine-chat'
|
|
4
|
+
import { renderSign } from '../sign-renderer'
|
|
5
|
+
import type { WorldRendererThree } from './worldRendererThree'
|
|
6
|
+
|
|
7
|
+
const signTextureCache = new Map<string, { texture: THREE.Texture, refCount: number }>()
|
|
8
|
+
|
|
9
|
+
// Build the key ONLY from fields that change rendered pixels, so two signs
|
|
10
|
+
// with identical visible text share a texture even if other NBT differs.
|
|
11
|
+
function createSignCacheKey (blockEntity: any, isHanging: boolean, backSide: boolean): string {
|
|
12
|
+
let lines: string[]
|
|
13
|
+
let color: string
|
|
14
|
+
if (blockEntity && 'front_text' in blockEntity) { // 1.20+
|
|
15
|
+
lines = blockEntity.front_text?.messages ?? []
|
|
16
|
+
color = blockEntity.front_text?.color || 'black'
|
|
17
|
+
} else { // legacy
|
|
18
|
+
lines = [blockEntity?.Text1, blockEntity?.Text2, blockEntity?.Text3, blockEntity?.Text4]
|
|
19
|
+
color = blockEntity?.Color || 'black'
|
|
20
|
+
}
|
|
21
|
+
// \0 separator: cannot appear in JSON text components, so no key collisions
|
|
22
|
+
return `${isHanging ? 1 : 0}|${backSide ? 1 : 0}|${color}|${lines.join('\0')}`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getSignTexture (
|
|
26
|
+
worldRenderer: WorldRendererThree,
|
|
27
|
+
blockEntity: any,
|
|
28
|
+
isHanging: boolean,
|
|
29
|
+
backSide = false
|
|
30
|
+
): THREE.Texture | undefined {
|
|
31
|
+
const cacheKey = createSignCacheKey(blockEntity, isHanging, backSide)
|
|
32
|
+
const cached = signTextureCache.get(cacheKey)
|
|
33
|
+
if (cached) {
|
|
34
|
+
cached.refCount++
|
|
35
|
+
return cached.texture
|
|
36
|
+
}
|
|
37
|
+
const PrismarineChat = PrismarineChatLoader(worldRenderer.version)
|
|
38
|
+
const canvas = renderSign(blockEntity, isHanging, PrismarineChat)
|
|
39
|
+
if (!canvas) return undefined
|
|
40
|
+
const tex = new THREE.Texture(canvas)
|
|
41
|
+
tex.magFilter = THREE.NearestFilter
|
|
42
|
+
tex.minFilter = THREE.NearestFilter
|
|
43
|
+
tex.needsUpdate = true
|
|
44
|
+
signTextureCache.set(cacheKey, { texture: tex, refCount: 1 })
|
|
45
|
+
return tex
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function releaseSignTexture (texture: THREE.Texture): void {
|
|
49
|
+
for (const [key, cached] of signTextureCache.entries()) {
|
|
50
|
+
if (cached.texture === texture) {
|
|
51
|
+
cached.refCount--
|
|
52
|
+
if (cached.refCount <= 0) {
|
|
53
|
+
cached.texture.dispose()
|
|
54
|
+
signTextureCache.delete(key)
|
|
55
|
+
}
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function disposeAllSignTextures (): void {
|
|
62
|
+
for (const [, cached] of signTextureCache) cached.texture.dispose()
|
|
63
|
+
signTextureCache.clear()
|
|
64
|
+
}
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
//@ts-nocheck
|
|
2
2
|
import { test, expect, vi, beforeEach } from 'vitest'
|
|
3
3
|
import * as THREE from 'three'
|
|
4
|
-
import { Vec3 } from 'vec3'
|
|
5
|
-
|
|
6
|
-
vi.mock('../entity/EntityMesh', () => ({
|
|
7
|
-
getMesh: vi.fn(),
|
|
8
|
-
}))
|
|
9
4
|
|
|
10
5
|
const renderSignMock = vi.fn()
|
|
11
6
|
vi.mock('../../sign-renderer', () => ({
|
|
@@ -16,25 +11,11 @@ vi.mock('prismarine-chat', () => ({
|
|
|
16
11
|
default: () => () => ({}),
|
|
17
12
|
}))
|
|
18
13
|
|
|
19
|
-
import {
|
|
14
|
+
import { getSignTexture, releaseSignTexture, disposeAllSignTextures } from '../signTextureCache'
|
|
20
15
|
import type { WorldRendererThree } from '../worldRendererThree'
|
|
21
16
|
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
const material = new THREE.MeshBasicMaterial()
|
|
25
|
-
const worldRenderer = {
|
|
26
|
-
version: '1.20',
|
|
27
|
-
shaderCubeBlocksEnabled: () => false,
|
|
28
|
-
getModule: () => undefined,
|
|
29
|
-
sceneOrigin: {
|
|
30
|
-
track: () => {},
|
|
31
|
-
removeAndUntrack: () => {},
|
|
32
|
-
removeAndUntrackAll: () => {},
|
|
33
|
-
},
|
|
34
|
-
blockEntities: {},
|
|
35
|
-
worldRendererConfig: {},
|
|
36
|
-
} as unknown as WorldRendererThree
|
|
37
|
-
return new ChunkMeshManager(worldRenderer, scene, material, 256, 1)
|
|
17
|
+
function createWorldRenderer (): WorldRendererThree {
|
|
18
|
+
return { version: '1.20' } as WorldRendererThree
|
|
38
19
|
}
|
|
39
20
|
|
|
40
21
|
function stubCanvas () {
|
|
@@ -44,40 +25,80 @@ function stubCanvas () {
|
|
|
44
25
|
beforeEach(() => {
|
|
45
26
|
renderSignMock.mockReset()
|
|
46
27
|
renderSignMock.mockImplementation(() => stubCanvas())
|
|
28
|
+
disposeAllSignTextures()
|
|
47
29
|
})
|
|
48
30
|
|
|
49
|
-
test('getSignTexture: same
|
|
50
|
-
const
|
|
51
|
-
const signHeadsRenderer = (manager as unknown as { signHeadsRenderer: { getSignTexture: Function } }).signHeadsRenderer
|
|
52
|
-
const pos = new Vec3(10, 64, 10)
|
|
31
|
+
test('getSignTexture: same content at different positions shares one texture', () => {
|
|
32
|
+
const wr = createWorldRenderer()
|
|
53
33
|
const blockEntity = { Text1: '{"text":"Hello"}' }
|
|
54
34
|
|
|
55
|
-
const tex1 =
|
|
56
|
-
const tex2 =
|
|
35
|
+
const tex1 = getSignTexture(wr, blockEntity, false)
|
|
36
|
+
const tex2 = getSignTexture(wr, { ...blockEntity }, false)
|
|
57
37
|
|
|
58
38
|
expect(tex1).toBeDefined()
|
|
59
39
|
expect(tex2).toBe(tex1)
|
|
60
40
|
expect(renderSignMock).toHaveBeenCalledTimes(1)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('getSignTexture: different text yields different textures', () => {
|
|
44
|
+
const wr = createWorldRenderer()
|
|
61
45
|
|
|
62
|
-
|
|
46
|
+
const tex1 = getSignTexture(wr, { Text1: '{"text":"Hello"}' }, false)!
|
|
47
|
+
const tex2 = getSignTexture(wr, { Text1: '{"text":"World"}' }, false)!
|
|
48
|
+
|
|
49
|
+
expect(tex2).not.toBe(tex1)
|
|
50
|
+
expect(renderSignMock).toHaveBeenCalledTimes(2)
|
|
63
51
|
})
|
|
64
52
|
|
|
65
|
-
test('
|
|
66
|
-
const
|
|
67
|
-
const signHeadsRenderer = (manager as unknown as { signHeadsRenderer: { getSignTexture: Function } }).signHeadsRenderer
|
|
68
|
-
const pos = new Vec3(10, 64, 10)
|
|
53
|
+
test('releaseSignTexture: partial release keeps texture in cache', () => {
|
|
54
|
+
const wr = createWorldRenderer()
|
|
69
55
|
const blockEntity = { Text1: '{"text":"Hello"}' }
|
|
70
56
|
|
|
71
|
-
const tex1 =
|
|
57
|
+
const tex1 = getSignTexture(wr, blockEntity, false)!
|
|
58
|
+
const tex2 = getSignTexture(wr, blockEntity, false)!
|
|
59
|
+
expect(tex1).toBe(tex2)
|
|
60
|
+
|
|
72
61
|
const disposeSpy = vi.spyOn(tex1, 'dispose')
|
|
62
|
+
releaseSignTexture(tex1)
|
|
63
|
+
|
|
64
|
+
expect(disposeSpy).not.toHaveBeenCalled()
|
|
65
|
+
expect(getSignTexture(wr, blockEntity, false)).toBe(tex1)
|
|
66
|
+
expect(renderSignMock).toHaveBeenCalledTimes(1)
|
|
67
|
+
})
|
|
73
68
|
|
|
74
|
-
|
|
75
|
-
const
|
|
69
|
+
test('releaseSignTexture: dispose at zero refcount removes cache entry', () => {
|
|
70
|
+
const wr = createWorldRenderer()
|
|
71
|
+
const blockEntity = { Text1: '{"text":"Hello"}' }
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
const tex = getSignTexture(wr, blockEntity, false)!
|
|
74
|
+
const disposeSpy = vi.spyOn(tex, 'dispose')
|
|
75
|
+
|
|
76
|
+
releaseSignTexture(tex)
|
|
79
77
|
expect(disposeSpy).toHaveBeenCalledTimes(1)
|
|
78
|
+
|
|
79
|
+
const tex2 = getSignTexture(wr, blockEntity, false)!
|
|
80
|
+
expect(tex2).not.toBe(tex)
|
|
80
81
|
expect(renderSignMock).toHaveBeenCalledTimes(2)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('getSignTexture: key ignores irrelevant NBT fields', () => {
|
|
85
|
+
const wr = createWorldRenderer()
|
|
86
|
+
const base = { Text1: '{"text":"Hello"}', Color: 'black' }
|
|
81
87
|
|
|
82
|
-
|
|
88
|
+
const tex1 = getSignTexture(wr, base, false)!
|
|
89
|
+
const tex2 = getSignTexture(wr, { ...base, GlowingText: 1, is_waxed: 1 }, false)!
|
|
90
|
+
|
|
91
|
+
expect(tex2).toBe(tex1)
|
|
92
|
+
expect(renderSignMock).toHaveBeenCalledTimes(1)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('getSignTexture: isHanging is part of cache key', () => {
|
|
96
|
+
const wr = createWorldRenderer()
|
|
97
|
+
const blockEntity = { Text1: '{"text":"Hello"}' }
|
|
98
|
+
|
|
99
|
+
const standing = getSignTexture(wr, blockEntity, false)!
|
|
100
|
+
const hanging = getSignTexture(wr, blockEntity, true)!
|
|
101
|
+
|
|
102
|
+
expect(hanging).not.toBe(standing)
|
|
103
|
+
expect(renderSignMock).toHaveBeenCalledTimes(2)
|
|
83
104
|
})
|
|
@@ -2,12 +2,10 @@
|
|
|
2
2
|
import * as THREE from 'three'
|
|
3
3
|
import { Vec3 } from 'vec3'
|
|
4
4
|
import nbt from 'prismarine-nbt'
|
|
5
|
-
import PrismarineChatLoader from 'prismarine-chat'
|
|
6
5
|
import * as tweenJs from '@tweenjs/tween.js'
|
|
7
6
|
import { Biome } from 'minecraft-data'
|
|
8
|
-
import { renderSign } from '../sign-renderer'
|
|
9
7
|
import { DisplayWorldOptions, GraphicsInitOptions } from '../graphicsBackend/types'
|
|
10
|
-
import {
|
|
8
|
+
import { sectionPos } from '../lib/simpleUtils'
|
|
11
9
|
import { WorldRendererCommon } from '../lib/worldrendererCommon'
|
|
12
10
|
import { calculateSkyLightSimple } from '../lib/skyLight'
|
|
13
11
|
import { addNewStat, MC_RENDERER_DEBUG_OVERLAY_CLASS } from '../lib/ui/newStats'
|
|
@@ -55,8 +53,6 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
55
53
|
get sectionObjects() {
|
|
56
54
|
return this.chunkMeshManager.sectionObjects
|
|
57
55
|
}
|
|
58
|
-
chunkTextures = new Map<string, { [pos: string]: THREE.Texture }>()
|
|
59
|
-
signsCache = new Map<string, any>()
|
|
60
56
|
cameraSectionPos: Vec3 = new Vec3(0, 0, 0)
|
|
61
57
|
holdingBlock: IHoldingBlock
|
|
62
58
|
holdingBlockLeft: IHoldingBlock
|
|
@@ -929,28 +925,6 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
929
925
|
}
|
|
930
926
|
|
|
931
927
|
|
|
932
|
-
getSignTexture(position: Vec3, blockEntity, isHanging, backSide = false) {
|
|
933
|
-
const chunk = chunkPos(position)
|
|
934
|
-
let textures = this.chunkTextures.get(`${chunk[0]},${chunk[1]}`)
|
|
935
|
-
if (!textures) {
|
|
936
|
-
textures = {}
|
|
937
|
-
this.chunkTextures.set(`${chunk[0]},${chunk[1]}`, textures)
|
|
938
|
-
}
|
|
939
|
-
const texturekey = `${position.x},${position.y},${position.z}`
|
|
940
|
-
// todo investigate bug and remove this so don't need to clean in section dirty
|
|
941
|
-
if (textures[texturekey]) return textures[texturekey]
|
|
942
|
-
|
|
943
|
-
const PrismarineChat = PrismarineChatLoader(this.version)
|
|
944
|
-
const canvas = renderSign(blockEntity, isHanging, PrismarineChat)
|
|
945
|
-
if (!canvas) return
|
|
946
|
-
const tex = new THREE.Texture(canvas)
|
|
947
|
-
tex.magFilter = THREE.NearestFilter
|
|
948
|
-
tex.minFilter = THREE.NearestFilter
|
|
949
|
-
tex.needsUpdate = true
|
|
950
|
-
textures[texturekey] = tex
|
|
951
|
-
return tex
|
|
952
|
-
}
|
|
953
|
-
|
|
954
928
|
getCameraPosition(target?: THREE.Vector3): THREE.Vector3 {
|
|
955
929
|
return (target ?? this._tmpCameraPos).set(this.cameraWorldPos.x, this.cameraWorldPos.y, this.cameraWorldPos.z)
|
|
956
930
|
}
|
|
@@ -1443,49 +1417,6 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1443
1417
|
}
|
|
1444
1418
|
}
|
|
1445
1419
|
|
|
1446
|
-
renderSign(position: Vec3, rotation: number, isWall: boolean, isHanging: boolean, blockEntity) {
|
|
1447
|
-
const tex = this.getSignTexture(position, blockEntity, isHanging)
|
|
1448
|
-
|
|
1449
|
-
if (!tex) return
|
|
1450
|
-
|
|
1451
|
-
// todo implement
|
|
1452
|
-
// const key = JSON.stringify({ position, rotation, isWall })
|
|
1453
|
-
// if (this.signsCache.has(key)) {
|
|
1454
|
-
// console.log('cached', key)
|
|
1455
|
-
// } else {
|
|
1456
|
-
// this.signsCache.set(key, tex)
|
|
1457
|
-
// }
|
|
1458
|
-
|
|
1459
|
-
const mesh = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), new THREE.MeshBasicMaterial({ map: tex, transparent: true }))
|
|
1460
|
-
mesh.renderOrder = 999
|
|
1461
|
-
|
|
1462
|
-
const lineHeight = 7 / 16
|
|
1463
|
-
const scaleFactor = isHanging ? 1.3 : 1
|
|
1464
|
-
mesh.scale.set(1 * scaleFactor, lineHeight * scaleFactor, 1 * scaleFactor)
|
|
1465
|
-
|
|
1466
|
-
const thickness = (isHanging ? 2 : 1.5) / 16
|
|
1467
|
-
const wallSpacing = 0.25 / 16
|
|
1468
|
-
if (isWall && !isHanging) {
|
|
1469
|
-
mesh.position.set(0, 0, -0.5 + thickness + wallSpacing + 0.0001)
|
|
1470
|
-
} else {
|
|
1471
|
-
mesh.position.set(0, 0, thickness / 2 + 0.0001)
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
const group = new THREE.Group()
|
|
1475
|
-
group.rotation.set(
|
|
1476
|
-
0,
|
|
1477
|
-
-THREE.MathUtils.degToRad(rotation * (isWall ? 90 : 45 / 2)),
|
|
1478
|
-
0
|
|
1479
|
-
)
|
|
1480
|
-
group.add(mesh)
|
|
1481
|
-
const height = (isHanging ? 10 : 8) / 16
|
|
1482
|
-
const heightOffset = (isHanging ? 0 : isWall ? 4.333 : 9.333) / 16
|
|
1483
|
-
const textPosition = height / 2 + heightOffset
|
|
1484
|
-
this.sceneOrigin.track(group)
|
|
1485
|
-
group.position.set(position.x + 0.5, position.y + textPosition, position.z + 0.5)
|
|
1486
|
-
return group
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
1420
|
lightUpdate(chunkX: number, chunkZ: number) {
|
|
1490
1421
|
// set all sections in the chunk dirty
|
|
1491
1422
|
for (let y = this.worldSizeParams.minY; y < this.worldSizeParams.worldHeight; y += 16) {
|
|
@@ -1535,19 +1466,6 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1535
1466
|
}))
|
|
1536
1467
|
}
|
|
1537
1468
|
|
|
1538
|
-
cleanChunkTextures(x, z) {
|
|
1539
|
-
const textures = this.chunkTextures.get(`${Math.floor(x / 16)},${Math.floor(z / 16)}`) ?? {}
|
|
1540
|
-
for (const key of Object.keys(textures)) {
|
|
1541
|
-
textures[key].dispose()
|
|
1542
|
-
delete textures[key]
|
|
1543
|
-
}
|
|
1544
|
-
// Sign / head textures moved to ChunkMeshManager.signHeadsRenderer in PR
|
|
1545
|
-
// #16; without invalidating that cache here, sign edits (and any other
|
|
1546
|
-
// block-entity NBT change picked up via setSectionDirty) would re-render
|
|
1547
|
-
// with the stale cached canvas until a full world reset.
|
|
1548
|
-
this.chunkMeshManager.cleanSignChunkTextures(x, z)
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
1469
|
readdChunks() {
|
|
1552
1470
|
for (const key of Object.keys(this.sectionObjects)) {
|
|
1553
1471
|
this.scene.remove(this.sectionObjects[key])
|
|
@@ -1569,7 +1487,6 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1569
1487
|
removeColumn(x, z) {
|
|
1570
1488
|
super.removeColumn(x, z)
|
|
1571
1489
|
|
|
1572
|
-
this.cleanChunkTextures(x, z)
|
|
1573
1490
|
this.clearPendingSectionUpdatesForChunk(x, z)
|
|
1574
1491
|
const sectionHeight = this.getSectionHeight()
|
|
1575
1492
|
const worldMinY = this.worldMinYRender
|