Initial attempt at textures

This commit is contained in:
Teriuihi 2025-06-22 19:53:27 +02:00
parent 237518638c
commit 0efd476676
3 changed files with 235 additions and 10 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -49,7 +49,11 @@ export class RenderContainerComponent implements AfterViewInit, OnDestroy {
*/ */
private initializeScene(): void { private initializeScene(): void {
this.rendererService.initializeRenderer(this.rendererContainer); this.rendererService.initializeRenderer(this.rendererContainer);
this.playerModelService.loadSkinTexture('/public/img/skins/akastijn.png')
.then(() => {
// Then create the player model with the texture applied
this.playerModelService.createPlayerModel(); this.playerModelService.createPlayerModel();
});
this.intersectionPlaneService.createIntersectionPlane(); this.intersectionPlaneService.createIntersectionPlane();
this.inputHandlerService.initializeInputHandlers(this.rendererService.renderer.domElement); this.inputHandlerService.initializeInputHandlers(this.rendererService.renderer.domElement);
} }

View File

@ -1,24 +1,68 @@
import { Injectable } from '@angular/core'; import {Injectable} from '@angular/core';
import * as THREE from 'three'; import * as THREE from 'three';
import { RendererService } from './renderer.service'; import {RendererService} from './renderer.service';
/**
* Service responsible for creating and managing the player model
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class PlayerModelService { export class PlayerModelService {
private playerModel!: THREE.Group; private playerModel!: THREE.Group;
private skinTexture!: THREE.Texture;
private textureLoaded = false;
constructor(private rendererService: RendererService) {} constructor(private rendererService: RendererService) {
}
/** /**
* Creates a simple player model and adds it to the scene * Loads a Minecraft skin texture from a URL
* @param textureUrl The URL of the skin texture to load
* @returns A promise that resolves when the texture is loaded
*/
loadSkinTexture(textureUrl: string): Promise<void> {
return new Promise((resolve) => {
const loader = new THREE.TextureLoader();
loader.load(textureUrl, (texture) => {
// Set texture parameters
texture.magFilter = THREE.NearestFilter;
texture.minFilter = THREE.NearestFilter;
this.skinTexture = texture;
this.textureLoaded = true;
// If the player model already exists, rebuild it with textures
if (this.playerModel) {
// Remove old model
this.rendererService.scene.remove(this.playerModel);
// Create new model with textures
this.createPlayerModel();
}
resolve();
});
});
}
/**
* Creates a player model with Minecraft-style textures and adds it to the scene
*/ */
createPlayerModel(): THREE.Group { createPlayerModel(): THREE.Group {
this.playerModel = new THREE.Group(); this.playerModel = new THREE.Group();
if (this.textureLoaded) {
// Create textured model if texture is loaded
this.createTexturedPlayerModel();
} else {
// Create simple colored model if no texture is loaded
this.createSimplePlayerModel();
}
this.rendererService.scene.add(this.playerModel);
return this.playerModel;
}
/**
* Creates a simple colored player model (without textures)
*/
private createSimplePlayerModel(): void {
// Head // Head
const headGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5); const headGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
const headMaterial = new THREE.MeshLambertMaterial({color: 0xffccaa}); const headMaterial = new THREE.MeshLambertMaterial({color: 0xffccaa});
@ -56,10 +100,187 @@ export class PlayerModelService {
const rightLeg = new THREE.Mesh(legGeometry, legMaterial); const rightLeg = new THREE.Mesh(legGeometry, legMaterial);
rightLeg.position.set(0.125, 0.15, 0); rightLeg.position.set(0.125, 0.15, 0);
this.playerModel.add(rightLeg); this.playerModel.add(rightLeg);
}
this.rendererService.scene.add(this.playerModel); /**
* Creates a textured player model using the Minecraft skin
*/
private createTexturedPlayerModel(): void {
// Create the player with properly mapped textures
return this.playerModel; // Head - 8x8x8 pixels in the texture
this.playerModel.add(this.createBoxWithUvMapping(
0.5, 0.5, 0.5, // width, height, depth
[
{x: 8, y: 0, w: 8, h: 8}, // top
{x: 16, y: 0, w: 8, h: 8}, // bottom
{x: 16, y: 8, w: 8, h: 8}, // right
{x: 0, y: 8, w: 8, h: 8}, // left
{x: 8, y: 8, w: 8, h: 8}, // front
{x: 24, y: 8, w: 8, h: 8} // back
],
{x: 0, y: 1.35, z: 0} // position
));
// Body - 8x12x4 pixels in the texture
this.playerModel.add(this.createBoxWithUvMapping(
0.5, 0.7, 0.25, // width, height, depth
[
{x: 20, y: 16, w: 8, h: 4}, // top
{x: 28, y: 16, w: 8, h: 4}, // bottom
{x: 28, y: 20, w: 4, h: 12}, // right
{x: 16, y: 20, w: 4, h: 12}, // left
{x: 20, y: 20, w: 8, h: 12}, // front
{x: 32, y: 20, w: 8, h: 12} // back
],
{x: 0, y: 0.75, z: 0} // position
));
// Left Arm - 4x12x4 pixels in the texture
this.playerModel.add(this.createBoxWithUvMapping(
0.2, 0.7, 0.25, // width, height, depth
[
{x: 44, y: 16, w: 4, h: 4}, // top
{x: 48, y: 16, w: 4, h: 4}, // bottom
{x: 48, y: 20, w: 4, h: 12}, // right
{x: 40, y: 20, w: 4, h: 12}, // left
{x: 44, y: 20, w: 4, h: 12}, // front
{x: 52, y: 20, w: 4, h: 12} // back
],
{x: -0.35, y: 0.75, z: 0} // position
));
// Right Arm - 4x12x4 pixels in the texture
this.playerModel.add(this.createBoxWithUvMapping(
0.2, 0.7, 0.25, // width, height, depth
[
{x: 44, y: 16, w: 4, h: 4}, // top - mirror of left arm
{x: 48, y: 16, w: 4, h: 4}, // bottom - mirror of left arm
{x: 40, y: 20, w: 4, h: 12}, // right - mirror of left arm's left
{x: 48, y: 20, w: 4, h: 12}, // left - mirror of left arm's right
{x: 44, y: 20, w: 4, h: 12}, // front - same as left arm
{x: 52, y: 20, w: 4, h: 12} // back - same as left arm
],
{x: 0.35, y: 0.75, z: 0} // position
));
// Left Leg - 4x12x4 pixels in the texture
this.playerModel.add(this.createBoxWithUvMapping(
0.25, 0.7, 0.25, // width, height, depth
[
{x: 4, y: 16, w: 4, h: 4}, // top
{x: 8, y: 16, w: 4, h: 4}, // bottom
{x: 8, y: 20, w: 4, h: 12}, // right
{x: 0, y: 20, w: 4, h: 12}, // left
{x: 4, y: 20, w: 4, h: 12}, // front
{x: 12, y: 20, w: 4, h: 12} // back
],
{x: -0.125, y: 0.15, z: 0} // position
));
// Right Leg - 4x12x4 pixels in the texture
this.playerModel.add(this.createBoxWithUvMapping(
0.25, 0.7, 0.25, // width, height, depth
[
{x: 4, y: 16, w: 4, h: 4}, // top - mirror of left leg
{x: 8, y: 16, w: 4, h: 4}, // bottom - mirror of left leg
{x: 0, y: 20, w: 4, h: 12}, // right - mirror of left leg's left
{x: 8, y: 20, w: 4, h: 12}, // left - mirror of left leg's right
{x: 4, y: 20, w: 4, h: 12}, // front - same as left leg
{x: 12, y: 20, w: 4, h: 12} // back - same as left leg
],
{x: 0.125, y: 0.15, z: 0} // position
));
}
/**
* Creates a box with proper UV mapping for a Minecraft character part
* @param width Width of the box
* @param height Height of the box
* @param depth Depth of the box
* @param uvMapping Array of UV coordinates for each face (top, bottom, right, left, front, back)
* @param position Position of the box
* @returns THREE.Mesh with properly mapped textures
*/
private createBoxWithUvMapping(
width: number,
height: number,
depth: number,
uvMapping: Array<{ x: number, y: number, w: number, h: number }>,
position: { x: number, y: number, z: number }
): THREE.Mesh {
// Create box geometry
const geometry = new THREE.BoxGeometry(width, height, depth);
// Texture dimensions (Minecraft skins are 64x64)
const textureWidth = 64;
const textureHeight = 64;
// Calculate UV coordinates for each face
// Three.js creates 2 triangles per face, so we need to set UVs for 6 faces * 2 triangles * 3 vertices
const uvs = [];
// Order of faces in BoxGeometry: px, nx, py, ny, pz, nz (right, left, top, bottom, front, back)
// However, our order is: top, bottom, right, left, front, back
// So we need to remap
const remappedUVs = [
uvMapping[2], // right (px)
uvMapping[3], // left (nx)
uvMapping[0], // top (py)
uvMapping[1], // bottom (ny)
uvMapping[4], // front (pz)
uvMapping[5], // back (nz)
];
// Set UVs for all faces
let faceUVs: number[][] = [];
remappedUVs.forEach(face => {
// Calculate corner coordinates (normalized from 0-1)
const x1 = face.x / textureWidth;
const y1 = 1 - (face.y / textureHeight);
const x2 = (face.x + face.w) / textureWidth;
const y2 = 1 - ((face.y + face.h) / textureHeight);
// Each face has 2 triangles with 3 vertices each
// First triangle: top-left, bottom-right, bottom-left
// Second triangle: top-left, top-right, bottom-right
// Triangle 1
faceUVs.push([x1, y1]); // top-left
faceUVs.push([x2, y2]); // bottom-right
faceUVs.push([x1, y2]); // bottom-left
// Triangle 2
faceUVs.push([x1, y1]); // top-left
faceUVs.push([x2, y1]); // top-right
faceUVs.push([x2, y2]); // bottom-right
});
// Flatten the UV array
const uvArray = new Float32Array(faceUVs.flat());
// Set the UV attribute
geometry.setAttribute('uv', new THREE.BufferAttribute(uvArray, 2));
// Create material with the skin texture
const material = new THREE.MeshBasicMaterial({
map: this.skinTexture,
transparent: true,
});
// Create mesh and set position
const mesh = new THREE.Mesh(geometry, material);
const wireframeMaterial = new THREE.MeshBasicMaterial({
wireframe: true,
color: 0xff0000
});
const wireframe = new THREE.Mesh(geometry, wireframeMaterial);
mesh.add(wireframe);
mesh.position.set(position.x, position.y, position.z);
return mesh;
} }
/** /**