sevenx_engine 0.2.11

Engine de jogos 2D/3D completa com suporte Android, física, áudio, partículas, tilemap, UI, eventos e sistema 3D avançado com PBR.
Documentation
// SevenX Engine - Android Visual Demo
// Versão com renderização visual usando wgpu

#[cfg(target_os = "android")]
use android_activity::{AndroidApp, InputStatus, MainEvent, PollEvent};

#[cfg(target_os = "android")]
use std::sync::Arc;
#[cfg(target_os = "android")]
use std::time::{Duration, Instant};

#[cfg(target_os = "android")]
struct GameState {
    surface: wgpu::Surface<'static>,
    device: wgpu::Device,
    queue: wgpu::Queue,
    config: wgpu::SurfaceConfiguration,
    size: (u32, u32),
    score: u32,
    touches: Vec<(f32, f32)>,
    bg_color: [f32; 4],
}

#[cfg(target_os = "android")]
impl GameState {
    async fn new(window: Arc<dyn raw_window_handle::HasWindowHandle + raw_window_handle::HasDisplayHandle + Send + Sync>) -> Self {
        let size = (1080, 2400); // Tamanho padrão, será atualizado
        
        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
            backends: wgpu::Backends::all(),
            ..Default::default()
        });
        
        let surface = instance.create_surface(window).unwrap();
        
        let adapter = instance
            .request_adapter(&wgpu::RequestAdapterOptions {
                power_preference: wgpu::PowerPreference::default(),
                compatible_surface: Some(&surface),
                force_fallback_adapter: false,
            })
            .await
            .unwrap();
        
        let (device, queue) = adapter
            .request_device(
                &wgpu::DeviceDescriptor {
                    required_features: wgpu::Features::empty(),
                    required_limits: wgpu::Limits::downlevel_webgl2_defaults(),
                    label: None,
                    memory_hints: Default::default(),
                },
                None,
            )
            .await
            .unwrap();
        
        let surface_caps = surface.get_capabilities(&adapter);
        let surface_format = surface_caps
            .formats
            .iter()
            .find(|f| f.is_srgb())
            .copied()
            .unwrap_or(surface_caps.formats[0]);
        
        let config = wgpu::SurfaceConfiguration {
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
            format: surface_format,
            width: size.0,
            height: size.1,
            present_mode: surface_caps.present_modes[0],
            alpha_mode: surface_caps.alpha_modes[0],
            view_formats: vec![],
            desired_maximum_frame_latency: 2,
        };
        
        surface.configure(&device, &config);
        
        log::info!("✅ WGPU initialized! {}x{}", size.0, size.1);
        
        Self {
            surface,
            device,
            queue,
            config,
            size,
            score: 0,
            touches: Vec::new(),
            bg_color: [0.1, 0.1, 0.2, 1.0],
        }
    }
    
    fn resize(&mut self, new_size: (u32, u32)) {
        if new_size.0 > 0 && new_size.1 > 0 {
            self.size = new_size;
            self.config.width = new_size.0;
            self.config.height = new_size.1;
            self.surface.configure(&self.device, &self.config);
            log::info!("📐 Resized to {}x{}", new_size.0, new_size.1);
        }
    }
    
    fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
        let output = self.surface.get_current_texture()?;
        let view = output
            .texture
            .create_view(&wgpu::TextureViewDescriptor::default());
        
        let mut encoder = self
            .device
            .create_command_encoder(&wgpu::CommandEncoderDescriptor {
                label: Some("Render Encoder"),
            });
        
        {
            let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                label: Some("Render Pass"),
                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                    view: &view,
                    resolve_target: None,
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(wgpu::Color {
                            r: self.bg_color[0] as f64,
                            g: self.bg_color[1] as f64,
                            b: self.bg_color[2] as f64,
                            a: self.bg_color[3] as f64,
                        }),
                        store: wgpu::StoreOp::Store,
                    },
                })],
                depth_stencil_attachment: None,
                occlusion_query_set: None,
                timestamp_writes: None,
            });
        }
        
        self.queue.submit(std::iter::once(encoder.finish()));
        output.present();
        
        Ok(())
    }
}

#[cfg(target_os = "android")]
#[unsafe(no_mangle)]
fn android_main(app: AndroidApp) {
    android_logger::init_once(
        android_logger::Config::default()
            .with_max_level(log::LevelFilter::Info)
            .with_tag("SevenX"),
    );
    
    log::info!("🤖 SevenX Android Visual Demo - Iniciando!");
    
    let mut game_state: Option<GameState> = None;
    let mut last_update = Instant::now();
    let mut running = true;
    let mut window_ready = false;
    
    // Loop principal
    while running {
        app.poll_events(Some(Duration::from_millis(16)), |event| {
            match event {
                PollEvent::Timeout => {
                    if !window_ready || game_state.is_none() {
                        return;
                    }
                    
                    // Update
                    let now = Instant::now();
                    let _dt = now.duration_since(last_update).as_secs_f32();
                    last_update = now;
                    
                    if let Some(state) = &mut game_state {
                        // Muda cor baseado no score
                        let hue = (state.score as f32 * 0.01) % 1.0;
                        state.bg_color = [
                            (hue * 6.28).sin() * 0.3 + 0.3,
                            ((hue + 0.33) * 6.28).sin() * 0.3 + 0.3,
                            ((hue + 0.66) * 6.28).sin() * 0.3 + 0.3,
                            1.0,
                        ];
                        
                        // Render
                        match state.render() {
                            Ok(_) => {}
                            Err(wgpu::SurfaceError::Lost) => state.resize(state.size),
                            Err(wgpu::SurfaceError::OutOfMemory) => {
                                log::error!("Out of memory!");
                                running = false;
                            }
                            Err(e) => log::warn!("Render error: {:?}", e),
                        }
                    }
                }
                
                PollEvent::Main(main_event) => {
                    match main_event {
                        MainEvent::InitWindow { .. } => {
                            log::info!("✅ Window initialized!");
                            
                            // Criar surface usando ndk-context
                            let window = ndk_context::android_context().native_window();
                            if let Some(window) = window {
                                let width = unsafe { ndk::native_window::NativeWindow::from_ptr(window).width() } as u32;
                                let height = unsafe { ndk::native_window::NativeWindow::from_ptr(window).height() } as u32;
                                
                                log::info!("📐 Window size: {}x{}", width, height);
                                
                                // Criar wrapper para raw-window-handle
                                struct AndroidWindow {
                                    window: *mut std::ffi::c_void,
                                }
                                
                                unsafe impl raw_window_handle::HasWindowHandle for AndroidWindow {
                                    fn window_handle(&self) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
                                        let handle = raw_window_handle::AndroidNdkWindowHandle::new(
                                            std::ptr::NonNull::new(self.window).unwrap()
                                        );
                                        Ok(unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.into()) })
                                    }
                                }
                                
                                unsafe impl raw_window_handle::HasDisplayHandle for AndroidWindow {
                                    fn display_handle(&self) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
                                        let handle = raw_window_handle::AndroidDisplayHandle::new();
                                        Ok(unsafe { raw_window_handle::DisplayHandle::borrow_raw(handle.into()) })
                                    }
                                }
                                
                                unsafe impl Send for AndroidWindow {}
                                unsafe impl Sync for AndroidWindow {}
                                
                                let android_window = Arc::new(AndroidWindow { window });
                                
                                // Inicializar wgpu
                                game_state = Some(pollster::block_on(GameState::new(android_window)));
                                
                                if let Some(state) = &mut game_state {
                                    state.resize((width, height));
                                }
                                
                                window_ready = true;
                                log::info!("🎨 Renderização inicializada!");
                            }
                        }
                        
                        MainEvent::TerminateWindow { .. } => {
                            log::info!("❌ Window terminated");
                            window_ready = false;
                            game_state = None;
                        }
                        
                        MainEvent::WindowResized { .. } => {
                            log::info!("📐 Window resized");
                        }
                        
                        MainEvent::InputAvailable { .. } => {
                            if let Some(state) = &mut game_state {
                                state.touches.clear();
                                
                                if let Ok(mut iter) = app.input_events_iter() {
                                    loop {
                                        let read_input = iter.next(|_event| {
                                            state.score += 1;
                                            state.touches.push((100.0, 100.0));
                                            
                                            if state.score % 10 == 0 {
                                                log::info!("👆 Score: {} | Cor mudando!", state.score);
                                            }
                                            
                                            InputStatus::Handled
                                        });
                                        
                                        if !read_input {
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        
                        MainEvent::Destroy => {
                            log::info!("💥 Destroy");
                            if let Some(state) = &game_state {
                                log::info!("Score final: {}", state.score);
                            }
                            running = false;
                        }
                        
                        _ => {}
                    }
                }
                
                _ => {}
            }
        });
    }
    
    log::info!("👋 Encerrando");
}

#[cfg(not(target_os = "android"))]
fn main() {
    println!("❌ Este exemplo só funciona no Android!");
    println!("Execute: cargo apk run --example android_visual");
}