|
|
| document.addEventListener('DOMContentLoaded', () => { |
| |
| const app = new PIXI.Application({ |
| width: 800, |
| height: 600, |
| backgroundColor: 0x111111, |
| view: document.getElementById('previewContainer') |
| }); |
|
|
| |
| const setupFileUpload = () => { |
| const dropZone = document.getElementById('dropZone'); |
| const fileInput = document.getElementById('fileInput'); |
| const uploadBtn = document.getElementById('uploadBtn'); |
| const resetBtn = document.getElementById('zoomResetBtn'); |
| |
| |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
| dropZone.addEventListener(eventName, preventDefaults, false); |
| }); |
| |
| function preventDefaults(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| } |
| |
| function highlight() { |
| dropZone.classList.add('border-indigo-400'); |
| } |
| |
| function unhighlight() { |
| dropZone.classList.remove('border-indigo-400'); |
| } |
| |
| ['dragenter', 'dragover'].forEach(eventName => { |
| dropZone.addEventListener(eventName, highlight, false); |
| }); |
| |
| ['dragleave', 'drop'].forEach(eventName => { |
| dropZone.addEventListener(eventName, unhighlight, false); |
| }); |
| |
| dropZone.addEventListener('drop', handleDrop, false); |
| |
| function handleDrop(e) { |
| const files = e.dataTransfer.files; |
| handleFiles(files); |
| } |
| |
| uploadBtn.addEventListener('click', () => fileInput.click()); |
| fileInput.addEventListener('change', () => handleFiles(fileInput.files)); |
| |
| function handleFiles(files) { |
| if (!files.length) return; |
| const file = files[0]; |
| if (!file.type.startsWith('image/')) { |
| alert('Please upload an image file.'); |
| return; |
| } |
| |
| const reader = new FileReader(); |
| reader.onload = async (e) => { |
| await loadCharacter(e.target.result); |
| }; |
| reader.readAsDataURL(file); |
| } |
| |
| resetBtn.addEventListener('click', () => { |
| app.stage.removeChildren(); |
| document.querySelector('#previewContainer p').style.display = 'block'; |
| }); |
| }; |
|
|
| |
| const loadCharacter = async (imageSrc) => { |
| try { |
| |
| const texture = await PIXI.Texture.fromURL(imageSrc); |
| const sprite = new PIXI.Sprite(texture); |
| |
| |
| sprite.anchor.set(0.5); |
| sprite.x = app.screen.width / 2; |
| sprite.y = app.screen.height / 2; |
| sprite.scale.set(0.5); |
| |
| app.stage.addChild(sprite); |
| document.querySelector('#previewContainer p').style.display = 'none'; |
| |
| |
| initRiggingControls(sprite); |
| |
| } catch (error) { |
| console.error('Error loading character:', error); |
| alert('Failed to load character image'); |
| } |
| }; |
|
|
| |
| const initRiggingControls = (sprite) => { |
| |
| document.getElementById('zoomInBtn').addEventListener('click', () => { |
| sprite.scale.x *= 1.1; |
| sprite.scale.y *= 1.1; |
| }); |
| |
| document.getElementById('zoomOutBtn').addEventListener('click', () => { |
| sprite.scale.x = Math.max(0.1, sprite.scale.x * 0.9); |
| sprite.scale.y = Math.max(0.1, sprite.scale.y * 0.9); |
| }); |
| |
| |
| document.querySelectorAll('.pivot-control input').forEach(input => { |
| input.addEventListener('input', (e) => { |
| const part = e.target.dataset.part; |
| const axis = e.target.dataset.axis; |
| |
| |
| }); |
| }); |
| |
| |
| document.getElementById('autoPivotsBtn').addEventListener('click', async () => { |
| try { |
| |
| const response = await fetch('https://api.posenet.com/detect', { |
| method: 'POST', |
| body: JSON.stringify({ image: sprite.texture.baseTexture.source.src }) |
| }); |
| const data = await response.json(); |
| |
| console.log('Detected pivots:', data); |
| } catch (error) { |
| console.error('Pivot detection failed:', error); |
| } |
| }); |
| }; |
|
|
| |
| setupFileUpload(); |
| |
| async function initPixi(imageSrc) { |
| try { |
| console.log('Initializing character rigging'); |
| |
| |
| const formData = new FormData(); |
| const blob = await fetch(imageSrc).then(r => r.blob()); |
| formData.append('image', blob, 'character.png'); |
| formData.append('name', document.getElementById('charName').value || 'Unnamed'); |
| formData.append('type', document.getElementById('charType').value); |
| |
| |
| const response = await fetch('/api/upload', { |
| method: 'POST', |
| body: formData |
| }); |
| |
| if (!response.ok) { |
| throw new Error(`API Error: ${response.status}`); |
| } |
| |
| const data = await response.json(); |
| console.log('Character uploaded:', data); |
| |
| |
| const app = new PIXI.Application({ |
| width: 600, |
| height: 600, |
| transparent: true, |
| view: document.getElementById('previewContainer') |
| }); |
| |
| const sprite = PIXI.Sprite.from(imageSrc); |
| sprite.anchor.set(0.5); |
| sprite.x = app.screen.width / 2; |
| sprite.y = app.screen.height / 2; |
| app.stage.addChild(sprite); |
| |
| |
| previewContainer.dataset.characterId = data.id; |
| |
| } catch (error) { |
| console.error('Rigging initialization failed:', error); |
| alert('Failed to initialize rigging. Please try again.'); |
| } |
| } |
| |
| |
| document.querySelectorAll('[data-api-call]').forEach(btn => { |
| btn.addEventListener('click', async () => { |
| const endpoint = btn.dataset.apiCall; |
| try { |
| const charId = previewContainer.dataset.characterId; |
| if (!charId) throw new Error('No character loaded'); |
| |
| const response = await fetch(`/api/${endpoint}`, { |
| method: 'POST', |
| headers: {'Content-Type': 'application/json'}, |
| body: JSON.stringify({ character_id: charId }) |
| }); |
| |
| if (!response.ok) throw new Error(await response.text()); |
| const result = await response.json(); |
| console.log(`${endpoint} success:`, result); |
| |
| } catch (error) { |
| console.error(`${endpoint} failed:`, error); |
| alert(`API Error: ${error.message}`); |
| } |
| }); |
| }); |
| |
| console.log('BoneWizardry 3000 Unleashed initialized'); |
| }); |