trešdiena, 2025. gada 8. janvāris

Python code to generate music with facebook/musicgen (facebook/musicgen-melody)

import os
import psutil
import torch
import gc
from transformers import AutoProcessor, MusicgenMelodyForConditionalGeneration, MusicgenMelodyConfig
import scipy
# https://huggingface.co/docs/transformers/main/model_doc/musicgen_melody

# Function to log memory usage
def log_memory(stage=""):
process = psutil.Process(os.getpid())
print(f"Memory Usage after {stage}: {process.memory_info().rss / 1024 ** 2} MB")

log_memory("initial load")

# Hugging Face token for authentication
token = "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"

# Load model configuration and manually add missing config attributes
#model_name = "facebook/musicgen-small" # Use smaller variants if available
model_name = "facebook/musicgen-melody" # For better output
config = MusicgenMelodyConfig.from_pretrained(model_name, token=token)

# Manually add the missing 'use_cache' attribute
config.use_cache = False # This should resolve the AttributeError you encountered

# Manually add the missing initializer_factor if it's required
config.initializer_factor = 1.0 # Default value for initialization

# Modify configuration parameters for debugging
config.dropout = 0.1
config.layerdrop = 0.1
config.max_position_embeddings = 512 # Reduced
config.hidden_size = 128 # Smaller hidden size
config.num_codebooks = 128 # Adjusted to a smaller number for compatibility
config.scale_embedding = True
config.vocab_size = 50257
config.num_hidden_layers = 2 # Fewer layers
config.num_attention_heads = 4 # Fewer attention heads
config.attention_dropout = 0.1
config.activation_function = "gelu"
config.activation_dropout = 0.1
config.ffn_dim = 1024

log_memory("after config")

# Load the model
model = MusicgenMelodyForConditionalGeneration.from_pretrained(model_name, config=config, token=token).eval()

log_memory("after model loaded")

# Processor for the model
processor = AutoProcessor.from_pretrained(model_name)

# Ensure proper input shape by padding to the required size
prompt = "A relaxing jazz track with piano and bass."

input_ids = processor(
text=[prompt],
padding=True,
return_tensors="pt",
).to(model.device)

# Check the shape after reshaping
print(f"Input tensor shape after reshaping: {input_ids['input_ids'].shape}")

# Generate audio based on input prompt with no_grad to save memory
with torch.no_grad():
generated_audio = model.generate(**input_ids, max_new_tokens=1024)
print(generated_audio)

log_memory("after generation")

# Check type of the audio data
print(f"Type of generated audio: {type(generated_audio)}")

# Save the generated audio to a file
if isinstance(generated_audio, torch.Tensor):
sampling_rate = model.config.audio_encoder.sampling_rate
scipy.io.wavfile.write("generated_music.wav", rate=sampling_rate, data=generated_audio.to("cpu")[0, 0].numpy())
else:
print("Unexpected audio format, unable to save.")

# Cleanup
del generated_audio # Explicitly delete the variable
gc.collect() # Garbage collection
log_memory("after cleanup")

Photorealistic filter for video game footage

Here is a step-by-step guide for creating a photorealistic filter for video game footage using the workflow and free tools mentioned:


Step 1: Extract Video Frames

First, convert the video game footage into individual image frames.

  1. Install FFmpeg:

    • Download FFmpeg from FFmpeg Official Website.
    • Add FFmpeg to your system’s PATH for easy access via the terminal/command prompt.
  2. Extract Frames:

    • Open a terminal and navigate to the folder containing your video (input.mp4).
    • Run the following command:
      ffmpeg -i input.mp4 -vf fps=30 frames/frame_%04d.png
      
      • input.mp4: Replace this with your video file name.
      • fps=30: Set the output frame rate (30 frames per second).
      • frames/frame_%04d.png: Saves frames in the frames/ folder as frame_0001.png, frame_0002.png, etc.
  3. Verify Output:

    • Check the frames/ folder to ensure the extracted frames are saved as images.

Step 2: Process Frames

Option A: Apply Style Transfer with CycleGAN

  1. Download CycleGAN:

    • Clone the repository:
      git clone https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix.git
      cd pytorch-CycleGAN-and-pix2pix
      
  2. Install Dependencies:

    • Install required Python libraries:
      pip install -r requirements.txt
      
  3. Download Pretrained Models:

    • Download a pretrained model (e.g., horse2zebra for stylistic changes or fine-tune for photorealism):
      bash ./scripts/download_cyclegan_model.sh horse2zebra
      
  4. Apply Style Transfer:

    • Use the test.py script to process frames:
      python test.py --dataroot ./frames --name horse2zebra_pretrained --model test --no_dropout
      
      • Replace horse2zebra with your pretrained model.
      • Processed frames will be saved in the results/ folder.

Option B: Apply Super-Resolution with ESRGAN

  1. Download ESRGAN:

    • Clone the repository:
      git clone https://github.com/xinntao/ESRGAN.git
      cd ESRGAN
      
  2. Install Dependencies:

    • Install required Python libraries:
      pip install -r requirements.txt
      
  3. Download Pretrained Models:

    • Download the ESRGAN model from here.
  4. Run Super-Resolution:

    • Process frames using the ESRGAN script:
      python test.py --input_folder ./frames --output_folder ./processed_frames
      
      • Input: ./frames/
      • Output: ./processed_frames/

Option C: Generate Depth Maps with MiDaS

  1. Download MiDaS:

    • Clone the MiDaS repository:
      git clone https://github.com/isl-org/MiDaS.git
      cd MiDaS
      
  2. Install Dependencies:

    • Install PyTorch and MiDaS dependencies:
      pip install torch torchvision
      pip install -r requirements.txt
      
  3. Run Depth Estimation:

    • Generate depth maps for each frame:
      python run.py --input_path ./frames --output_path ./depth_maps
      
      • Input: ./frames/
      • Output: ./depth_maps/

Step 3: Reassemble Processed Frames into a Video

  1. Ensure Processed Frames are Ordered:

    • Processed frames should be named sequentially (frame_0001.png, frame_0002.png, etc.).
  2. Merge Frames into Video:

    • Run the following FFmpeg command:
      ffmpeg -framerate 30 -i processed_frames/frame_%04d.png -c:v libx264 -pix_fmt yuv420p output.mp4
      
      • -framerate 30: Match the original frame rate (30 FPS).
      • processed_frames/frame_%04d.png: Processed frames directory.
      • output.mp4: Final video file.
  3. Verify Output:

    • Check the output.mp4 file to ensure it combines the processed frames into a smooth video.

Optimizing for GTX 1650 Ti

  • Resolution: Limit frame resolution to 720p (1280x720) to avoid GPU memory issues.
  • Batch Processing: Process frames in batches to reduce VRAM usage.
    • Modify scripts to load and process fewer images at a time.
  • Mixed Precision: Use PyTorch's torch.cuda.amp for mixed-precision training or inference to save VRAM.

Potential Enhancements

  1. Dataset Fine-Tuning:

    • Train CycleGAN or ESRGAN on a custom dataset with real-world images for better photorealism.
    • Use datasets like COCO or Flickr for training.
  2. Post-Processing:

    • Add cinematic effects using DaVinci Resolve (free) for professional video editing.

Let me know if you’d like help setting up any of these tools or troubleshooting specific issues!

pirmdiena, 2024. gada 5. augusts

How to code a multiplayer 3d game using javascript, php, mysql, enable3d?

 get enable3d libraries - use lib's from this nice example - 

https://enable3d.io/examples/medieval-fantasy-book.html


this code has almost zero features implemented besides having character animations, multiplayer and chatbox. 


what You need to implement Yourself ->

this game needs some features like, opening car doors and driving them, realistic trains, maybe weapons, and off course it needs very good 3d modelling of glb files - game map and characters, good animation design where You can use mixamo.com or make animations on character *bones* Yourself.

use code from enable3d to have best implementation.


missing php files - >

I will give only db.php as backend, You will have to code additional API interfaces that return json (or not) Yourself. (It's and easy task to do in php pdo)


database format that You need in mysql.






back end code (php) works on database called game, without implemented password, tables and collumns must be implemented after example - 

<?php
session_start();
//header('Content-Type: application/json');

//// Error reporting for debugging
//ini_set('display_errors', 1);
//ini_set('display_startup_errors', 1);
//error_reporting(E_ALL);

// Database connection function
function getDbConnection() {
$dsn = 'mysql:dbname=game;host=127.0.0.1';
$user = 'root';
$password2 = '';
try {
return new PDO($dsn, $user, $password2);
} catch (PDOException $e) {
echo json_encode(['error' => 'Database connection failed: ' . $e->getMessage()]);
exit;
}
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
header('Content-Type: application/json');

$action = $_POST['action'] ?? '';
$x = $_POST['x'] ?? '';
$y = $_POST['y'] ?? '';
$z = $_POST['z'] ?? '';
function validateRotation($rotation) {
return is_numeric($rotation) ? floatval($rotation) : 0.0;
}


$rotationx = validateRotation($_POST['rotationx'] ?? '');

$rotationy = validateRotation($_POST['rotationy'] ?? '');

$rotationz = validateRotation($_POST['rotationz'] ?? '');



$animation = $_POST['animation'] ?? '';
//if (!isset($_SESSION['user_id'])) {
//echo json_encode(['error' => 'User not logged in']);
//exit;
//}
switch ($action) {
case 'update_position':
updatePlayerPosition($x, $y, $z);
break;
case 'update_rotation':
updatePlayerRotation($rotationx, $rotationy, $rotationz);
break;
case 'update_animation':
updatePlayerAnimation($animation);
break;
case 'update_date':
updatePlayerDATE();
break;
case 'get_players':
getPlayers();
break;
default:
echo json_encode(['error' => 'Invalid action']);
}
}



function updatePlayerAnimation($animation) {
error_log("Updating animation for player {$_SESSION['user_id']} to {$animation}");

$dbh = getDbConnection();
$player_id = $_SESSION['user_id'];
$stmt = $dbh->prepare("UPDATE users SET animation = ? WHERE user_id = ?");
if ($stmt->execute([$animation, $player_id])) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['error' => 'Failed to update position']);
}
}
function updatePlayerRotation($rotationx, $rotationy, $rotationz) {
$dbh = getDbConnection();
$player_id = $_SESSION['user_id'];
header('Content-Type: application/json');

$stmt = $dbh->prepare("UPDATE users SET rotationx = ? , rotationy = ? , rotationz = ? WHERE user_id = ?");
if ($stmt->execute([$rotationx, $rotationy, $rotationz, $player_id])) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['error' => 'Failed to update position rotation']);
}
}


function updatePlayerPosition($x, $y, $z) {
$dbh = getDbConnection();
$player_id = $_SESSION['user_id'];
header('Content-Type: application/json');

if($y > 0.5){
$stmt = $dbh->prepare("UPDATE users SET x = ?, y = ?, z = ? WHERE user_id = ?");
if ($stmt->execute([$x, $y, $z, $player_id])) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['error' => 'Failed to update position']);
}
}
}



function updatePlayerDATE() {

$dbh = getDbConnection();

$SEARCH = "yes";
$datenow = date("YmdHis");
$stmt = $dbh->prepare("UPDATE users SET date = :date WHERE logged = 'yes'");
$stmt->bindValue(':date', $datenow);
$stmt->execute();
}





//$dbh = getDbConnection();
//$lietotajs = $_SESSION['user_id'];
//echo $lietotajs;
function getPlayers() {
try {
$dbh = getDbConnection();
$lietotajs = $_SESSION['user_id'];

$stmt = $dbh->prepare("SELECT user_id, name, x, y, z, rotationx, rotationy, rotationz, animation, skin, logged FROM users WHERE user_id != ? AND logged = 'yes'");
if ($stmt->execute([$lietotajs])) {
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Ensure all values are of the correct type and handle missing keys
$result = array_map(function($player) {
return [
'user_id' => isset($player['user_id']) ? (int)$player['user_id'] : 0,
'name' => isset($player['name']) ? (string)$player['name'] : 'Player ' . $player['user_id'], // Added name
'x' => isset($player['x']) ? (float)$player['x'] : 0.0,
'y' => isset($player['y']) ? (float)$player['y'] : 0.0,
'z' => isset($player['z']) ? (float)$player['z'] : 0.0,
'rotationx' => isset($player['rotationx']) ? (float)$player['rotationx'] : 0.0,
'rotationy' => isset($player['rotationy']) ? (float)$player['rotationy'] : 0.0,
'rotationz' => isset($player['rotationz']) ? (float)$player['rotationz'] : 0.0,
'animation' => isset($player['animation']) ? (string)$player['animation'] : 'idle',
'skin' => isset($player['skin']) ? (string)$player['skin'] : '/assets/glb/girl.glb'
];
}, $result);

header('Content-Type: application/json');
echo json_encode($result);
} else {
header('Content-Type: application/json');
echo json_encode(['error' => 'Failed to fetch players']);
}
} catch (Exception $e) {
header('Content-Type: application/json');
echo json_encode(['error' => $e->getMessage()]);
}
}




function logged_in() {
return (isset($_SESSION['user_id']));
}

function logged() {
if(isset($_SESSION['user_id'])){
$dbh = getDbConnection();

$player_id = $_SESSION['user_id'];
$stmt = $dbh->prepare("UPDATE users SET logged = 'yes' WHERE user_id = ?");
$stmt->execute([$player_id]);
$stmt->execute();
}
}

function login($username, $password) {
$user_id = user_id_from_username($username);
$password = md5($password);
$dbh = getDbConnection();

$results = $dbh->prepare("SELECT user_id FROM users WHERE name = ? AND password = ? ");
$results->execute(array($username, $password));
$results->execute();
$countedresults = $results ->rowCount();
if($countedresults == 1){
$loginst = $user_id;
$_SESSION['user_id'] = $user_id;
} else $loginst = false;
return $loginst;
}
function register_user($register_data){
$dbh = getDbConnection();


$active = 1;
$query = "INSERT INTO `users` (
`password`,
`name`,
`email`,
`gender`,
`active`
) VALUES(
:password,
:name,
:email,
:gender,
:active
)
";
$stmt = $dbh->prepare($query);
$stmt->bindValue(':password', md5($register_data['password']), PDO::PARAM_STR);
$stmt->bindValue(':name', $register_data['name'], PDO::PARAM_STR);
$stmt->bindValue(':email', $register_data['email'], PDO::PARAM_STR);
$stmt->bindValue(':gender', $register_data['gender'], PDO::PARAM_STR);

$stmt->bindValue(':active', $active, PDO::PARAM_STR);
$stmt->execute();
$affected_rows = $stmt->rowCount();
}
function email_exists($email) {
$dbh = getDbConnection();

$results = $dbh->prepare("SELECT `user_id` FROM `users` WHERE `email` = ?");
$results->execute(array($email));
$results->execute();
$countedresults = $results ->rowCount();

if($countedresults == 1){
$emailexists = true;
} else $emailexists = false;
return $emailexists;
}
function logged_in_redirect() {
if (logged_in() === true) {
header('Location: index.php');
exit();
}
}
function user_id_from_username($username) {
$dbh = getDbConnection();

$results = $dbh->prepare("SELECT * FROM users WHERE name = ?");
$results->execute(array($username));
$results->execute();
while($row = $results->fetch(PDO::FETCH_ASSOC))
{
$user_id = $row['user_id'];
$_SESSION['user_id'] = $user_id;
}
$countedresults = $results ->rowCount();
if($countedresults == 1){
$userexists = $user_id;
} else $userexists = 0;
return $userexists;
}
function user_active($username) {
$dbh = getDbConnection();

$stmt = $dbh->prepare("SELECT * FROM `users` WHERE `name` = ? AND 'active' = 1");
$stmt->execute(array($username));
$countedresults = $stmt ->rowCount();
if($countedresults == 1){
$results = true;
$_SESSION['user_id'] = $user_id;
} else $results = false;
return $results;
}

function user_count() {
$dbh = getDbConnection();

$results = $dbh->prepare("SELECT user_id FROM users WHERE active = 1");
$results->execute();
$countedresults = $results ->rowCount();
return $countedresults;
}

function user_data($user_id){
$data = array();
$user_id = (int)$user_id;

$func_num_args = func_num_args();
$func_get_args = func_get_args();

if ($func_num_args > 1) {
unset($func_get_args[0]);

$fields = '`' . implode('`, `', $func_get_args) . '`';
$dbh = getDbConnection();

$sql = sprintf("SELECT %s FROM users WHERE user_id = ? LIMIT 1", $fields);
$stmt = $dbh->prepare($sql);
$stmt->execute(array($user_id));
$data = $stmt->fetch(PDO::FETCH_ASSOC);

return $data;
}
}



if (logged_in() === true) {
$session_user_id = $_SESSION['user_id'];
$user_data = user_data($session_user_id, 'user_id', 'password', 'name', 'gender', 'email', 'x','y','z', 'animation', 'skin', 'active');


if (user_active($user_data['name']) === true) {
session_destroy();
header('Location: index.php');
exit();
}
} else {
header('Location: index.php');
}




?>













front end code, libs and other files can be found on enable3d homepage github -


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>3D ONLINE GAME 2</title>
<link rel="stylesheet" href="css/examples.css?ver=1.0.0" />
<script src="js/examples.js?ver=1.1.1"></script>
<script src="lib/phaser.min.js?ver=3.52.0"></script>
<script src="lib/enable3d/enable3d.phaserExtension.0.25.0.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

<script src="lib/tween.umd.js"></script>


</head>

<body>
<style>
.player-label {
position: absolute;
z-index: 1;
pointer-events: none;
}
</style>
<center>
<h1>
<?php include_once 'db.php';



if(isset($_SESSION['user_id'])){

$dsn = 'mysql:dbname=game;host=127.0.0.1';
$user = 'root';
$password = '';
$dbh = new PDO($dsn, $user, $password);

$stmt = $dbh->prepare("SELECT * FROM users WHERE user_id = :user_id");
$stmt->bindValue(':user_id', $_SESSION['user_id']);

$stmt->execute();
while($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
$logged = $row['logged'];

if($logged === 'no'){
header('Location:index.php');
}
}
}


if(!isset($_SESSION['user_id'])){
header('Location:index.php');
};
$lietotajs = $user_data['name'];
$skin = $user_data['skin'];
echo $lietotajs . ' ' . $skin;
?>
</h1> <a href="logout.php">Logout.</a>

</center>
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}

#chatbox {
position: fixed;
bottom: 20px;
right: 20px;
width: 300px;
height: 400px;
border: 1px solid #ccc;
display: flex;
flex-direction: column;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
border-radius: 10px;
overflow: hidden;
background-color: rgba(255, 255, 255, 0.9);
transition: height 0.3s ease;
z-index: 9999;
}

#chat-header {
padding: 10px;
background-color: #4CAF50;
color: white;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
}

#toggle-chat {
background: none;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
}

#chat-messages {
flex-grow: 1;
overflow-y: auto;
padding: 10px;
}

#chat-input {
display: flex;
padding: 10px;
background-color: #eee;
}

#message-input {
flex-grow: 1;
margin-right: 10px;
padding: 5px;
border: 1px solid #ddd;
border-radius: 3px;
}

#send-button {
padding: 5px 10px;
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
border-radius: 3px;
}

.message {
margin-bottom: 10px;
padding: 5px;
background-color: #e6e6e6;
border-radius: 5px;
}

#chatbox.minimized {
height: 40px;
}

#chatbox.minimized #chat-messages,
#chatbox.minimized #chat-input {
display: none;
}
</style>
<div id="chatbox">
<div id="chat-messages"></div>
<div id="chat-input">
<input type="text" id="message-input" placeholder="Type your message...">
<button id="send-button">Send</button>
</div>
</div>


<script src="scriptM.js"></script>
<div id="info-text">Use WASD, SPACE and your Mouse.<br />Try to play it on your mobile device :)</div>
<script>
// Add this to the top of the script

const {
enable3d,
Scene3D,
Canvas,
ThirdDimension,
THREE,
JoyStick,
ExtendedObject3D,
ThirdPersonControls,
PointerLock,
PointerDrag
} = ENABLE3D

/**
* Is touch device?
*/
const isTouchDevice = 'ontouchstart' in window

class MainScene extends Scene3D {
constructor() {
super({ key: 'MainScene' })
this.playerId;
this.otherPlayers = {};
this.lastPlayedAnimations = {}; // Add this line

}

init() {
this.accessThirdDimension({ maxSubSteps: 10, fixedTimeStep: 1 / 120 })

this.canJump = true
this.move = false

this.moveTop = 0
this.moveRight = 0
}


updatePlayerPosition() {
if (this.man && this.man.position) {
$.post('db.php', {
action: 'update_position',
x: this.man.position.x,
y: this.man.position.y,
z: this.man.position.z
});
}
}
updatePlayerDATE() {
$.post('db.php', {
action: 'update_date'
});
}
updatePlayerRotation() {
if (!this.man) {
console.warn("Player object not available");
return;
}
if (!this.man.rotation) {
console.warn("Player rotation not available");
return;
}

const rotationX = this.man.rotation?.x ?? this.man.rotation?._x ?? 0;
const rotationY = this.man.rotation?.y ?? this.man.rotation?._y ?? 0;
const rotationZ = this.man.rotation?.z ?? this.man.rotation?._z ?? 0;

if (typeof rotationX === 'number' &&
typeof rotationY === 'number' &&
typeof rotationZ === 'number') {
$.post('db.php', {
action: 'update_rotation',
rotationx: rotationX,
rotationy: rotationY,
rotationz: rotationZ,
});
} else {
console.warn("Invalid rotation data", {
x: rotationX,
y: rotationY,
z: rotationZ,
rawRotation: this.man.rotation
});
}
}


updatePlayerAnimation() {
//console.log("this.man", this.man._currentAnimation);
if (this.man && this.man._currentAnimation) {
$.post('db.php', {
action: 'update_animation',
animation: this.man._currentAnimation,
});
}
}
addLabelToPlayer(playerObject, labelText) {
// Create a canvas for the label text
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = 256;
canvas.height = 64;

// Set font and draw text
context.font = '24px Arial';
context.fillStyle = 'black';
context.textAlign = 'center';
context.fillText(labelText, 128, 32);

// Create a texture from the canvas
const texture = new THREE.CanvasTexture(canvas);

// Create a sprite material using the texture
const material = new THREE.SpriteMaterial({ map: texture });

// Create a sprite and add it to the player object
const sprite = new THREE.Sprite(material);
sprite.scale.set(2, 0.5, 1); // Adjust scale as needed
sprite.position.y = 1.4; // Adjust this value to position the label above the character's head

playerObject.add(sprite);
playerObject.userData.labelSprite = sprite;
}



updateLabelPositions() {
const camera = this.third.camera;
const tempV = new THREE.Vector3();

Object.values(this.otherPlayers).forEach(playerObject => {
const labelElement = playerObject.userData.labelElement;
if (labelElement) {
// Get the position of the player in world space
playerObject.getWorldPosition(tempV);
// Offset the label position to be above the player's head
tempV.y += 12; // Adjust this value based on your character's height
// Project the world position to screen space
tempV.project(camera);

// Convert the normalized device coordinates to CSS pixels
const x = (tempV.x * 0.5 + 0.5) * window.innerWidth;
const y = (tempV.y * -0.5 + 0.5) * window.innerHeight;

// Update the label's position
labelElement.style.transform = `translate(-50%, -50%) translate(${x}px,${y}px)`;
// Optional: hide the label if it's behind the camera
labelElement.style.display = tempV.z > 1 ? 'none' : 'block';
}
});
}


updatePlayerList() {
if (!this.otherPlayers) {
this.otherPlayers = {};
}
if (!this.lastPlayedAnimations) {
this.lastPlayedAnimations = {};
}

$.ajax({
url: 'db.php',
type: 'POST',
data: { action: 'get_players' },
dataType: 'json',
success: (data) => {
if (!Array.isArray(data)) {
console.error('Received data is not an array:', data);
return;
}
const currentPlayerIds = new Set();
data.forEach((player) => {
const playerId = player.user_id.toString();
currentPlayerIds.add(playerId);
if (playerId === this.playerId) {
// Skip updating the current player
return;
}
if (!this.otherPlayers[playerId]) {
// Initialize new player
this.otherPlayers[playerId] = this.createOrUpdatePlayerObject(player);
this.lastPlayedAnimations[playerId] = '';
}
const playerObject = this.createOrUpdatePlayerObject(player);
// Update position
playerObject.position.set(
player.x,
player.y - (playerObject.userData.yOffset || 0.5),
player.z
);
// Update rotation
playerObject.rotation.set(
player.rotationx,
player.rotationy,
player.rotationz
);
// Update animation
if (player.animation && player.animation !== this.lastPlayedAnimations[playerId]) {
this.playAnimation(playerObject, player.animation);
this.lastPlayedAnimations[playerId] = player.animation;
}

// Update skin if needed
//if (player.skin && playerObject.skin !== player.skin) {
//this.updatePlayerSkin(playerObject, player.skin);
//}
});
// Remove players that are no longer in the game
Object.keys(this.otherPlayers).forEach(id => {
if (!currentPlayerIds.has(id)) {
this.removePlayer(id);
}
});
},
error: (jqXHR, textStatus, errorThrown) => {
console.error('Error updating player list:', textStatus, errorThrown);
}
});
}

//updatePlayerSkin(playerObject, newSkinPath) {
//// Remove the current model
//while(playerObject.children.length > 0) {
//playerObject.remove(playerObject.children[0]);
//}
//// Load and add the new model
//this.third.load.gltf('https://skatetubenext.sytes.net/jurmalastreets3d/assets/glb/' + newSkinPath).then(object => {
//const model = object.scene;
//playerObject.add(model);
//playerObject.skin = newSkinPath;

//// Set up animations for the new model
//this.setupPlayerAnimation(playerObject, object.animations);
//});
//}




playAnimation(playerObject, animationName) {
console.log(`Attempting to play animation: ${animationName}`);
if (!playerObject.animationsMap) {
console.warn(`AnimationsMap not found for player`);
return;
}
let animation = playerObject.animationsMap.get(animationName);
if (!animation) {
console.warn(`Animation ${animationName} not found for player. Falling back to 'idle' if available.`);
animation = playerObject.animationsMap.get('idle') || playerObject.animationsMap.values().next().value;
if (!animation) {
console.error(`No animations available for player`);
return;
}
}

const oldAction = playerObject.currentAction;
if (oldAction && oldAction !== animation) {
oldAction.fadeOut(0.5);
}
animation.reset().fadeIn(0.5).play();
playerObject.currentAction = animation;
console.log(`Playing animation '${animation._clip.name}' for player`);
}



removePlayer(id) {
if (this.otherPlayers[id]) {
const playerObject = this.otherPlayers[id];
// Remove the label
if (playerObject.userData.labelElement) {
playerObject.userData.labelElement.remove();
}
// Stop any ongoing animations
if (playerObject.mixer) {
playerObject.mixer.stopAllAction();
}

// Remove from scene
this.third.scene.remove(playerObject);

// Remove physics body if it exists
if (playerObject.body) {
this.third.physics.destroy(playerObject);
}

// Clean up resources
playerObject.traverse((child) => {
if (child.geometry) child.geometry.dispose();
if (child.material) {
if (Array.isArray(child.material)) {
child.material.forEach(material => material.dispose());
} else {
child.material.dispose();
}
}
});

delete this.otherPlayers[id];
delete this.lastPlayedAnimations[id];
console.log(`Player ${id} removed`);
}
}




createOrUpdatePlayerObject(playerData) {
const playerId = playerData.user_id.toString();
if (this.otherPlayers[playerId]) {
return this.updatePlayerObject(this.otherPlayers[playerId], playerData);
}
console.log('Creating new player object for player ${playerId}');
const playerObject = new ExtendedObject3D();
this.third.add.existing(playerObject);
playerObject.userData.id = playerId;
this.otherPlayers[playerId] = playerObject;
function calculateYOffset(object) {
let yOffset = 0;
// Compute the bounding box
const boundingBox = new THREE.Box3().setFromObject(object);
// Calculate the height of the model
const height = boundingBox.max.y - boundingBox.min.y;
// Set the offset to half the height (assuming you want the origin at the center)
yOffset = height / 2;
return yOffset;
}
const skinPath = 'https://skatetubenext.sytes.net/jurmalastreets3d/assets/glb/' + playerData.skin;
console.log('Attempting to load skin for player ${playerId}: ${skinPath}');

this.third.load.gltf(skinPath)
.then(object => {
console.log("objekts ielādējās");
const model = object.scene;
console.log("model", model);
playerObject.add(model);
//const yOffset = calculateYOffset(model);
//playerObject.userData.yOffset = yOffset;
//if(yOffset){
//playerObject.position.set(playerData.x, playerData.y - yOffset, playerData.z);
//} else {
playerObject.position.set(playerData.x, playerData.y, playerData.z);
//}
playerObject.traverse(child => {
if (child.isMesh) {
child.castShadow = child.receiveShadow = false
// https://discourse.threejs.org/t/cant-export-material-from-blender-gltf/12258
child.material.roughness = 1
child.material.metalness = 0
}
})
this.setupPlayerAnimation(playerObject, object.animations);
//add label
this.addLabelToPlayer(playerObject, playerData.name || `Player ${playerId}`);

})
.catch(error => {
console.error(`Error loading player model for player ${playerId}:`, error);
console.log('fallback modelis būtu jaielādē');
//return this.loadFallbackModel();
})
return playerObject;
}








updatePlayerObject(playerObject, playerData) {
const previousPosition = playerObject.position.clone();
playerObject.position.set(playerData.x, playerData.y - (playerObject.userData.yOffset || 0.5), playerData.z);
// Calculate movement
const movement = playerObject.position.distanceTo(previousPosition);
// Threshold for considering the player as moving
const movementThreshold = 0.01; // Adjust this value as needed
if (movement > movementThreshold) {
if (playerObject.animation.current !== 'run') {
playerObject.animation.play('run');
}
} else {
if (playerObject.animation.current !== 'idle') {
playerObject.animation.play('idle');
}
}
// Update other properties as needed...
return playerObject;
}


setupPlayerAnimation(playerObject, animations) {
console.log("uzstāda player animācijas");
playerObject.mixer = new THREE.AnimationMixer(playerObject);
playerObject.animations = animations || [];
playerObject.animationsMap = new Map();
this.third.animationMixers.add(playerObject.animation.mixer)
playerObject.animations.forEach(animation => {
if (animation.name) {
playerObject.animation.add(animation.name, animation)
}
})
playerObject.animation.play('idle')
}

createFallbackObject(playerObject) {
const geometry = new THREE.BoxGeometry(1, 2, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
playerObject.add(cube);
//this.third.add.existing(playerObject);
//// Add physics if needed
//this.third.physics.add.existing(playerObject, {
//shape: 'capsule',
//radius: 0.5,
//height: 1,
//offset: { y: -0.5 }
//});
}



startMultiplayerFeatures() {
console.log('Starting multiplayer features');
this.time.addEvent({
delay: 1000,
callback: this.updatePlayerList,
callbackScope: this,
loop: true
});
}


async create() {
$.ajax({
url: 'get_player_id.php',
method: 'GET',
dataType: 'json',
success: (data) => {
this.playerId = data.playerId;
// Start the game or enable multiplayer features here
this.startMultiplayerFeatures();
},
error: (jqXHR, textStatus, errorThrown) => {
console.error('Error getting player ID:', textStatus, errorThrown);
}
});

this.time.addEvent({
delay: 100,
callback: this.updatePlayerPosition,
callbackScope: this,
loop: true
});

this.time.addEvent({
delay: 100,
callback: this.updatePlayerAnimation,
callbackScope: this,
loop: true
});
this.time.addEvent({
delay: 100,
callback: this.updatePlayerList,
callbackScope: this,
loop: true
});
this.time.addEvent({
delay: 100,
callback: this.updatePlayerDATE,
callbackScope: this,
loop: true
});

const { lights } = await this.third.warpSpeed('-ground', '-orbitControls')

const { hemisphereLight, ambientLight, directionalLight } = lights
const intensity = 1
hemisphereLight.intensity = intensity
ambientLight.intensity = intensity
directionalLight.intensity = intensity

this.third.physics.add.box({ y: 10, x: 35 }, { lambert: { color: 'red' } })

// this.third.physics.debug.enable()

/**
* Medieval Fantasy Book by Pixel (https://sketchfab.com/stefan.lengyel1)
* https://sketchfab.com/3d-models/medieval-fantasy-book-06d5a80a04fc4c5ab552759e9a97d91a
* Attribution 4.0 International (CC BY 4.0)
*/
this.third.load.gltf('/jurmalastreets3d/assets/glb/FINALLY_RESULTS_for_game_map3.glb').then(object => {
const scene = object.scenes[0]

const book = new ExtendedObject3D()
book.name = 'scene'
book.add(scene)
this.third.add.existing(book)

// add animations
// sadly only the flags animations works
object.animations.forEach((anim, i) => {
book.mixer = this.third.animationMixers.create(book)
// overwrite the action to be an array of actions
book.action = []
book.action[i] = book.mixer.clipAction(anim)
book.action[i].play()
})

book.traverse(child => {
if (child.isMesh) {
child.castShadow = child.receiveShadow = false
child.material.metalness = 0
child.material.roughness = 1

if (/mesh/i.test(child.name)) {
this.third.physics.add.existing(child, {
shape: 'concave',
mass: 0,
collisionFlags: 1,
autoCenter: false
})
child.body.setAngularFactor(0, 0, 0)
child.body.setLinearFactor(0, 0, 0)
}
}
})
})

/**
* box_man.glb by Jan Bláha
* https://github.com/swift502/Sketchbook
* CC-0 license 2018
*/
////masivi kur turet skinus
var objskin = [];
$.ajax({
type: "GET",
async: false,
url: 'getskin.php',
data:{},
success:function(data) {
objskin = JSON.parse(data);
console.log("objskin", objskin);
}

});




this.third.load.gltf("/jurmalastreets3d/assets/glb/" + objskin).then(object => {
const man = object.scene.children[0]

this.man = new ExtendedObject3D()
this.time.addEvent({
delay: 100,
callback: this.updatePlayerRotation,
callbackScope: this,
loop: true
});
//console.log(names[step], "names")
this.man.name = 'girl';
this.man.rotateY(Math.PI + 0.1) // a hack
this.man.add(man)
this.man.rotation.set(0, Math.PI * 1.5, 0)
//console.log("rotation", this.man.rotation._x);
//console.log("rotation", this.man.rotation._y);
//console.log("rotation", this.man.rotation._z);
var objx;
$.ajax({
type: "GET",
async: false,
url: 'getlocationx.php',
data:{},
success:function(data) {
objx = JSON.parse(data);
}

});
var objy;
$.ajax({
type: "GET",
async: false,
url: 'getlocationy.php',
data:{},
success:function(data) {
objy = JSON.parse(data);
}

});
var objz;
$.ajax({
type: "GET",
async: false,
url: 'getlocationz.php',
data:{},
success:function(data) {
objz = JSON.parse(data);
}

});
this.man.position.set(objx, objy + 10, objz);

//this.man.position.set(objx[step], objy[step], objz[step])



// add shadow
this.man.traverse(child => {
if (child.isMesh) {
child.castShadow = child.receiveShadow = true
// https://discourse.threejs.org/t/cant-export-material-from-blender-gltf/12258
child.material.roughness = 1
child.material.metalness = 0
}
})

/**
* Animations
*/
this.third.animationMixers.add(this.man.animation.mixer)
object.animations.forEach(animation => {
if (animation.name) {
this.man.animation.add(animation.name, animation)
}
})
this.man.animation.play('idle')

/**
* Add the player to the scene with a body
*/
this.third.add.existing(this.man)
this.third.physics.add.existing(this.man, {
shape: 'sphere',
radius: 0.25,
width: 0.5,
offset: { y: -0.25 }
})
this.man.body.setFriction(0.8)
this.man.body.setAngularFactor(0, 0, 0)

// https://docs.panda3d.org/1.10/python/programming/physics/bullet/ccd
this.man.body.setCcdMotionThreshold(1e-7)
this.man.body.setCcdSweptSphereRadius(0.25)

/**
* Add 3rd Person Controls
*/
this.controls = new ThirdPersonControls(this.third.camera, this.man, {
offset: new THREE.Vector3(0, 1, 0),
targetRadius: 3
})
// set initial view to 90 deg theta
this.controls.theta = 90

/**
* Add Pointer Lock and Pointer Drag
*/
if (!isTouchDevice) {
let pl = new PointerLock(this.game.canvas)
let pd = new PointerDrag(this.game.canvas)
pd.onMove(delta => {
if (pl.isLocked()) {
this.moveTop = -delta.y
this.moveRight = delta.x
}
})
}

/**
* Add Keys
*/
const chatMessages = document.getElementById('chat-messages');
const chatForm = document.getElementById('chat-input');

console.log(chatForm);

function addMessage(message) {
const messageElement = document.createElement('div');
messageElement.classList.add('fromuser');
messageElement.classList.add('message');
messageElement.textContent = `${message.fromuser}: ${message.message}`;
chatMessages.appendChild(messageElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
this.keys = {
a: this.input.keyboard.addKey('a'),
w: this.input.keyboard.addKey('w'),
d: this.input.keyboard.addKey('d'),
s: this.input.keyboard.addKey('s'),
space: this.input.keyboard.addKey(32)
}


var area = document.getElementById('message-input');

area.addEventListener('click', (e) => {
e.preventDefault();

this.input.keyboard.removeCapture('W,S,A,D');
this.input.keyboard.removeCapture([ 32 ]);

});

var elem = document.getElementById('enable3d-phaser-canvas');
elem.addEventListener('click', (e) => {

this.keys = {
a: this.input.keyboard.addKey('a'),
w: this.input.keyboard.addKey('w'),
d: this.input.keyboard.addKey('d'),
s: this.input.keyboard.addKey('s'),
space: this.input.keyboard.addKey(32)
}


});



setInterval(loadMessages, 5000);

function loadMessages() {
//alert("loads messages");
fetch('get_messages.php')
.then(response => response.json())
.then(messages => {
chatMessages.innerHTML = '';

messages.forEach(addMessage);
})
.catch(error => console.error('Error:', error));
}

document.getElementById('send-button').addEventListener('click', function() {
const messageInput = document.getElementById('message-input');
const messageText = messageInput.value;
//console.log("submit");
const message = messageInput.value.trim();
//alert(message);
if (message) {
const formData = new FormData();
formData.append('message', message);

fetch('send_message.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
messageInput.value = '';
loadMessages();
} else {
console.error('Error sending message:', data.error);
}
})
.catch(error => console.error('Error:', error));
}
});

/**
* Add joystick
*/
if (isTouchDevice) {
const joystick = new JoyStick()
const axis = joystick.add.axis({
styles: { left: 35, bottom: 35, size: 100 }
})
axis.onMove(event => {
/**
* Update Camera
*/
const { top, right } = event
this.moveTop = top * 3
this.moveRight = right * 3
})
const buttonA = joystick.add.button({
letter: 'A',
styles: { right: 35, bottom: 110, size: 80 }
})
buttonA.onClick(() => this.jump())
const buttonB = joystick.add.button({
letter: 'B',
styles: { right: 110, bottom: 35, size: 80 }
})
buttonB.onClick(() => (this.move = true))
buttonB.onRelease(() => (this.move = false))
}
});
};

jump() {
if (!this.man || !this.canJump) return
this.canJump = false
this.man.animation.play('jump_running', 500, false)
this.time.addEvent({
delay: 650,
callback: () => {
this.canJump = true
this.man.animation.play('idle')
}
})
this.man.body.applyForceY(6)
};

update(time, delta) {
// Update animations
if (this.man && this.man.animation && this.man.animation.mixer) {
this.man.animation.mixer.update(delta / 1000);
}
this.third.animationMixers.update(delta / 1000);

// Update animations for other players
Object.values(this.otherPlayers).forEach(player => {
if (player.mixer) {
player.mixer.update(delta / 1000);
}
});
this.updateLabelPositions();

if (this.man && this.man.body) {
/**
* Update Controls
*/
this.controls.update(this.moveRight * 2, -this.moveTop * 2)
if (!isTouchDevice) this.moveRight = this.moveTop = 0
/**
* Player Turn
*/
const speed = 4
const v3 = new THREE.Vector3()

const rotation = this.third.camera.getWorldDirection(v3)
const theta = Math.atan2(rotation.x, rotation.z)
const rotationMan = this.man.getWorldDirection(v3)
const thetaMan = Math.atan2(rotationMan.x, rotationMan.z)
this.man.body.setAngularVelocityY(0)

const l = Math.abs(theta - thetaMan)
let rotationSpeed = isTouchDevice ? 2 : 4
let d = Math.PI / 24

if (l > d) {
if (l > Math.PI - d) rotationSpeed *= -1
if (theta < thetaMan) rotationSpeed *= -1
this.man.body.setAngularVelocityY(rotationSpeed)
}

/**
* Player Move
*/
if (this.keys.w.isDown || this.move) {
if (this.man.animation.current === 'idle' && this.canJump) this.man.animation.play('run')

const x = Math.sin(theta) * speed,
y = this.man.body.velocity.y,
z = Math.cos(theta) * speed




this.man.body.setVelocity(x, y, z)
} else {


if (this.man.animation.current === 'run' && this.canJump) this.man.animation.play('idle')
}

/**
* Player Jump
*/
if (this.keys.space.isDown && this.canJump) {
this.jump()
}
}
}
}

const config = {
type: Phaser.WEBGL,
transparent: true,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
width: window.innerWidth * Math.max(1, window.devicePixelRatio / 2),
height: window.innerHeight * Math.max(1, window.devicePixelRatio / 2)
},
scene: [MainScene],
...Canvas({ antialias: false })
}

window.addEventListener('load', () => {
enable3d(() => new Phaser.Game(config)).withPhysics('lib/ammo/kripken')
})






</script>
</body>
</html>