Compare commits
8 Commits
237518638c
...
fdb57289f8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdb57289f8 | ||
|
|
60c1329163 | ||
|
|
0e71c0f581 | ||
|
|
eb67a33331 | ||
|
|
39f20796ce | ||
|
|
e00165c56f | ||
|
|
02c6497700 | ||
|
|
0efd476676 |
BIN
frontend/public/img/skins/steve.png
Normal file
BIN
frontend/public/img/skins/steve.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -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/steve.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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ export class IntersectionPlaneService {
|
||||||
// Center the plane vertically with the player (player is about 2 blocks tall)
|
// Center the plane vertically with the player (player is about 2 blocks tall)
|
||||||
this.intersectionPlane.position.y = 1;
|
this.intersectionPlane.position.y = 1;
|
||||||
this.rendererService.scene.add(this.intersectionPlane);
|
this.rendererService.scene.add(this.intersectionPlane);
|
||||||
|
this.intersectionPlane.renderOrder = 1;
|
||||||
|
|
||||||
return this.intersectionPlane;
|
return this.intersectionPlane;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,68 @@ 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.playerModel.renderOrder = 0;
|
||||||
|
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,16 +101,154 @@ 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);
|
|
||||||
|
|
||||||
return this.playerModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the player model
|
* Creates a textured player model using the Minecraft skin
|
||||||
*/
|
*/
|
||||||
getPlayerModel(): THREE.Group {
|
private createTexturedPlayerModel(): void {
|
||||||
return this.playerModel;
|
// Create the player with properly mapped textures
|
||||||
|
|
||||||
|
// 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.26, 0.7, 0.26, // 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.26, 0.7, 0.26, // 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 {
|
||||||
|
const geometry = new THREE.BoxGeometry(width, height, depth);
|
||||||
|
|
||||||
|
// Remap the custom face order to BoxGeometry face order: px, nx, py, ny, pz, nz
|
||||||
|
const faceOrder = [2, 3, 0, 1, 4, 5]; // right, left, top, bottom, front, back
|
||||||
|
const textureWidth = 64;
|
||||||
|
const textureHeight = 64;
|
||||||
|
const uv = geometry.attributes['uv'];
|
||||||
|
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
const face = uvMapping[faceOrder[i]];
|
||||||
|
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;
|
||||||
|
|
||||||
|
let uvs: [number, number][] = [
|
||||||
|
[x1, y1], // top-left
|
||||||
|
[x2, y1], // top-right
|
||||||
|
[x1, y2], // bottom-left
|
||||||
|
[x2, y2] // bottom-right
|
||||||
|
];
|
||||||
|
|
||||||
|
const uvOffset = i * 8;
|
||||||
|
for (let j = 0; j < 4; j++) {
|
||||||
|
uv.array[uvOffset + j * 2] = uvs[j][0];
|
||||||
|
uv.array[uvOffset + j * 2 + 1] = uvs[j][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uv.needsUpdate = true;
|
||||||
|
|
||||||
|
const material = new THREE.MeshBasicMaterial({
|
||||||
|
map: this.skinTexture,
|
||||||
|
transparent: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mesh = new THREE.Mesh(geometry, material);
|
||||||
|
mesh.position.set(position.x, position.y, position.z);
|
||||||
|
|
||||||
|
return mesh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export class RendererService {
|
||||||
|
|
||||||
// Create camera
|
// Create camera
|
||||||
this.camera = new THREE.PerspectiveCamera(75, containerWidth / containerHeight, 0.1, 1000);
|
this.camera = new THREE.PerspectiveCamera(75, containerWidth / containerHeight, 0.1, 1000);
|
||||||
this.camera.position.set(0, 1, 3);
|
this.camera.position.set(-1, 2, 3);
|
||||||
this.camera.lookAt(0, 1, 0);
|
this.camera.lookAt(0, 1, 0);
|
||||||
|
|
||||||
// Create renderer
|
// Create renderer
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user