Skip to content

Commit c9d9c86

Browse files
authored
deduplicate skin sprites (#38)
- Improved deduplication logic by using content hashing (`packRaw`) instead of texture paths. - Sprites with identical content and padding now share the same texture atlas region, optimizing texture memory.
1 parent 634ebe9 commit c9d9c86

1 file changed

Lines changed: 55 additions & 10 deletions

File tree

src/core/skin.ts

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,45 @@ function packSkin(
139139
tasks.push({
140140
description: `Packing skin "${name}" texture...`,
141141
async execute() {
142+
const uniqueSprites = new Map<
143+
string,
144+
{
145+
name: string
146+
texture: string
147+
padding: {
148+
left: boolean
149+
right: boolean
150+
top: boolean
151+
bottom: boolean
152+
}
153+
}
154+
>()
155+
const spriteMapping = new Map<string, string>()
156+
const textureHashCache = new Map<string, string>()
157+
for (const s of skin.data.sprites) {
158+
let hash = textureHashCache.get(s.texture)
159+
if (!hash) {
160+
const result = await packRaw(s.texture)
161+
hash = result.hash
162+
textureHashCache.set(s.texture, hash)
163+
}
164+
const key = `${hash}:${Number(s.padding.left)}:${Number(s.padding.right)}:${Number(s.padding.top)}:${Number(s.padding.bottom)}`
165+
let unique = uniqueSprites.get(key)
166+
if (!unique) {
167+
unique = {
168+
name: s.name,
169+
texture: s.texture,
170+
padding: s.padding,
171+
}
172+
uniqueSprites.set(key, unique)
173+
}
174+
spriteMapping.set(s.name, unique.name)
175+
}
176+
177+
const uniqueSpriteList = Array.from(uniqueSprites.values())
178+
142179
const { size, layouts } = await tryCalculateLayout(
143-
skin.data.sprites.map((s) => ({
180+
uniqueSpriteList.map((s) => ({
144181
name: s.name,
145182
texture: s.texture,
146183
padding: s.padding,
@@ -158,18 +195,26 @@ function packSkin(
158195

159196
ctx.clearRect(0, 0, size, size)
160197

161-
for (const { name, x, y, w, h } of layouts) {
162-
const sprite = skin.data.sprites.find((sprite) => sprite.name === name)
163-
if (!sprite) throw new Error('Unexpected missing sprite')
198+
for (const s of skin.data.sprites) {
199+
const uniqueName = spriteMapping.get(s.name)
200+
if (!uniqueName) throw new Error('Unexpected missing sprite mapping')
201+
202+
const layout = layouts.find((l) => l.name === uniqueName)
203+
if (!layout) throw new Error('Unexpected missing sprite layout')
164204

165205
skinData.sprites.push({
166-
name,
167-
x: x + (sprite.padding.left ? 1 : 0),
168-
y: y + (sprite.padding.top ? 1 : 0),
169-
w,
170-
h,
171-
transform: sprite.transform,
206+
name: s.name,
207+
x: layout.x + (s.padding.left ? 1 : 0),
208+
y: layout.y + (s.padding.top ? 1 : 0),
209+
w: layout.w,
210+
h: layout.h,
211+
transform: s.transform,
172212
})
213+
}
214+
215+
for (const { name, x, y, w, h } of layouts) {
216+
const sprite = skin.data.sprites.find((s) => s.name === name)
217+
if (!sprite) throw new Error('Unexpected missing sprite')
173218

174219
await bakeSprite(sprite, x, y, w, h, ctx)
175220
}

0 commit comments

Comments
 (0)