Spaces:
Running
include this manifest on the code so you can fetch the rpm avatars // src/manifests/avatarManifest.js
Browse filesexport const avatarManifest = [
{
id: "6637232cc6a3e0f03418f723",
name: "David",
body: "M",
avatarMood: "neutral",
sources: [
{ type: "gltf", url: "/assets/avatars/david.glb" },
{ type: "rpm", url: (id) => `https://models.readyplayer.me/${id}.glb` },
],
thumbnails: {
default: [
"/assets/thumbs/avatars/avatarDavid_default.png",
"/assets/thumbs/avatars/avatarDavid_happy.png",
],
},
},
{
id: "64e602a9b54bdcd880df8ca3",
name: "Anja",
body: "F",
avatarMood: "neutral",
sources: [
{ type: "gltf", url: "/assets/avatars/anja.glb" },
{ type: "rpm", url: (id) => `https://models.readyplayer.me/${id}.glb` },
],
thumbnails: {
default: [
"/assets/thumbs/avatars/avatarAnja_default.png",
"/assets/thumbs/avatars/avatarAnja_happy.png",
],
},
},
{
id: "6658826709dff701a3a2955e",
name: "Eli",
body: "M",
avatarMood: "neutral",
sources: [
{ type: "gltf", url: "/assets/avatars/eli.glb" },
{ type: "rpm", url: (id) => `https://models.readyplayer.me/${id}.glb` },
],
thumbnails: {
default: [
"/assets/thumbs/avatars/avatarEli_default.png",
"/assets/thumbs/avatars/avatarEli_happy.png",
],
},
},
{
id: "664e502a6ef2fae943a4e5a4",
name: "Julien",
body: "M",
avatarMood: "neutral",
sources: [
{ type: "gltf", url: "/assets/avatars/julien.glb" },
{ type: "rpm", url: (id) => `https://models.readyplayer.me/${id}.glb` },
],
thumbnails: {
default: [
"/assets/thumbs/avatars/avatarJulien_default.png",
"/assets/thumbs/avatars/avatarJulien_happy.png",
],
},
},
{
id: "680ebd7587f61ba0328013ae",
name: "Dan",
body: "M",
avatarMood: "neutral",
sources: [
{ type: "gltf", url: "/assets/avatars/dan.glb" },
{ type: "rpm", url: (id) => `https://avatars.readyplayer.me/${id}.glb` },
],
thumbnails: {
default: [
"/assets/thumbs/avatars/avatarDan_default.png",
"/assets/thumbs/avatars/avatarDan_happy.png",
],
},
},
{
id: "664e502a6ef2fae943a4e5a4",
name: "Lucy",
body: "F",
avatarMood: "neutral",
sources: [
{ type: "gltf", url: "/assets/avatars/lucy.glb" },
{ type: "rpm", url: (id) => `https://avatars.readyplayer.me/${id}.glb` },
],
thumbnails: {
default: [
"/assets/thumbs/avatars/avatarLucy_default.png",
"/assets/thumbs/avatars/avatarLucy_happy.png",
],
},
},
{
id: "665b2ed436c854537e38cdf8",
name: "Lana",
body: "F",
avatarMood: "neutral",
sources: [
{ type: "gltf", url: "/assets/avatars/lana.glb" },
{ type: "rpm", url: (id) => `https://models.readyplayer.me/${id}.glb` },
],
thumbnails: {
default: [
"/assets/thumbs/avatars/avatarLana_default.png",
"/assets/thumbs/avatars/avatarLana_happy.png",
],
},
},
{
id: "665b2ed436c854537e38cdf8",
name: "Nyx",
body: "F",
avatarMood: "neutral",
sources: [
{ type: "gltf", url: "/assets/avatars/nyx.glb" },
{ type: "rpm", url: (id) => `https://models.readyplayer.me/${id}.glb` },
],
thumbnails: {
default: [
"/assets/thumbs/avatars/avatarNyx_default.png",
"/assets/thumbs/avatars/avatarNyx_happy.png",
],
},
},
];
export default avatarManifest;
- index.html +91 -42
|
@@ -258,16 +258,64 @@
|
|
| 258 |
space: false
|
| 259 |
}
|
| 260 |
};
|
| 261 |
-
//
|
| 262 |
const characterData = [
|
| 263 |
-
{
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
];
|
| 272 |
// Three.js variables
|
| 273 |
let scene, camera, renderer, controls;
|
|
@@ -333,13 +381,14 @@
|
|
| 333 |
card.className = 'character-card bg-gray-800 rounded-xl p-4 cursor-pointer transition-all shadow-lg';
|
| 334 |
card.innerHTML = `
|
| 335 |
<div class="character-preview mb-4 flex items-center justify-center">
|
| 336 |
-
<div class="w-24 h-24 rounded-full
|
|
|
|
|
|
|
| 337 |
</div>
|
| 338 |
-
<h3 class="text-xl font-bold text-center mb-2">${character.name}</h3>
|
| 339 |
-
<p class="text-gray-
|
| 340 |
`;
|
| 341 |
-
|
| 342 |
-
card.addEventListener('click', () => {
|
| 343 |
// Deselect all cards
|
| 344 |
document.querySelectorAll('.character-card').forEach(c => {
|
| 345 |
c.classList.remove('ring-2', 'ring-purple-500');
|
|
@@ -370,13 +419,14 @@
|
|
| 370 |
}`;
|
| 371 |
card.innerHTML = `
|
| 372 |
<div class="character-preview mb-4 flex items-center justify-center">
|
| 373 |
-
<div class="w-16 h-16 rounded-full
|
|
|
|
|
|
|
| 374 |
</div>
|
| 375 |
-
<h3 class="text-lg font-bold text-center mb-2">${character.name}</h3>
|
| 376 |
-
<p class="text-gray-
|
| 377 |
`;
|
| 378 |
-
|
| 379 |
-
card.addEventListener('click', () => {
|
| 380 |
// Check if character is already selected
|
| 381 |
const index = gameState.selectedWorldCharacters.findIndex(c => c.id === character.id);
|
| 382 |
|
|
@@ -443,22 +493,22 @@
|
|
| 443 |
scene.background = envMap;
|
| 444 |
scene.environment = envMap;
|
| 445 |
|
| 446 |
-
// Add
|
| 447 |
-
const
|
| 448 |
-
scene.add(
|
| 449 |
-
|
| 450 |
-
const
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
scene.add(
|
| 462 |
// Create ground
|
| 463 |
const groundGeometry = new THREE.PlaneGeometry(100, 100);
|
| 464 |
const groundMaterial = new THREE.MeshStandardMaterial({
|
|
@@ -531,9 +581,9 @@ function addEnvironmentObjects() {
|
|
| 531 |
function loadPlayerCharacter() {
|
| 532 |
loader.load(gameState.selectedCharacter.modelUrl, (gltf) => {
|
| 533 |
const model = gltf.scene;
|
| 534 |
-
// Scale and position the model -
|
| 535 |
-
model.scale.set(0.
|
| 536 |
-
|
| 537 |
// Enable shadows for all children
|
| 538 |
model.traverse((child) => {
|
| 539 |
if (child.isMesh) {
|
|
@@ -750,10 +800,9 @@ function loadNPCCharacters() {
|
|
| 750 |
head.position.y = 1.6;
|
| 751 |
head.castShadow = true;
|
| 752 |
group.add(head);
|
| 753 |
-
// Scale
|
| 754 |
-
group.scale.set(0.
|
| 755 |
-
|
| 756 |
-
// Position NPCs in a circle around the center
|
| 757 |
const angle = (index / gameState.selectedWorldCharacters.length) * Math.PI * 2;
|
| 758 |
const radius = 10 + Math.random() * 10;
|
| 759 |
|
|
|
|
| 258 |
space: false
|
| 259 |
}
|
| 260 |
};
|
| 261 |
+
// RPM Avatar Manifest Data
|
| 262 |
const characterData = [
|
| 263 |
+
{
|
| 264 |
+
id: "6637232cc6a3e0f03418f723",
|
| 265 |
+
name: "David",
|
| 266 |
+
modelUrl: "https://models.readyplayer.me/6637232cc6a3e0f03418f723.glb",
|
| 267 |
+
color: "#3B82F6",
|
| 268 |
+
dialog: ["Hello there! I'm David.", "Nice to meet you!", "Ready for an adventure?"]
|
| 269 |
+
},
|
| 270 |
+
{
|
| 271 |
+
id: "64e602a9b54bdcd880df8ca3",
|
| 272 |
+
name: "Anja",
|
| 273 |
+
modelUrl: "https://models.readyplayer.me/64e602a9b54bdcd880df8ca3.glb",
|
| 274 |
+
color: "#EC4899",
|
| 275 |
+
dialog: ["Hi, I'm Anja!", "Beautiful day, isn't it?", "What brings you here?"]
|
| 276 |
+
},
|
| 277 |
+
{
|
| 278 |
+
id: "6658826709dff701a3a2955e",
|
| 279 |
+
name: "Eli",
|
| 280 |
+
modelUrl: "https://models.readyplayer.me/6658826709dff701a3a2955e.glb",
|
| 281 |
+
color: "#10B981",
|
| 282 |
+
dialog: ["Hey, I'm Eli!", "Love exploring new places.", "What's your story?"]
|
| 283 |
+
},
|
| 284 |
+
{
|
| 285 |
+
id: "664e502a6ef2fae943a4e5a4",
|
| 286 |
+
name: "Julien",
|
| 287 |
+
modelUrl: "https://models.readyplayer.me/664e502a6ef2fae943a4e5a4.glb",
|
| 288 |
+
color: "#F59E0B",
|
| 289 |
+
dialog: ["Julien here!", "Always up for a challenge.", "Let's make this fun!"]
|
| 290 |
+
},
|
| 291 |
+
{
|
| 292 |
+
id: "680ebd7587f61ba0328013ae",
|
| 293 |
+
name: "Dan",
|
| 294 |
+
modelUrl: "https://avatars.readyplayer.me/680ebd7587f61ba0328013ae.glb",
|
| 295 |
+
color: "#8B5CF6",
|
| 296 |
+
dialog: ["Dan reporting for duty!", "Ready when you are.", "Let's do this!"]
|
| 297 |
+
},
|
| 298 |
+
{
|
| 299 |
+
id: "664e502a6ef2fae943a4e5a4",
|
| 300 |
+
name: "Lucy",
|
| 301 |
+
modelUrl: "https://avatars.readyplayer.me/664e502a6ef2fae943a4e5a4.glb",
|
| 302 |
+
color: "#EF4444",
|
| 303 |
+
dialog: ["Hi, I'm Lucy!", "So excited to be here!", "Ready for anything!"]
|
| 304 |
+
},
|
| 305 |
+
{
|
| 306 |
+
id: "665b2ed436c854537e38cdf8",
|
| 307 |
+
name: "Lana",
|
| 308 |
+
modelUrl: "https://models.readyplayer.me/665b2ed436c854537e38cdf8.glb",
|
| 309 |
+
color: "#6366F1",
|
| 310 |
+
dialog: ["Lana at your service!", "What a wonderful world!", "Let's explore together!"]
|
| 311 |
+
},
|
| 312 |
+
{
|
| 313 |
+
id: "665b2ed436c854537e38cdf8",
|
| 314 |
+
name: "Nyx",
|
| 315 |
+
modelUrl: "https://models.readyplayer.me/665b2ed436c854537e38cdf8.glb",
|
| 316 |
+
color: "#F97316",
|
| 317 |
+
dialog: ["I'm Nyx, nice to meet you!", "The night is my domain.", "Ready for adventure!"]
|
| 318 |
+
}
|
| 319 |
];
|
| 320 |
// Three.js variables
|
| 321 |
let scene, camera, renderer, controls;
|
|
|
|
| 381 |
card.className = 'character-card bg-gray-800 rounded-xl p-4 cursor-pointer transition-all shadow-lg';
|
| 382 |
card.innerHTML = `
|
| 383 |
<div class="character-preview mb-4 flex items-center justify-center">
|
| 384 |
+
<div class="w-24 h-24 rounded-full bg-gradient-to-br from-purple-500 to-blue-500 flex items-center justify-center text-white font-bold text-sm">
|
| 385 |
+
${character.name}
|
| 386 |
+
</div>
|
| 387 |
</div>
|
| 388 |
+
<h3 class="text-xl font-bold text-center mb-2 text-white">${character.name}</h3>
|
| 389 |
+
<p class="text-gray-300 text-center text-sm">RPM Avatar</p>
|
| 390 |
`;
|
| 391 |
+
card.addEventListener('click', () => {
|
|
|
|
| 392 |
// Deselect all cards
|
| 393 |
document.querySelectorAll('.character-card').forEach(c => {
|
| 394 |
c.classList.remove('ring-2', 'ring-purple-500');
|
|
|
|
| 419 |
}`;
|
| 420 |
card.innerHTML = `
|
| 421 |
<div class="character-preview mb-4 flex items-center justify-center">
|
| 422 |
+
<div class="w-16 h-16 rounded-full bg-gradient-to-br from-purple-500 to-blue-500 flex items-center justify-center text-white font-bold text-xs">
|
| 423 |
+
${character.name}
|
| 424 |
+
</div>
|
| 425 |
</div>
|
| 426 |
+
<h3 class="text-lg font-bold text-center mb-2 text-white">${character.name}</h3>
|
| 427 |
+
<p class="text-gray-300 text-sm text-center">Click to ${gameState.selectedWorldCharacters.some(c => c.id === character.id) ? 'deselect' : 'select'}</p>
|
| 428 |
`;
|
| 429 |
+
card.addEventListener('click', () => {
|
|
|
|
| 430 |
// Check if character is already selected
|
| 431 |
const index = gameState.selectedWorldCharacters.findIndex(c => c.id === character.id);
|
| 432 |
|
|
|
|
| 493 |
scene.background = envMap;
|
| 494 |
scene.environment = envMap;
|
| 495 |
|
| 496 |
+
// Add better lighting for RPM avatars
|
| 497 |
+
const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1);
|
| 498 |
+
scene.add(hemisphereLight);
|
| 499 |
+
|
| 500 |
+
const mainLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
| 501 |
+
mainLight.position.set(10, 20, 15);
|
| 502 |
+
mainLight.castShadow = true;
|
| 503 |
+
mainLight.shadow.mapSize.width = 2048;
|
| 504 |
+
mainLight.shadow.mapSize.height = 2048;
|
| 505 |
+
mainLight.shadow.camera.near = 0.5;
|
| 506 |
+
mainLight.shadow.camera.far = 50;
|
| 507 |
+
mainLight.shadow.camera.left = -20;
|
| 508 |
+
mainLight.shadow.camera.right = 20;
|
| 509 |
+
mainLight.shadow.camera.top = 20;
|
| 510 |
+
mainLight.shadow.camera.bottom = -20;
|
| 511 |
+
scene.add(mainLight);
|
| 512 |
// Create ground
|
| 513 |
const groundGeometry = new THREE.PlaneGeometry(100, 100);
|
| 514 |
const groundMaterial = new THREE.MeshStandardMaterial({
|
|
|
|
| 581 |
function loadPlayerCharacter() {
|
| 582 |
loader.load(gameState.selectedCharacter.modelUrl, (gltf) => {
|
| 583 |
const model = gltf.scene;
|
| 584 |
+
// Scale and position the RPM avatar model - adjusted for human proportions
|
| 585 |
+
model.scale.set(0.8, 0.8, 0.8);
|
| 586 |
+
model.position.set(0, 0, 0);
|
| 587 |
// Enable shadows for all children
|
| 588 |
model.traverse((child) => {
|
| 589 |
if (child.isMesh) {
|
|
|
|
| 800 |
head.position.y = 1.6;
|
| 801 |
head.castShadow = true;
|
| 802 |
group.add(head);
|
| 803 |
+
// Scale NPCs to match player size
|
| 804 |
+
group.scale.set(0.8, 0.8, 0.8);
|
| 805 |
+
// Position NPCs in a circle around the center
|
|
|
|
| 806 |
const angle = (index / gameState.selectedWorldCharacters.length) * Math.PI * 2;
|
| 807 |
const radius = 10 + Math.random() * 10;
|
| 808 |
|