feat: add Ears mod support to skin selector (#6775)

* feat: add Ears mod support to skin selector

* fix: lint

* feat: ears mod notice + toggle + fix uv issues

* fix: lint
This commit is contained in:
Calum H.
2026-07-20 18:01:04 +00:00
committed by GitHub
parent ee2ad5f05b
commit 8b6f0ec695
12 changed files with 2119 additions and 45 deletions
@@ -21,7 +21,7 @@
class="absolute left-0 right-0 z-10 flex items-center justify-center pointer-events-none"
:style="subtitlePositionStyle"
>
<div ref="subtitleElement" class="pointer-events-auto" @click="ignoreControlClick">
<div ref="subtitleElement" class="w-full pointer-events-auto" @click="ignoreControlClick">
<slot name="subtitle" />
</div>
</div>
@@ -133,6 +133,7 @@ import { createRadialSpotlightShader, syncDamageFlashShader } from './skin-previ
const props = withDefaults(
defineProps<{
textureSrc: string
earsTextureSrc?: string
capeSrc?: string
variant?: 'SLIM' | 'CLASSIC' | 'UNKNOWN'
nametag?: string
@@ -147,9 +148,11 @@ const props = withDefaults(
fov?: number
initialRotation?: number
animationConfig?: SkinPreviewAnimationConfig
earsEnabled?: boolean
}>(),
{
variant: 'CLASSIC',
earsTextureSrc: undefined,
capeSrc: undefined,
initialRotation: 15.75,
nametag: undefined,
@@ -157,6 +160,7 @@ const props = withDefaults(
lockFit: true,
framing: 'page',
fitZoom: 1,
earsEnabled: true,
animationConfig: () => ({
baseAnimation: 'idle',
randomAnimations: ['idle_sub_1', 'idle_sub_2', 'idle_sub_3'],
@@ -166,6 +170,10 @@ const props = withDefaults(
},
)
const emit = defineEmits<{
earsFeaturesDetected: [detected: boolean]
}>()
const skinPreviewContainer = useTemplateRef<HTMLElement>('skinPreviewContainer')
const subtitleElement = useTemplateRef<HTMLElement>('subtitleElement')
const slots = useSlots()
@@ -259,13 +267,16 @@ const {
},
})
const { isModelLoaded, isTextureLoaded, modelCenter, modelSize, scene } = useSkinPreviewScene({
selectedModelSrc,
textureSrc: toRef(props, 'textureSrc'),
capeSrc: toRef(props, 'capeSrc'),
initializeAnimations,
cleanupAnimationState,
})
const { hasEarsFeatures, isModelLoaded, isTextureLoaded, modelCenter, modelSize, scene } =
useSkinPreviewScene({
selectedModelSrc,
textureSrc: toRef(props, 'textureSrc'),
earsTextureSrc: toRef(props, 'earsTextureSrc'),
capeSrc: toRef(props, 'capeSrc'),
earsEnabled: toRef(props, 'earsEnabled'),
initializeAnimations,
cleanupAnimationState,
})
function syncDamageFlashShaderMaterials() {
syncDamageFlashShader(scene.value, damageFlashIntensity.value)
@@ -310,6 +321,13 @@ const { isPreviewVisible, showLoading } = useSkinPreviewLoading(isReady)
onMounted(observeSubtitleElement)
watch(hasSubtitle, () => nextTick(observeSubtitleElement), { flush: 'post' })
watch(
hasEarsFeatures,
(detected) => {
emit('earsFeaturesDetected', detected)
},
{ immediate: true },
)
watch(scene, syncDamageFlashShaderMaterials, { immediate: true })
watch(damageFlashIntensity, syncDamageFlashShaderMaterials)