AI_Diplomacy/diplomacy/animation/renderer/UnitModels.js
2025-03-03 23:13:00 -08:00

290 lines
No EOL
8.9 KiB
JavaScript

// ==============================================================================
// Copyright (C) 2023
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <https://www.gnu.org/licenses/>.
// ==============================================================================
import * as THREE from 'three';
/**
* UnitModels class for creating and managing 3D models for armies and fleets
*/
export class UnitModels {
/**
* Create unit models
* @param {Object} options - Configuration options
* @param {string} options.detailLevel - Level of detail for models ('low', 'medium', 'high')
*/
constructor(options = {}) {
this.detailLevel = options.detailLevel || 'medium';
this.models = {
army: null,
fleet: null
};
// Standard power colors
this.powerColors = {
AUSTRIA: 0xBF1E2E, // Red
ENGLAND: 0x1B5EC0, // Blue
FRANCE: 0x127BBF, // Light Blue
GERMANY: 0x454545, // Dark Gray
ITALY: 0x087E3B, // Green
RUSSIA: 0xFFFFFF, // White
TURKEY: 0xFFD700 // Gold
};
// Initialize models
this._createModels();
}
/**
* Create 3D models for armies and fleets
* @private
*/
_createModels() {
// Create models based on detail level
switch (this.detailLevel) {
case 'high':
this._createHighDetailModels();
break;
case 'low':
this._createLowDetailModels();
break;
case 'medium':
default:
this._createMediumDetailModels();
break;
}
}
/**
* Create low detail (simple) unit models
* @private
*/
_createLowDetailModels() {
// Simple army model (box)
const armyGeometry = new THREE.BoxGeometry(10, 5, 10);
const armyMaterial = new THREE.MeshPhongMaterial({
color: 0x888888,
shininess: 30
});
this.models.army = new THREE.Mesh(armyGeometry, armyMaterial);
// Simple fleet model (cone)
const fleetGeometry = new THREE.ConeGeometry(6, 15, 8);
const fleetMaterial = new THREE.MeshPhongMaterial({
color: 0x888888,
shininess: 30
});
this.models.fleet = new THREE.Mesh(fleetGeometry, fleetMaterial);
// Rotate fleet to look like a ship
this.models.fleet.rotation.x = Math.PI / 2;
}
/**
* Create medium detail unit models
* @private
*/
_createMediumDetailModels() {
// Create army group
this.models.army = new THREE.Group();
// Army base (tank-like shape)
const bodyGeometry = new THREE.BoxGeometry(10, 4, 12);
const bodyMaterial = new THREE.MeshPhongMaterial({
color: 0x888888,
shininess: 50
});
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
body.position.y = 2;
this.models.army.add(body);
// Army turret
const turretGeometry = new THREE.CylinderGeometry(3, 3, 3, 8);
const turretMaterial = new THREE.MeshPhongMaterial({
color: 0x888888,
shininess: 50
});
const turret = new THREE.Mesh(turretGeometry, turretMaterial);
turret.position.y = 5.5;
this.models.army.add(turret);
// Army cannon
const cannonGeometry = new THREE.CylinderGeometry(0.8, 0.8, 8, 8);
const cannonMaterial = new THREE.MeshPhongMaterial({
color: 0x666666,
shininess: 70
});
const cannon = new THREE.Mesh(cannonGeometry, cannonMaterial);
cannon.position.set(0, 5.5, 6);
cannon.rotation.x = Math.PI / 2;
this.models.army.add(cannon);
// Army flag (to show power color)
const flagPoleGeometry = new THREE.CylinderGeometry(0.3, 0.3, 10, 6);
const flagPoleMaterial = new THREE.MeshPhongMaterial({
color: 0xDDDDDD,
shininess: 30
});
const flagPole = new THREE.Mesh(flagPoleGeometry, flagPoleMaterial);
flagPole.position.set(0, 9, -3);
this.models.army.add(flagPole);
const flagGeometry = new THREE.PlaneGeometry(5, 3);
const flagMaterial = new THREE.MeshPhongMaterial({
color: 0xFFFFFF,
shininess: 30,
side: THREE.DoubleSide
});
const flag = new THREE.Mesh(flagGeometry, flagMaterial);
flag.position.set(2.5, 11, -3);
flag.rotation.y = Math.PI / 2;
this.models.army.userData.flag = flag;
this.models.army.add(flag);
// Create fleet group
this.models.fleet = new THREE.Group();
// Fleet hull
const hullGeometry = new THREE.CylinderGeometry(3, 6, 16, 8);
const hullMaterial = new THREE.MeshPhongMaterial({
color: 0x888888,
shininess: 50
});
const hull = new THREE.Mesh(hullGeometry, hullMaterial);
hull.rotation.x = Math.PI / 2;
this.models.fleet.add(hull);
// Fleet deck
const deckGeometry = new THREE.BoxGeometry(4, 1, 12);
const deckMaterial = new THREE.MeshPhongMaterial({
color: 0x666666,
shininess: 50
});
const deck = new THREE.Mesh(deckGeometry, deckMaterial);
deck.position.y = 3;
this.models.fleet.add(deck);
// Fleet sail (to show power color)
const sailGeometry = new THREE.PlaneGeometry(1, 10);
const sailMaterial = new THREE.MeshPhongMaterial({
color: 0xFFFFFF,
shininess: 20,
side: THREE.DoubleSide
});
const sail = new THREE.Mesh(sailGeometry, sailMaterial);
sail.position.set(0, 8, 0);
this.models.fleet.userData.sail = sail;
this.models.fleet.add(sail);
// Fleet mast
const mastGeometry = new THREE.CylinderGeometry(0.4, 0.4, 10, 6);
const mastMaterial = new THREE.MeshPhongMaterial({
color: 0x8B4513,
shininess: 30
});
const mast = new THREE.Mesh(mastGeometry, mastMaterial);
mast.position.set(0, 8, 0);
this.models.fleet.add(mast);
}
/**
* Create high detail unit models
* @private
*/
_createHighDetailModels() {
// For high detail, we'll use the medium detail models for now
// In a real implementation, this would create more complex geometries
this._createMediumDetailModels();
// Add extra details to army
const wheelsGeometry = new THREE.CylinderGeometry(1.5, 1.5, 12, 16);
const wheelsMaterial = new THREE.MeshPhongMaterial({
color: 0x333333,
shininess: 40
});
// Add wheels to the tank
const frontWheels = new THREE.Mesh(wheelsGeometry, wheelsMaterial);
frontWheels.rotation.z = Math.PI / 2;
frontWheels.position.set(0, 1, 4);
this.models.army.add(frontWheels);
const backWheels = new THREE.Mesh(wheelsGeometry, wheelsMaterial);
backWheels.rotation.z = Math.PI / 2;
backWheels.position.set(0, 1, -4);
this.models.army.add(backWheels);
// Add extra details to fleet
const bowGeometry = new THREE.ConeGeometry(3, 4, 8);
const bowMaterial = new THREE.MeshPhongMaterial({
color: 0x888888,
shininess: 50
});
const bow = new THREE.Mesh(bowGeometry, bowMaterial);
bow.rotation.x = Math.PI / 2;
bow.position.z = 10;
this.models.fleet.add(bow);
}
/**
* Get unit model by type
* @param {string} type - Unit type ('A' for army, 'F' for fleet)
* @param {string} power - Power controlling the unit (e.g. 'AUSTRIA')
* @returns {THREE.Object3D} The unit model
*/
getUnitModel(type, power) {
// Get the base model
const modelType = type === 'A' ? 'army' : 'fleet';
const model = this.models[modelType].clone();
// Determine unit color based on power
const color = this.powerColors[power] || 0x888888;
// Apply color
model.traverse(child => {
if (child.isMesh) {
// Create a new material to avoid modifying the original
child.material = child.material.clone();
// Apply power color to main body parts, but not to some details
if (
(modelType === 'army' && child !== model.userData.flag) ||
(modelType === 'fleet' && child !== model.userData.sail)
) {
child.material.color.setHex(color);
}
}
});
// For army flag or fleet sail, set a lighter color
const colorObj = new THREE.Color(color);
const lightColor = new THREE.Color(
Math.min(1, colorObj.r + 0.3),
Math.min(1, colorObj.g + 0.3),
Math.min(1, colorObj.b + 0.3)
);
if (modelType === 'army' && model.userData.flag) {
model.userData.flag.material.color.copy(lightColor);
}
if (modelType === 'fleet' && model.userData.sail) {
model.userData.sail.material.color.copy(lightColor);
}
return model;
}
}