fix: shading + lighting improvements

This commit is contained in:
Calum H.
2025-06-16 15:34:34 +01:00
parent 5e69cbc329
commit b851cd8d7c
5 changed files with 78 additions and 23 deletions

View File

@@ -9,9 +9,7 @@
<Avatar
size="36px"
:src="
selectedAccount
? `https://mc-heads.net/avatar/${selectedAccount.profile.id}/128`
: 'https://launcher-files.modrinth.com/assets/steve_head.png'
selectedAccount ? avatarUrl : 'https://launcher-files.modrinth.com/assets/steve_head.png'
"
/>
<div class="flex flex-col w-full">
@@ -58,7 +56,14 @@
<div v-if="displayAccounts.length > 0" class="account-group">
<div v-for="account in displayAccounts" :key="account.profile.id" class="account-row">
<Button class="option account" @click="setAccount(account)">
<Avatar :src="`https://mc-heads.net/avatar/${account.profile.id}/128`" class="icon" />
<Avatar
:src="
account.profile.id == selectedAccount.profile.id
? avatarUrl
: `https://mc-heads.net/avatar/${account.profile.id}/128`
"
class="icon"
/>
<p>{{ account.profile.name }}</p>
</Button>
<Button v-tooltip="'Log out'" icon-only @click="logout(account.profile.id)">
@@ -89,6 +94,7 @@ import { handleError } from '@/store/state.js'
import { trackEvent } from '@/helpers/analytics'
import { process_listener } from '@/helpers/events'
import { handleSevereError } from '@/store/error.js'
import { get_available_skins } from '@/helpers/skins'
defineProps({
mode: {
@@ -103,10 +109,18 @@ const emit = defineEmits(['change'])
const accounts = ref({})
const loginDisabled = ref(false)
const defaultUser = ref()
const equippedSkin = ref(null)
async function refreshValues() {
defaultUser.value = await get_default_user().catch(handleError)
accounts.value = await users().catch(handleError)
try {
const skins = await get_available_skins()
equippedSkin.value = skins.find((skin) => skin.is_equipped)
} catch {
equippedSkin.value = null
}
}
function setLoginDisabled(value) {
@@ -124,6 +138,13 @@ const displayAccounts = computed(() =>
accounts.value.filter((account) => defaultUser.value !== account.profile.id),
)
const avatarUrl = computed(() => {
if (equippedSkin.value?.texture_key) {
return `https://mc-heads.net/avatar/${equippedSkin.value.texture_key}/128`
}
return `https://mc-heads.net/avatar/${selectedAccount.value.profile.id}/128`
})
const selectedAccount = computed(() =>
accounts.value.find((account) => account.profile.id === defaultUser.value),
)

View File

@@ -30,6 +30,7 @@ class BatchSkinRenderer {
this.renderer.outputColorSpace = THREE.SRGBColorSpace
this.renderer.toneMapping = THREE.NoToneMapping
this.renderer.toneMappingExposure = 10.0
this.renderer.setClearColor(0x000000, 0)
this.renderer.setSize(width, height)
@@ -37,7 +38,11 @@ class BatchSkinRenderer {
this.camera = new THREE.PerspectiveCamera(20, width / height, 0.4, 1000)
const ambientLight = new THREE.AmbientLight(0xffffff, 2)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2)
directionalLight.castShadow = false
directionalLight.position.set(2, 4, 3)
this.scene.add(ambientLight)
this.scene.add(directionalLight)
}
public async renderSkin(
@@ -59,8 +64,8 @@ class BatchSkinRenderer {
throw new Error("Failed to find 'Head' object in model.")
}
const frontCameraPos: [number, number, number] = [2, 1, 6.0]
const backCameraPos: [number, number, number] = [2, 1, -2.5]
const frontCameraPos: [number, number, number] = [-1.3, 1, 6.3]
const backCameraPos: [number, number, number] = [-1.3, 1, -2.5]
const forwards = await this.renderView(frontCameraPos, lookAtTarget)
const backwards = await this.renderView(backCameraPos, lookAtTarget)

View File

@@ -148,6 +148,9 @@ async function changeSkin(newSkin: Skin) {
try {
await equip_skin(newSkin)
if (accountsCard.value) {
await accountsCard.value.refreshValues()
}
} catch (error) {
selectedSkin.value = previousSkin
skins.value = previousSkinsList

View File

@@ -17,7 +17,7 @@
class="absolute top-[18%] left-1/2 transform -translate-x-1/2 px-3 py-1 rounded-md pointer-events-none z-10 font-minecraft nametag-bg transition-all duration-200"
:style="{ fontSize: nametagFontSize }"
>
{{ nametag }}
{{ nametagText }}
</div>
<TresCanvas
@@ -28,6 +28,7 @@
:renderer-options="{
outputColorSpace: THREE.SRGBColorSpace,
toneMapping: THREE.NoToneMapping,
toneMappingExposure: 10.0,
}"
@pointerdown="onPointerDown"
@pointermove="onPointerMove"
@@ -47,12 +48,12 @@
<TresMesh
:position="[0, -0.095 * scale, 2]"
:rotation="[-Math.PI / 2, 0, 0]"
:scale="[0.4 * scale, 0.4 * scale, 0.4 * scale]"
:scale="[0.5 * 0.75 * scale, 0.5 * 0.75 * scale, 0.5 * 0.75 * scale]"
>
<TresCircleGeometry :args="[1, 32]" />
<TresCircleGeometry :args="[1, 128]" />
<TresMeshBasicMaterial
color="#000000"
:opacity="0.3"
:opacity="0.2"
transparent
:depth-write="false"
/>
@@ -63,7 +64,7 @@
:rotation="[-Math.PI / 2, 0, 0]"
:scale="[0.75 * scale, 0.75 * scale, 0.75 * scale]"
>
<TresPlaneGeometry :args="[2, 2]" />
<TresCircleGeometry :args="[1, 128]" />
<TresMeshBasicMaterial
:map="radialTexture"
transparent
@@ -82,6 +83,7 @@
/>
<TresAmbientLight :intensity="2" />
<TresDirectionalLight :position="[2, 4, 3]" :intensity="1.2" :cast-shadow="false" />
</TresCanvas>
<div v-if="!isReady" class="w-full h-full flex items-center justify-center">
@@ -161,9 +163,9 @@ const nametagText = computed(() => props.nametag)
const { fontSize: nametagFontSize } = useDynamicFontSize({
containerElement: skinPreviewContainer,
text: nametagText,
baseFontSize: 2,
baseFontSize: 1.8,
minFontSize: 1.25,
maxFontSize: 4,
maxFontSize: 2,
padding: 24,
fontFamily: 'inherit',
})
@@ -190,6 +192,7 @@ const actions = ref<Record<string, THREE.AnimationAction>>({})
const clock = new THREE.Clock()
const currentAnimation = ref<string>('')
const randomAnimationTimer = ref<number | null>(null)
const lastRandomAnimation = ref<string>('')
const { baseAnimation, randomAnimations } = toRefs(props.animationConfig)
@@ -282,18 +285,36 @@ function playAnimation(name: string) {
function setupRandomAnimationLoop() {
const interval = props.animationConfig.randomAnimationInterval || 10000
randomAnimationTimer.value = window.setInterval(() => {
if (randomAnimations.value.length > 0) {
if (currentAnimation.value === baseAnimation.value) {
const randomIndex = Math.floor(Math.random() * randomAnimations.value.length)
const randomAnimationName = randomAnimations.value[randomIndex]
function scheduleNextAnimation() {
if (randomAnimationTimer.value) {
clearTimeout(randomAnimationTimer.value)
}
randomAnimationTimer.value = window.setTimeout(() => {
if (randomAnimations.value.length > 0 && currentAnimation.value === baseAnimation.value) {
const availableAnimations = randomAnimations.value.filter(
(anim) => anim !== lastRandomAnimation.value,
)
// If all animations have been used, reset and use the full list
const animationsToChooseFrom =
availableAnimations.length > 0 ? availableAnimations : randomAnimations.value
const randomIndex = Math.floor(Math.random() * animationsToChooseFrom.length)
const randomAnimationName = animationsToChooseFrom[randomIndex]
if (actions.value[randomAnimationName]) {
lastRandomAnimation.value = randomAnimationName
playRandomAnimation(randomAnimationName)
}
} else {
// If not in base animation, wait and try again
scheduleNextAnimation()
}
}
}, interval)
}, interval)
}
scheduleNextAnimation()
}
function playRandomAnimation(name: string) {
@@ -334,6 +355,9 @@ function playRandomAnimation(name: string) {
baseAction.fadeIn(transitionDuration)
baseAction.play()
currentAnimation.value = baseAnimation.value
// Schedule the next random animation after returning to base
setupRandomAnimationLoop()
}
}
}
@@ -528,7 +552,7 @@ function createRadialTexture(size: number): THREE.CanvasTexture {
canvas.width = canvas.height = size
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
const grad = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2)
grad.addColorStop(0, 'rgba(119,119,119,0.15)')
grad.addColorStop(0, 'rgba(119,119,119,0.1)')
grad.addColorStop(0.9, 'rgba(255,255,255,0)')
ctx.fillStyle = grad
ctx.fillRect(0, 0, size, size)
@@ -578,7 +602,7 @@ watch(
() => props.animationConfig,
(newConfig) => {
if (randomAnimationTimer.value) {
clearInterval(randomAnimationTimer.value)
clearTimeout(randomAnimationTimer.value)
randomAnimationTimer.value = null
}
@@ -612,7 +636,7 @@ onBeforeMount(async () => {
onUnmounted(() => {
if (randomAnimationTimer.value) {
clearInterval(randomAnimationTimer.value)
clearTimeout(randomAnimationTimer.value)
}
if (mixer.value) {

View File

@@ -71,6 +71,7 @@ export function applyTexture(model: THREE.Object3D, texture: THREE.Texture): voi
mat.metalness = 0
mat.color.set(0xffffff)
mat.toneMapped = false
mat.flatShading = true
mat.roughness = 1
mat.needsUpdate = true
mat.depthTest = true
@@ -100,6 +101,7 @@ export function applyCapeTexture(
mat.metalness = 0
mat.color.set(0xffffff)
mat.toneMapped = false
mat.flatShading = true
mat.roughness = 1
mat.needsUpdate = true
mat.depthTest = true