docs: Add The Trinity official Minecraft skins
- Frost Wizard skin for Frostystyle (Michael) - cyan/ice theme - Fire Emissary skin for Gingerfury (Meg) - orange/fire theme - Arcane Catalyst skin for unicorn20089 (Holly) - purple/arcane theme - All skins use light skin tones with element-colored glowing eyes - Detailed armor/robe designs with overlay layers for depth - Includes 3D skin viewer HTML tool for previewing before upload - Comprehensive README with usage instructions and design philosophy All three skins represent The Trinity's Fire/Frost/Arcane elements. Signed-off-by: Claude <claude@firefrostgaming.com>
This commit is contained in:
154
docs/branding/trinity-skins/README.md
Normal file
154
docs/branding/trinity-skins/README.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# The Trinity - Minecraft Skins
|
||||
|
||||
**Official Minecraft character skins for The Trinity founders.**
|
||||
|
||||
Created: March 27, 2026
|
||||
Format: 64x64 PNG with overlay layers
|
||||
Compatible: Minecraft Java Edition (all versions with skin support)
|
||||
|
||||
---
|
||||
|
||||
## The Three Skins
|
||||
|
||||
### ❄️ Frost Wizard - Frostystyle (Michael)
|
||||
**File:** `frost-wizard-frostystyle.png`
|
||||
**Element:** Frost/Ice
|
||||
**Primary Color:** #00E5FF (Electric Cyan - Frost Primary)
|
||||
**Model:** Steve (classic/wider arms)
|
||||
**Role:** The Wizard - Technical lead, infrastructure, Path of Frost leader
|
||||
|
||||
**Design Features:**
|
||||
- Black hair
|
||||
- Cyan/frost armor with ice crystal patterns
|
||||
- Detailed snowflake accents throughout
|
||||
- Frost crown overlay with icicle pattern
|
||||
- Cyan glowing eyes
|
||||
- Gray boots with frost accents
|
||||
- Light skin tone
|
||||
|
||||
---
|
||||
|
||||
### 🔥 Fire Emissary - Gingerfury (Meg)
|
||||
**File:** `fire-emissary-gingerfury.png`
|
||||
**Element:** Fire/Passion
|
||||
**Primary Color:** #FF3D00 (Deep Orange/Red - Fire Primary)
|
||||
**Model:** Alex (slim arms)
|
||||
**Role:** The Emissary - Community manager, social lead, Path of Fire leader
|
||||
|
||||
**Design Features:**
|
||||
- Ginger/red hair (warm red-orange tones)
|
||||
- Fire gradient armor (yellow→orange→red)
|
||||
- Flame crown with upward flames
|
||||
- Fire symbols and flame patterns throughout
|
||||
- Warm glowing orange eyes
|
||||
- Friendly smile
|
||||
- Golden amber boots (#FFD600 - Fire Accent)
|
||||
- Light skin tone
|
||||
|
||||
---
|
||||
|
||||
### ⚡ Arcane Catalyst - unicorn20089 (Holly)
|
||||
**File:** `arcane-catalyst-unicorn20089.png`
|
||||
**Element:** Arcane/Magic
|
||||
**Primary Color:** #A855F7 (Purple - Official Arcane color)
|
||||
**Model:** Alex (slim arms)
|
||||
**Role:** The Catalyst - Lead Builder, creative authority, bridge between Fire and Frost
|
||||
|
||||
**Design Features:**
|
||||
- Purple/violet hair (deep purple tones)
|
||||
- Arcane gradient armor (light→bright→dark purple)
|
||||
- Crystal crown with mystical energy
|
||||
- Arcane rune and symbol patterns throughout
|
||||
- Purple glowing eyes
|
||||
- Central arcane crystal on chest
|
||||
- Light purplish-gray boots
|
||||
- Light skin tone
|
||||
|
||||
---
|
||||
|
||||
## How to Use These Skins
|
||||
|
||||
### Option 1: Upload to Minecraft.net
|
||||
1. Go to: https://www.minecraft.net/en-us/msaprofile/mygames/editskin
|
||||
2. Click "Choose File"
|
||||
3. Select the skin PNG file
|
||||
4. Choose model (Steve or Alex as noted above)
|
||||
5. Click "Upload"
|
||||
|
||||
### Option 2: Use in Multiplayer Servers
|
||||
Once uploaded to your Minecraft.net profile, the skin will automatically appear on all servers.
|
||||
|
||||
### Option 3: Preview Before Uploading
|
||||
Use the included `minecraft_skin_viewer.html` tool (in this directory or docs/tools/) to preview skins in 3D before uploading.
|
||||
|
||||
---
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
Each skin represents one element of The Trinity:
|
||||
|
||||
- **Frost (Cyan)** - Technical precision, systems, infrastructure
|
||||
- **Fire (Orange/Red)** - Passion, community, warmth
|
||||
- **Arcane (Purple)** - Balance, creativity, catalytic force
|
||||
|
||||
All three skins:
|
||||
- Use light skin tones (requested preference)
|
||||
- Feature glowing eyes in their element color
|
||||
- Include detailed overlay layers for depth
|
||||
- Have element-specific armor/robe designs
|
||||
- Are built on proper Minecraft skin template (64x64)
|
||||
|
||||
---
|
||||
|
||||
## File Specifications
|
||||
|
||||
- **Resolution:** 64x64 pixels
|
||||
- **Format:** PNG with transparency
|
||||
- **Color Mode:** RGBA
|
||||
- **Layers:** Base layer + Overlay layer (second skin layer enabled)
|
||||
- **Compatible:** Minecraft Java Edition 1.8+
|
||||
|
||||
---
|
||||
|
||||
## Customization
|
||||
|
||||
These skins can be customized using:
|
||||
- **Nova Skin Editor:** https://minecraft.novaskin.me/
|
||||
- **Miners Need Cool Shoes:** https://www.needcoolshoes.com/
|
||||
- **Skindex:** https://www.minecraftskins.com/
|
||||
|
||||
When editing, preserve:
|
||||
- The element color schemes (Frost/Fire/Arcane)
|
||||
- The glowing eye aesthetic
|
||||
- The overall Trinity branding
|
||||
|
||||
---
|
||||
|
||||
## Usage Rights
|
||||
|
||||
These skins are official Firefrost Gaming branding assets.
|
||||
|
||||
**Permitted Use:**
|
||||
- The Trinity members (Michael, Meg, Holly) for personal Minecraft profiles
|
||||
- Firefrost Gaming marketing materials
|
||||
- Community showcases and promotional content
|
||||
- Server branding and displays
|
||||
|
||||
**Not Permitted:**
|
||||
- Commercial redistribution
|
||||
- Modification and claiming as original work
|
||||
- Use by non-Trinity members claiming to be The Trinity
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Date | Changes |
|
||||
|---------|------|---------|
|
||||
| 1.0 | 2026-03-27 | Initial Trinity skins created |
|
||||
|
||||
---
|
||||
|
||||
**Fire + Frost + Arcane = The Trinity** 🔥❄️⚡
|
||||
|
||||
*"For children not yet born"* 💙
|
||||
BIN
docs/branding/trinity-skins/arcane-catalyst-unicorn20089.png
Normal file
BIN
docs/branding/trinity-skins/arcane-catalyst-unicorn20089.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
docs/branding/trinity-skins/fire-emissary-gingerfury.png
Normal file
BIN
docs/branding/trinity-skins/fire-emissary-gingerfury.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
BIN
docs/branding/trinity-skins/frost-wizard-frostystyle.png
Normal file
BIN
docs/branding/trinity-skins/frost-wizard-frostystyle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
625
docs/branding/trinity-skins/minecraft_skin_viewer.html
Normal file
625
docs/branding/trinity-skins/minecraft_skin_viewer.html
Normal file
@@ -0,0 +1,625 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Firefrost Gaming - Minecraft Skin Viewer</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
color: white;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5em;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.header p {
|
||||
font-size: 1.1em;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-input-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.file-input-wrapper input[type=file] {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
.file-input-wrapper label {
|
||||
display: inline-block;
|
||||
padding: 12px 24px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.file-input-wrapper label:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.preset-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.preset-btn {
|
||||
padding: 10px 20px;
|
||||
border: 2px solid #667eea;
|
||||
background: white;
|
||||
color: #667eea;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.preset-btn:hover {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.preset-btn.frost {
|
||||
border-color: #00E5FF;
|
||||
color: #00E5FF;
|
||||
}
|
||||
|
||||
.preset-btn.frost:hover {
|
||||
background: #00E5FF;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.preset-btn.fire {
|
||||
border-color: #FF3D00;
|
||||
color: #FF3D00;
|
||||
}
|
||||
|
||||
.preset-btn.fire:hover {
|
||||
background: #FF3D00;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.preset-btn.arcane {
|
||||
border-color: #A855F7;
|
||||
color: #A855F7;
|
||||
}
|
||||
|
||||
.preset-btn.arcane:hover {
|
||||
background: #A855F7;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#viewer-container {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
background: linear-gradient(to bottom, #87CEEB 0%, #E0F6FF 100%);
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.info strong {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.animation-controls {
|
||||
margin-top: 15px;
|
||||
padding: 15px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.animation-controls label {
|
||||
margin-right: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.animation-controls select {
|
||||
padding: 8px 12px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 1em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>🔥❄️ Firefrost Gaming 🔥❄️</h1>
|
||||
<p>Minecraft Skin Viewer - The Trinity</p>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="controls">
|
||||
<div class="file-input-wrapper">
|
||||
<input type="file" id="skinInput" accept="image/png" />
|
||||
<label for="skinInput">📁 Upload Skin PNG</label>
|
||||
</div>
|
||||
|
||||
<div class="preset-buttons">
|
||||
<button class="preset-btn frost" onclick="loadPreset('frost')">❄️ Frost Wizard</button>
|
||||
<button class="preset-btn fire" onclick="loadPreset('fire')">🔥 Fire Emissary</button>
|
||||
<button class="preset-btn arcane" onclick="loadPreset('arcane')">⚡ Arcane Catalyst</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="animation-controls">
|
||||
<label for="animationSelect">Animation:</label>
|
||||
<select id="animationSelect" onchange="changeAnimation()">
|
||||
<option value="idle">Idle (Standing)</option>
|
||||
<option value="walk">Walking</option>
|
||||
<option value="run">Running</option>
|
||||
<option value="wave">Waving</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="viewer-container"></div>
|
||||
|
||||
<div class="info">
|
||||
<strong>Controls:</strong> Click and drag to rotate • Scroll to zoom • Right-click drag to pan<br>
|
||||
<strong>Tip:</strong> Upload any 64x64 Minecraft skin PNG or try the Trinity presets!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<script>
|
||||
// Three.js scene setup
|
||||
let scene, camera, renderer, skinMesh;
|
||||
let animationTime = 0;
|
||||
let currentAnimation = 'idle';
|
||||
|
||||
// Initialize the 3D scene
|
||||
function init() {
|
||||
const container = document.getElementById('viewer-container');
|
||||
|
||||
// Scene
|
||||
scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x87CEEB);
|
||||
|
||||
// Camera
|
||||
camera = new THREE.PerspectiveCamera(
|
||||
50,
|
||||
container.clientWidth / container.clientHeight,
|
||||
0.1,
|
||||
1000
|
||||
);
|
||||
camera.position.set(0, 16, 40);
|
||||
camera.lookAt(0, 16, 0);
|
||||
|
||||
// Renderer
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize(container.clientWidth, container.clientHeight);
|
||||
container.appendChild(renderer.domElement);
|
||||
|
||||
// Lighting
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
||||
scene.add(ambientLight);
|
||||
|
||||
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
||||
directionalLight.position.set(10, 20, 10);
|
||||
scene.add(directionalLight);
|
||||
|
||||
// Ground plane
|
||||
const groundGeometry = new THREE.PlaneGeometry(100, 100);
|
||||
const groundMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0x90EE90,
|
||||
side: THREE.DoubleSide
|
||||
});
|
||||
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
||||
ground.rotation.x = Math.PI / 2;
|
||||
ground.position.y = 0;
|
||||
scene.add(ground);
|
||||
|
||||
// Mouse controls
|
||||
let isDragging = false;
|
||||
let previousMousePosition = { x: 0, y: 0 };
|
||||
let rotation = { x: 0, y: 0 };
|
||||
|
||||
renderer.domElement.addEventListener('mousedown', (e) => {
|
||||
isDragging = true;
|
||||
});
|
||||
|
||||
renderer.domElement.addEventListener('mousemove', (e) => {
|
||||
if (isDragging && skinMesh) {
|
||||
const deltaMove = {
|
||||
x: e.offsetX - previousMousePosition.x,
|
||||
y: e.offsetY - previousMousePosition.y
|
||||
};
|
||||
|
||||
rotation.y += deltaMove.x * 0.01;
|
||||
rotation.x += deltaMove.y * 0.01;
|
||||
|
||||
// Clamp vertical rotation
|
||||
rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, rotation.x));
|
||||
}
|
||||
|
||||
previousMousePosition = { x: e.offsetX, y: e.offsetY };
|
||||
});
|
||||
|
||||
renderer.domElement.addEventListener('mouseup', () => {
|
||||
isDragging = false;
|
||||
});
|
||||
|
||||
// Zoom with mouse wheel
|
||||
renderer.domElement.addEventListener('wheel', (e) => {
|
||||
e.preventDefault();
|
||||
camera.position.z += e.deltaY * 0.05;
|
||||
camera.position.z = Math.max(20, Math.min(80, camera.position.z));
|
||||
});
|
||||
|
||||
// Handle window resize
|
||||
window.addEventListener('resize', () => {
|
||||
const width = container.clientWidth;
|
||||
const height = container.clientHeight;
|
||||
camera.aspect = width / height;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(width, height);
|
||||
});
|
||||
|
||||
// Animation loop
|
||||
animate();
|
||||
}
|
||||
|
||||
// Create Minecraft character from skin texture
|
||||
function createMinecraftCharacter(skinTexture) {
|
||||
// Remove old mesh if exists
|
||||
if (skinMesh) {
|
||||
scene.remove(skinMesh);
|
||||
}
|
||||
|
||||
const group = new THREE.Group();
|
||||
|
||||
// Minecraft skin texture
|
||||
skinTexture.magFilter = THREE.NearestFilter;
|
||||
skinTexture.minFilter = THREE.NearestFilter;
|
||||
const skinMaterial = new THREE.MeshStandardMaterial({
|
||||
map: skinTexture,
|
||||
transparent: true
|
||||
});
|
||||
|
||||
// Head (8x8x8)
|
||||
const headGeo = new THREE.BoxGeometry(8, 8, 8);
|
||||
const head = new THREE.Mesh(headGeo, skinMaterial);
|
||||
head.position.y = 24;
|
||||
|
||||
// UV mapping for head
|
||||
setHeadUVs(headGeo);
|
||||
|
||||
group.add(head);
|
||||
|
||||
// Body (8x12x4)
|
||||
const bodyGeo = new THREE.BoxGeometry(8, 12, 4);
|
||||
const body = new THREE.Mesh(bodyGeo, skinMaterial);
|
||||
body.position.y = 14;
|
||||
|
||||
setBodyUVs(bodyGeo);
|
||||
|
||||
group.add(body);
|
||||
|
||||
// Arms (4x12x4 each)
|
||||
const armGeo = new THREE.BoxGeometry(4, 12, 4);
|
||||
|
||||
const rightArm = new THREE.Mesh(armGeo, skinMaterial);
|
||||
rightArm.position.set(-6, 14, 0);
|
||||
setRightArmUVs(rightArm.geometry);
|
||||
group.add(rightArm);
|
||||
|
||||
const leftArm = new THREE.Mesh(armGeo, skinMaterial);
|
||||
leftArm.position.set(6, 14, 0);
|
||||
setLeftArmUVs(leftArm.geometry);
|
||||
group.add(leftArm);
|
||||
|
||||
// Legs (4x12x4 each)
|
||||
const legGeo = new THREE.BoxGeometry(4, 12, 4);
|
||||
|
||||
const rightLeg = new THREE.Mesh(legGeo, skinMaterial);
|
||||
rightLeg.position.set(-2, 6, 0);
|
||||
setRightLegUVs(rightLeg.geometry);
|
||||
group.add(rightLeg);
|
||||
|
||||
const leftLeg = new THREE.Mesh(legGeo, skinMaterial);
|
||||
leftLeg.position.set(2, 6, 0);
|
||||
setLeftLegUVs(leftLeg.geometry);
|
||||
group.add(leftLeg);
|
||||
|
||||
// Store body parts for animation
|
||||
group.userData = {
|
||||
head: head,
|
||||
body: body,
|
||||
rightArm: rightArm,
|
||||
leftArm: leftArm,
|
||||
rightLeg: rightLeg,
|
||||
leftLeg: leftLeg
|
||||
};
|
||||
|
||||
skinMesh = group;
|
||||
scene.add(group);
|
||||
}
|
||||
|
||||
// UV mapping functions for each body part
|
||||
function setHeadUVs(geometry) {
|
||||
const uvs = geometry.attributes.uv.array;
|
||||
// Front, back, top, bottom, right, left faces
|
||||
// Simplified UV mapping for head (8,8 to 16,16 area)
|
||||
const faceUVs = [
|
||||
[8/64, 8/64, 16/64, 16/64], // front
|
||||
[24/64, 8/64, 32/64, 16/64], // back
|
||||
[8/64, 0/64, 16/64, 8/64], // top
|
||||
[16/64, 0/64, 24/64, 8/64], // bottom
|
||||
[0/64, 8/64, 8/64, 16/64], // right
|
||||
[16/64, 8/64, 24/64, 16/64] // left
|
||||
];
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const [x1, y1, x2, y2] = faceUVs[i];
|
||||
const offset = i * 8;
|
||||
uvs[offset + 0] = x1; uvs[offset + 1] = 1 - y2;
|
||||
uvs[offset + 2] = x2; uvs[offset + 3] = 1 - y2;
|
||||
uvs[offset + 4] = x1; uvs[offset + 5] = 1 - y1;
|
||||
uvs[offset + 6] = x2; uvs[offset + 7] = 1 - y1;
|
||||
}
|
||||
|
||||
geometry.attributes.uv.needsUpdate = true;
|
||||
}
|
||||
|
||||
function setBodyUVs(geometry) {
|
||||
const uvs = geometry.attributes.uv.array;
|
||||
const faceUVs = [
|
||||
[20/64, 20/64, 28/64, 32/64], // front
|
||||
[32/64, 20/64, 40/64, 32/64], // back
|
||||
[20/64, 16/64, 28/64, 20/64], // top
|
||||
[28/64, 16/64, 36/64, 20/64], // bottom
|
||||
[16/64, 20/64, 20/64, 32/64], // right
|
||||
[28/64, 20/64, 32/64, 32/64] // left
|
||||
];
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const [x1, y1, x2, y2] = faceUVs[i];
|
||||
const offset = i * 8;
|
||||
uvs[offset + 0] = x1; uvs[offset + 1] = 1 - y2;
|
||||
uvs[offset + 2] = x2; uvs[offset + 3] = 1 - y2;
|
||||
uvs[offset + 4] = x1; uvs[offset + 5] = 1 - y1;
|
||||
uvs[offset + 6] = x2; uvs[offset + 7] = 1 - y1;
|
||||
}
|
||||
|
||||
geometry.attributes.uv.needsUpdate = true;
|
||||
}
|
||||
|
||||
function setRightArmUVs(geometry) {
|
||||
const uvs = geometry.attributes.uv.array;
|
||||
const faceUVs = [
|
||||
[44/64, 20/64, 48/64, 32/64],
|
||||
[52/64, 20/64, 56/64, 32/64],
|
||||
[44/64, 16/64, 48/64, 20/64],
|
||||
[48/64, 16/64, 52/64, 20/64],
|
||||
[40/64, 20/64, 44/64, 32/64],
|
||||
[48/64, 20/64, 52/64, 32/64]
|
||||
];
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const [x1, y1, x2, y2] = faceUVs[i];
|
||||
const offset = i * 8;
|
||||
uvs[offset + 0] = x1; uvs[offset + 1] = 1 - y2;
|
||||
uvs[offset + 2] = x2; uvs[offset + 3] = 1 - y2;
|
||||
uvs[offset + 4] = x1; uvs[offset + 5] = 1 - y1;
|
||||
uvs[offset + 6] = x2; uvs[offset + 7] = 1 - y1;
|
||||
}
|
||||
|
||||
geometry.attributes.uv.needsUpdate = true;
|
||||
}
|
||||
|
||||
function setLeftArmUVs(geometry) {
|
||||
const uvs = geometry.attributes.uv.array;
|
||||
const faceUVs = [
|
||||
[36/64, 52/64, 40/64, 64/64],
|
||||
[44/64, 52/64, 48/64, 64/64],
|
||||
[36/64, 48/64, 40/64, 52/64],
|
||||
[40/64, 48/64, 44/64, 52/64],
|
||||
[32/64, 52/64, 36/64, 64/64],
|
||||
[40/64, 52/64, 44/64, 64/64]
|
||||
];
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const [x1, y1, x2, y2] = faceUVs[i];
|
||||
const offset = i * 8;
|
||||
uvs[offset + 0] = x1; uvs[offset + 1] = 1 - y2;
|
||||
uvs[offset + 2] = x2; uvs[offset + 3] = 1 - y2;
|
||||
uvs[offset + 4] = x1; uvs[offset + 5] = 1 - y1;
|
||||
uvs[offset + 6] = x2; uvs[offset + 7] = 1 - y1;
|
||||
}
|
||||
|
||||
geometry.attributes.uv.needsUpdate = true;
|
||||
}
|
||||
|
||||
function setRightLegUVs(geometry) {
|
||||
const uvs = geometry.attributes.uv.array;
|
||||
const faceUVs = [
|
||||
[4/64, 20/64, 8/64, 32/64],
|
||||
[12/64, 20/64, 16/64, 32/64],
|
||||
[4/64, 16/64, 8/64, 20/64],
|
||||
[8/64, 16/64, 12/64, 20/64],
|
||||
[0/64, 20/64, 4/64, 32/64],
|
||||
[8/64, 20/64, 12/64, 32/64]
|
||||
];
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const [x1, y1, x2, y2] = faceUVs[i];
|
||||
const offset = i * 8;
|
||||
uvs[offset + 0] = x1; uvs[offset + 1] = 1 - y2;
|
||||
uvs[offset + 2] = x2; uvs[offset + 3] = 1 - y2;
|
||||
uvs[offset + 4] = x1; uvs[offset + 5] = 1 - y1;
|
||||
uvs[offset + 6] = x2; uvs[offset + 7] = 1 - y1;
|
||||
}
|
||||
|
||||
geometry.attributes.uv.needsUpdate = true;
|
||||
}
|
||||
|
||||
function setLeftLegUVs(geometry) {
|
||||
const uvs = geometry.attributes.uv.array;
|
||||
const faceUVs = [
|
||||
[20/64, 52/64, 24/64, 64/64],
|
||||
[28/64, 52/64, 32/64, 64/64],
|
||||
[20/64, 48/64, 24/64, 52/64],
|
||||
[24/64, 48/64, 28/64, 52/64],
|
||||
[16/64, 52/64, 20/64, 64/64],
|
||||
[24/64, 52/64, 28/64, 64/64]
|
||||
];
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const [x1, y1, x2, y2] = faceUVs[i];
|
||||
const offset = i * 8;
|
||||
uvs[offset + 0] = x1; uvs[offset + 1] = 1 - y2;
|
||||
uvs[offset + 2] = x2; uvs[offset + 3] = 1 - y2;
|
||||
uvs[offset + 4] = x1; uvs[offset + 5] = 1 - y1;
|
||||
uvs[offset + 6] = x2; uvs[offset + 7] = 1 - y1;
|
||||
}
|
||||
|
||||
geometry.attributes.uv.needsUpdate = true;
|
||||
}
|
||||
|
||||
// Animation function
|
||||
function updateAnimation() {
|
||||
if (!skinMesh) return;
|
||||
|
||||
const parts = skinMesh.userData;
|
||||
animationTime += 0.05;
|
||||
|
||||
// Reset rotations
|
||||
Object.values(parts).forEach(part => {
|
||||
part.rotation.set(0, 0, 0);
|
||||
});
|
||||
|
||||
switch(currentAnimation) {
|
||||
case 'walk':
|
||||
parts.rightArm.rotation.x = Math.sin(animationTime) * 0.5;
|
||||
parts.leftArm.rotation.x = -Math.sin(animationTime) * 0.5;
|
||||
parts.rightLeg.rotation.x = -Math.sin(animationTime) * 0.5;
|
||||
parts.leftLeg.rotation.x = Math.sin(animationTime) * 0.5;
|
||||
break;
|
||||
|
||||
case 'run':
|
||||
parts.rightArm.rotation.x = Math.sin(animationTime * 2) * 0.8;
|
||||
parts.leftArm.rotation.x = -Math.sin(animationTime * 2) * 0.8;
|
||||
parts.rightLeg.rotation.x = -Math.sin(animationTime * 2) * 0.8;
|
||||
parts.leftLeg.rotation.x = Math.sin(animationTime * 2) * 0.8;
|
||||
skinMesh.position.y = Math.abs(Math.sin(animationTime * 2)) * 2;
|
||||
break;
|
||||
|
||||
case 'wave':
|
||||
parts.rightArm.rotation.z = -Math.PI / 2;
|
||||
parts.rightArm.rotation.x = Math.sin(animationTime * 3) * 0.3;
|
||||
break;
|
||||
|
||||
case 'idle':
|
||||
default:
|
||||
// Gentle breathing/idle motion
|
||||
skinMesh.position.y = Math.sin(animationTime * 0.5) * 0.3;
|
||||
parts.rightArm.rotation.x = Math.sin(animationTime * 0.3) * 0.05;
|
||||
parts.leftArm.rotation.x = -Math.sin(animationTime * 0.3) * 0.05;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Animation loop
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
if (skinMesh) {
|
||||
updateAnimation();
|
||||
}
|
||||
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
// Load skin from file
|
||||
document.getElementById('skinInput').addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
const loader = new THREE.TextureLoader();
|
||||
loader.load(event.target.result, (texture) => {
|
||||
createMinecraftCharacter(texture);
|
||||
});
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
|
||||
// Load preset skins
|
||||
function loadPreset(type) {
|
||||
const paths = {
|
||||
'frost': 'frost-wizard-skin-proper.png',
|
||||
'fire': 'fire-emissary-meg-skin.png',
|
||||
'arcane': 'arcane-catalyst-holly-skin.png'
|
||||
};
|
||||
|
||||
// Note: In a real deployment, these would need to be accessible
|
||||
// For now, show a message
|
||||
alert(`To view ${type} skin:\n\n1. Download the ${type} skin PNG\n2. Click "Upload Skin PNG"\n3. Select the downloaded file`);
|
||||
}
|
||||
|
||||
// Change animation
|
||||
function changeAnimation() {
|
||||
const select = document.getElementById('animationSelect');
|
||||
currentAnimation = select.value;
|
||||
animationTime = 0;
|
||||
}
|
||||
|
||||
// Initialize on page load
|
||||
window.addEventListener('load', init);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user