3d-gun-builder / index.html
bsd121's picture
when i press the buttons nothing happens or shows up fix it - Follow Up Deployment
971b5c0 verified
Raw
History Blame Contribute Delete
39.7 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Realistic Gun Customizer</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/GLTFLoader.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/DRACOLoader.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Ubuntu+Mono:wght@400;700&display=swap');
body {
font-family: 'Ubuntu Mono', monospace;
background: #1a1a1a;
color: #e0e0e0;
overflow-x: hidden;
}
#gun-container {
width: 100%;
height: 60vh;
background: #222;
border-radius: 8px;
position: relative;
}
#gun-canvas {
width: 100%;
height: 100%;
display: block;
}
.tab-button.active {
border-bottom: 2px solid #dc2626;
color: white;
}
.part-item {
transition: all 0.2s ease;
}
.part-item:hover {
transform: translateY(-2px);
background: #333;
}
.part-item.selected {
border: 1px solid #dc2626;
background: #333;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #1a1a1a;
}
::-webkit-scrollbar-thumb {
background: #444;
border-radius: 4px;
}
.color-swatch {
width: 24px;
height: 24px;
border-radius: 50%;
cursor: pointer;
border: 2px solid transparent;
}
.color-swatch.selected {
border-color: white;
transform: scale(1.1);
}
#music-control {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 100;
background: rgba(0,0,0,0.7);
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: all 0.3s ease;
}
#music-control:hover {
transform: scale(1.1);
background: rgba(220, 38, 38, 0.7);
}
</style>
</head>
<body class="min-h-screen">
<!-- Audio Elements -->
<audio id="clickSound" src="https://assets.mixkit.co/sfx/preview/mixkit-gun-reload-1668.mp3" preload="auto"></audio>
<audio id="lofiMusic" loop>
<source src="https://assets.mixkit.co/music/preview/mixkit-chilled-urban-jazz-522.mp3" type="audio/mpeg">
</audio>
<!-- Music Control -->
<div id="music-control" title="Toggle Music">
<i class="fas fa-music text-white text-xl"></i>
</div>
<div class="container mx-auto px-4 py-8">
<div class="flex flex-col lg:flex-row gap-8">
<!-- Left Panel - 3D Gun Viewer -->
<div class="w-full lg:w-2/3">
<div class="flex justify-between items-center mb-4">
<h1 class="text-2xl font-bold text-red-600">GUN CUSTOMIZER</h1>
<button id="save-design" class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-md">
<i class="fas fa-save mr-2"></i>Save Design
</button>
</div>
<div id="gun-container" class="shadow-lg">
<canvas id="gun-canvas"></canvas>
</div>
<div class="mt-6 bg-gray-800 p-4 rounded-md">
<h2 class="text-lg font-semibold mb-2">Current Model: <span id="current-model" class="text-red-400">Glock 17</span></h2>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="bg-gray-700 p-3 rounded-md">
<h3 class="text-sm text-gray-400">Model</h3>
<p class="font-mono">3D Demo</p>
</div>
<div class="bg-gray-700 p-3 rounded-md">
<h3 class="text-sm text-gray-400">Format</h3>
<p class="font-mono">GLTF</p>
</div>
<div class="bg-gray-700 p-3 rounded-md">
<h3 class="text-sm text-gray-400">Type</h3>
<p class="font-mono">3D Model</p>
</div>
<div class="bg-gray-700 p-3 rounded-md">
<h3 class="text-sm text-gray-400">Source</h3>
<p class="font-mono">Three.js</p>
</div>
</div>
</div>
</div>
<!-- Right Panel - Customization Options -->
<div class="w-full lg:w-1/3 bg-gray-800 rounded-lg shadow-lg p-4 flex flex-col">
<!-- Tabs -->
<div class="flex border-b border-gray-700 mb-4">
<button class="tab-button active px-4 py-2" data-tab="model">MODEL</button>
<button class="tab-button px-4 py-2" data-tab="parts">PARTS</button>
<button class="tab-button px-4 py-2" data-tab="colors">COLORS</button>
<button class="tab-button px-4 py-2" data-tab="attachments">ATTACHMENTS</button>
</div>
<!-- Tab Content -->
<div class="flex-grow overflow-y-auto pb-4">
<!-- Model Selection Tab -->
<div id="model-tab" class="tab-content active">
<h3 class="font-semibold mb-3 text-gray-300">SELECT WEAPON TYPE</h3>
<div class="space-y-2">
<button class="model-select-btn w-full text-left px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-md" data-model="glock17">
<div class="flex items-center">
<img src="https://via.placeholder.com/80x40/333/666?text=Glock17" class="w-20 h-10 object-contain mr-3">
<span>Glock 17 (Pistol)</span>
</div>
</button>
<button class="model-select-btn w-full text-left px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-md" data-model="m4a1">
<div class="flex items-center">
<img src="https://via.placeholder.com/80x40/333/666?text=M4A1" class="w-20 h-10 object-contain mr-3">
<span>M4A1 (Assault Rifle)</span>
</div>
</button>
<button class="model-select-btn w-full text-left px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-md" data-model="awp">
<div class="flex items-center">
<img src="https://via.placeholder.com/80x40/333/666?text=AWP" class="w-20 h-10 object-contain mr-3">
<span>AWP (Sniper Rifle)</span>
</div>
</button>
<button class="model-select-btn w-full text-left px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-md" data-model="mp5">
<div class="flex items-center">
<img src="https://via.placeholder.com/80x40/333/666?text=MP5" class="w-20 h-10 object-contain mr-3">
<span>MP5 (SMG)</span>
</div>
</button>
<button class="model-select-btn w-full text-left px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-md" data-model="870">
<div class="flex items-center">
<img src="https://via.placeholder.com/80x40/333/666?text=Rem870" class="w-20 h-10 object-contain mr-3">
<span>Remington 870 (Shotgun)</span>
</div>
</button>
<button class="model-select-btn w-full text-left px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-md" data-model="x26">
<div class="flex items-center">
<img src="https://via.placeholder.com/80x40/333/666?text=Taser" class="w-20 h-10 object-contain mr-3">
<span>Taser X26 (Non-Lethal)</span>
</div>
</button>
</div>
</div>
<!-- Parts Customization Tab -->
<div id="parts-tab" class="tab-content hidden">
<h3 class="font-semibold mb-3 text-gray-300">SELECT PART TO MODIFY</h3>
<div class="space-y-2">
<button class="part-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-part="barrel">
<i class="fas fa-bolt mr-2 text-red-400"></i> Barrel
</button>
<button class="part-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-part="slide">
<i class="fas fa-arrows-alt-h mr-2 text-red-400"></i> Slide
</button>
<button class="part-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-part="grip">
<i class="fas fa-hand-holding mr-2 text-red-400"></i> Grip
</button>
<button class="part-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-part="trigger">
<i class="fas fa-arrow-circle-left mr-2 text-red-400"></i> Trigger
</button>
<button class="part-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-part="magazine">
<i class="fas fa-clone mr-2 text-red-400"></i> Magazine
</button>
</div>
<div class="mt-6" id="part-options">
<h3 class="font-semibold mb-3 text-gray-300">SELECT BARREL TYPE</h3>
<div class="grid grid-cols-2 gap-3">
<div class="part-option part-item p-3 bg-gray-700 rounded-md cursor-pointer text-center" data-option="standard">
<img src="https://via.placeholder.com/80x40/333/666?text=Standard" class="w-full h-12 object-contain mb-2">
<p class="text-sm">Standard</p>
</div>
<div class="part-option part-item p-3 bg-gray-700 rounded-md cursor-pointer text-center" data-option="threaded">
<img src="https://via.placeholder.com/80x40/333/666?text=Threaded" class="w-full h-12 object-contain mb-2">
<p class="text-sm">Threaded</p>
</div>
<div class="part-option part-item p-3 bg-gray-700 rounded-md cursor-pointer text-center" data-option="extended">
<img src="https://via.placeholder.com/80x40/333/666?text=Extended" class="w-full h-12 object-contain mb-2">
<p class="text-sm">Extended</p>
</div>
<div class="part-option part-item p-3 bg-gray-700 rounded-md cursor-pointer text-center" data-option="suppressed">
<img src="https://via.placeholder.com/80x40/333/666?text=Suppressed" class="w-full h-12 object-contain mb-2">
<p class="text-sm">Suppressed</p>
</div>
</div>
</div>
</div>
<!-- Colors Customization Tab -->
<div id="colors-tab" class="tab-content hidden">
<h3 class="font-semibold mb-3 text-gray-300">SELECT PART TO RECOLOR</h3>
<div class="space-y-2">
<button class="color-part-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-part="frame">
<i class="fas fa-square mr-2" style="color: #555;"></i> Frame
</button>
<button class="color-part-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-part="slide">
<i class="fas fa-square mr-2" style="color: #777;"></i> Slide
</button>
<button class="color-part-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-part="grip">
<i class="fas fa-square mr-2" style="color: #333;"></i> Grip
</button>
<button class="color-part-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-part="barrel">
<i class="fas fa-square mr-2" style="color: #444;"></i> Barrel
</button>
<button class="color-part-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-part="trigger">
<i class="fas fa-square mr-2" style="color: #888;"></i> Trigger
</button>
</div>
<div class="mt-6">
<h3 class="font-semibold mb-3 text-gray-300">SELECT COLOR</h3>
<div class="grid grid-cols-6 gap-3">
<div class="color-swatch selected" style="background-color: #222;" data-color="#222"></div>
<div class="color-swatch" style="background-color: #555;" data-color="#555"></div>
<div class="color-swatch" style="background-color: #777;" data-color="#777"></div>
<div class="color-swatch" style="background-color: #454545;" data-color="#454545"></div>
<div class="color-swatch" style="background-color: #dc2626;" data-color="#dc2626"></div>
<div class="color-swatch" style="background-color: #166534;" data-color="#166534"></div>
<div class="color-swatch" style="background-color: #1e40af;" data-color="#1e40af"></div>
<div class="color-swatch" style="background-color: #713f12;" data-color="#713f12"></div>
<div class="color-swatch" style="background-color: #111;" data-color="#111"></div>
<div class="color-swatch" style="background-color: #444;" data-color="#444"></div>
<div class="color-swatch" style="background-color: #666;" data-color="#666"></div>
<div class="color-swatch" style="background-color: #999;" data-color="#999"></div>
</div>
</div>
</div>
<!-- Attachments Tab -->
<div id="attachments-tab" class="tab-content hidden">
<h3 class="font-semibold mb-3 text-gray-300">ATTACHMENT POINTS</h3>
<div class="space-y-2">
<button class="attachment-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-slot="top">
<i class="fas fa-arrow-up mr-2 text-red-400"></i> Top Rail (Optics)
</button>
<button class="attachment-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-slot="front">
<i class="fas fa-arrow-right mr-2 text-red-400"></i> Front Rail (Flashlight)
</button>
<button class="attachment-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-slot="side">
<i class="fas fa-arrow-left mr-2 text-red-400"></i> Side Rail (Laser)
</button>
<button class="attachment-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-slot="under">
<i class="fas fa-arrow-down mr-2 text-red-400"></i> Under Rail (Grip)
</button>
<button class="attachment-btn part-item px-4 py-2 bg-gray-700 rounded-md w-full text-left" data-slot="muzzle">
<i class="fas fa-bullseye mr-2 text-red-400"></i> Muzzle (Suppressor)
</button>
</div>
<div class="mt-6">
<h3 class="font-semibold mb-3 text-gray-300">AVAILABLE ATTACHMENTS</h3>
<div id="attachment-options" class="grid grid-cols-2 gap-3">
<div class="attachment-option part-item p-3 bg-gray-700 rounded-md cursor-pointer text-center" data-attachment="none">
<img src="https://via.placeholder.com/80x40/333/666?text=Empty" class="w-full h-12 object-contain mb-2">
<p class="text-sm">None</p>
</div>
<div class="attachment-option part-item p-3 bg-gray-700 rounded-md cursor-pointer text-center" data-attachment="holo">
<img src="https://via.placeholder.com/80x40/333/666?text=Holo" class="w-full h-12 object-contain mb-2">
<p class="text-sm">Holo Sight</p>
</div>
<div class="attachment-option part-item p-3 bg-gray-700 rounded-md cursor-pointer text-center" data-attachment="reddot">
<img src="https://via.placeholder.com/80x40/333/666?text=RedDot" class="w-full h-12 object-contain mb-2">
<p class="text-sm">Red Dot</p>
</div>
<div class="attachment-option part-item p-3 bg-gray-700 rounded-md cursor-pointer text-center" data-attachment="acog">
<img src="https://via.placeholder.com/80x40/333/666?text=ACOG" class="w-full h-12 object-contain mb-2">
<p class="text-sm">ACOG Scope</p>
</div>
<div class="attachment-option part-item p-3 bg-gray-700 rounded-md cursor-pointer text-center" data-attachment="suppressor">
<img src="https://via.placeholder.com/80x40/333/666?text=Suppressor" class="w-full h-12 object-contain mb-2">
<p class="text-sm">Suppressor</p>
</div>
<div class="attachment-option part-item p-3 bg-gray-700 rounded-md cursor-pointer text-center" data-attachment="flashlight">
<img src="https://via.placeholder.com/80x40/333/666?text=Flashlight" class="w-full h-12 object-contain mb-2">
<p class="text-sm">Flashlight</p>
</div>
</div>
</div>
</div>
</div>
<!-- Preview & Reset -->
<div class="mt-auto pt-4 border-t border-gray-700">
<div class="flex gap-3">
<button id="reset-colors" class="flex-1 bg-gray-600 hover:bg-gray-500 text-white px-4 py-2 rounded-md">
<i class="fas fa-palette mr-2"></i>Reset Colors
</button>
<button id="reset-parts" class="flex-1 bg-gray-600 hover:bg-gray-500 text-white px-4 py-2 rounded-md">
<i class="fas fa-undo mr-2"></i>Reset Parts
</button>
</div>
</div>
</div>
</div>
</div>
<script>
// Initialize Three.js scene, camera, and renderer
let scene, camera, renderer, gunModel, controls;
// Current gun configuration
const gunConfig = {
model: 'glock17',
parts: {
barrel: 'standard',
slide: 'standard',
grip: 'standard',
trigger: 'standard',
magazine: 'standard'
},
colors: {
frame: '#222',
slide: '#777',
grip: '#333',
barrel: '#444',
trigger: '#888'
},
attachments: {
top: 'none',
front: 'none',
side: 'none',
under: 'none',
muzzle: 'none'
}
};
function initThreeJS() {
// Scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0x222222);
scene.add(new THREE.AmbientLight(0x404040));
// Camera
camera = new THREE.PerspectiveCamera(75, document.getElementById('gun-container').clientWidth / document.getElementById('gun-container').clientHeight, 0.1, 1000);
camera.position.z = 5;
// Renderer
renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('gun-canvas'), antialias: true });
renderer.setSize(document.getElementById('gun-container').clientWidth, document.getElementById('gun-container').clientHeight);
// Lighting
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
scene.add(directionalLight);
const light2 = new THREE.DirectionalLight(0xffffff, 0.5);
light2.position.set(-1, -1, -1);
scene.add(light2);
// Controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.screenSpacePanning = false;
controls.maxPolarAngle = Math.PI / 2;
controls.minDistance = 2;
controls.maxDistance = 10;
// Load initial gun model
loadGunModel(gunConfig.model);
// Handle window resize
window.addEventListener('resize', onWindowResize);
}
function onWindowResize() {
const container = document.getElementById('gun-container');
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
}
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
function loadGunModel(modelName) {
// Remove existing model
if (gunModel) {
scene.remove(gunModel);
gunModel = null;
}
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.1/');
loader.setDRACOLoader(dracoLoader);
let modelUrl;
switch(modelName) {
case 'glock17':
modelUrl = 'https://threejs.org/examples/models/gltf/LittlestTokyo.glb';
break;
case 'm4a1':
modelUrl = 'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf';
break;
case 'awp':
modelUrl = 'https://threejs.org/examples/models/gltf/FlightHelmet/glTF/FlightHelmet.gltf';
break;
case 'mp5':
modelUrl = 'https://threejs.org/examples/models/gltf/LeePerrySmith/LeePerrySmith.glb';
break;
case '870':
modelUrl = 'https://threejs.org/examples/models/gltf/kira.glb';
break;
case 'x26':
modelUrl = 'https://threejs.org/examples/models/gltf/duck/glTF/Duck.gltf';
break;
}
loader.load(
modelUrl,
function (gltf) {
gunModel = gltf.scene;
scene.add(gunModel);
// Set initial position and scale based on model
if (modelName === 'glock17') {
gunModel.scale.set(1, 1, 1);
gunModel.position.y = -1;
} else if (modelName === 'x26') {
gunModel.scale.set(0.01, 0.01, 0.01);
gunModel.position.y = -0.5;
} else {
gunModel.scale.set(0.5, 0.5, 0.5);
gunModel.position.y = -1;
}
// Add initial material variation
gunModel.traverse(function(child) {
if (child.isMesh) {
child.material = new THREE.MeshStandardMaterial({
color: getColorForPart(child.name),
metalness: 0.7,
roughness: 0.3
});
}
});
},
undefined,
function (error) {
console.error('Error loading model:', error);
}
);
function getColorForPart(partName) {
if (!partName) return new THREE.Color('#777777');
partName = partName.toLowerCase();
if (partName.includes('frame') || partName.includes('body')) {
return new THREE.Color(gunConfig.colors.frame);
} else if (partName.includes('slide')) {
return new THREE.Color(gunConfig.colors.slide);
} else if (partName.includes('grip')) {
return new THREE.Color(gunConfig.colors.grip);
} else if (partName.includes('barrel')) {
return new THREE.Color(gunConfig.colors.barrel);
} else if (partName.includes('trigger')) {
return new THREE.Color(gunConfig.colors.trigger);
}
return new THREE.Color('#777777');
}
gunConfig.model = modelName;
updateCurrentModelText(modelName);
// Position camera based on gun size
camera.position.z = modelName === 'awp' ? 8 : 5;
controls.minDistance = modelName === 'awp' ? 5 : 2;
controls.maxDistance = modelName === 'awp' ? 15 : 10;
}
function updateCurrentModelText(modelName) {
let displayName = '';
switch(modelName) {
case 'glock17': displayName = 'Tokyo Model'; break;
case 'm4a1': displayName = 'Damaged Helmet'; break;
case 'awp': displayName = 'Flight Helmet'; break;
case 'mp5': displayName = 'Character Head'; break;
case '870': displayName = 'Kira Model'; break;
case 'x26': displayName = 'Duck Model'; break;
}
document.getElementById('current-model').textContent = displayName;
}
// UI Event Handlers
function setupEventListeners() {
// Tab switching
document.querySelectorAll('.tab-button').forEach(button => {
button.addEventListener('click', function() {
const tabName = this.getAttribute('data-tab');
// Play click sound
document.getElementById('clickSound').play();
// Update active tab
document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active'));
this.classList.add('active');
// Show correct tab content
document.querySelectorAll('.tab-content').forEach(content => content.classList.add('hidden'));
document.getElementById(`${tabName}-tab`).classList.remove('hidden');
});
});
// Model selection
document.querySelectorAll('.model-select-btn').forEach(button => {
button.addEventListener('click', function() {
const model = this.getAttribute('data-model');
document.getElementById('clickSound').play();
loadGunModel(model);
});
});
// Part selection
document.querySelectorAll('.part-btn').forEach(button => {
button.addEventListener('click', function() {
document.getElementById('clickSound').play();
// Highlight selected part
document.querySelectorAll('.part-btn').forEach(btn => btn.classList.remove('selected'));
this.classList.add('selected');
// Update part options display
const part = this.getAttribute('data-part');
document.querySelector('#part-options h3').textContent = `SELECT ${part.toUpperCase()} TYPE`;
// In a real app, we would update the options shown based on the selected part
});
});
// Part options
document.querySelectorAll('.part-option').forEach(option => {
option.addEventListener('click', function() {
document.getElementById('clickSound').play();
// Highlight selected option
document.querySelectorAll('.part-option').forEach(opt => opt.classList.remove('selected'));
this.classList.add('selected');
// In a real app, we would update the gun model with the selected part
});
});
// Color part selection
document.querySelectorAll('.color-part-btn').forEach(button => {
button.addEventListener('click', function() {
document.getElementById('clickSound').play();
// Highlight selected part
document.querySelectorAll('.color-part-btn').forEach(btn => btn.classList.remove('selected'));
this.classList.add('selected');
});
});
// Color selection
document.querySelectorAll('.color-swatch').forEach(swatch => {
swatch.addEventListener('click', function() {
const color = this.getAttribute('data-color');
// Highlight selected color
document.querySelectorAll('.color-swatch').forEach(s => s.classList.remove('selected'));
this.classList.add('selected');
// Get currently selected part
const selectedPartBtn = document.querySelector('.color-part-btn.selected');
if (selectedPartBtn) {
const part = selectedPartBtn.getAttribute('data-part');
gunConfig.colors[part] = color;
// Update model colors
if (gunModel) {
gunModel.traverse(function(child) {
if (child.isMesh) {
const color = getColorForPart(child.name);
child.material.color.copy(color);
}
});
}
}
});
});
// Attachment slot selection
document.querySelectorAll('.attachment-btn').forEach(button => {
button.addEventListener('click', function() {
document.getElementById('clickSound').play();
// Highlight selected slot
document.querySelectorAll('.attachment-btn').forEach(btn => btn.classList.remove('selected'));
this.classList.add('selected');
// In a real app, we would filter/sort available attachments based on the slot
});
});
// Attachment selection
document.querySelectorAll('.attachment-option').forEach(option => {
option.addEventListener('click', function() {
document.getElementById('clickSound').play();
// Highlight selected option
document.querySelectorAll('.attachment-option').forEach(opt => opt.classList.remove('selected'));
this.classList.add('selected');
// In a real app, we would update the gun model with the selected attachment
});
});
// Reset buttons
document.getElementById('reset-colors').addEventListener('click', function() {
document.getElementById('clickSound').play();
resetColors();
});
document.getElementById('reset-parts').addEventListener('click', function() {
document.getElementById('clickSound').play();
resetParts();
});
// Save design
document.getElementById('save-design').addEventListener('click', function() {
document.getElementById('clickSound').play();
saveDesign();
});
// Music control
document.getElementById('music-control').addEventListener('click', function() {
const music = document.getElementById('lofiMusic');
if (music.paused) {
music.play();
this.innerHTML = '<i class="fas fa-music text-white text-xl"></i>';
} else {
music.pause();
this.innerHTML = '<i class="fas fa-volume-mute text-white text-xl"></i>';
}
});
}
function resetColors() {
// Reset to default colors
gunConfig.colors = {
frame: '#222',
slide: '#777',
grip: '#333',
barrel: '#444',
trigger: '#888'
};
// Update UI
document.querySelectorAll('.color-swatch').forEach(swatch => {
swatch.classList.remove('selected');
if (swatch.getAttribute('data-color') === '#222') {
swatch.classList.add('selected');
}
});
// In a real app, we would update the gun model materials
}
function resetParts() {
// Reset to default parts
gunConfig.parts = {
barrel: 'standard',
slide: 'standard',
grip: 'standard',
trigger: 'standard',
magazine: 'standard'
};
// Update UI
document.querySelectorAll('.part-option').forEach(option => {
option.classList.remove('selected');
if (option.getAttribute('data-option') === 'standard') {
option.classList.add('selected');
}
});
// In a real app, we would update the gun model
}
function saveDesign() {
// In a real app, this would save the configuration to localStorage or server
alert('Design saved! (This would actually save in a real implementation)');
console.log('Current gun configuration:', gunConfig);
}
// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
initThreeJS();
setupEventListeners();
animate();
// Start with music paused
document.getElementById('lofiMusic').pause();
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=bsd121/3d-gun-builder" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>