diff options
author | HampusM <hampus@hampusmat.com> | 2025-10-18 17:04:28 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2025-10-18 17:04:28 +0200 |
commit | 7083a19bf1029bff21a9550d40cc3260e99aac53 (patch) | |
tree | 524a8bd2e75ca712b0536218089804cf9838553b /engine/src/renderer/opengl.rs | |
parent | 7f3072ed7e016dff359439d7580403e36ad6b325 (diff) |
refactor(engine): use winit instead of glfw
Diffstat (limited to 'engine/src/renderer/opengl.rs')
-rw-r--r-- | engine/src/renderer/opengl.rs | 1420 |
1 files changed, 1080 insertions, 340 deletions
diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs index b48bac5..fb7dfbe 100644 --- a/engine/src/renderer/opengl.rs +++ b/engine/src/renderer/opengl.rs @@ -1,84 +1,101 @@ //! OpenGL renderer. +use std::any::type_name; use std::collections::HashMap; -use std::ffi::{c_void, CString}; +use std::ffi::CString; use std::io::{Error as IoError, ErrorKind as IoErrorKind}; use std::path::Path; -use std::process::abort; use ecs::actions::Actions; -use ecs::component::local::Local; use ecs::component::Handle as ComponentHandle; -use ecs::phase::START as START_PHASE; +use ecs::entity::obtainer::Obtainer as EntityObtainer; +use ecs::event::component::{Changed, Removed}; +use ecs::pair::{ChildOf, Pair, Wildcard}; +use ecs::phase::Phase; use ecs::query::term::Without; use ecs::sole::Single; -use ecs::system::initializable::Initializable; -use ecs::system::Into as _; -use ecs::{Component, Query}; - -use crate::asset::{Assets, Id as AssetId}; -use crate::camera::{Active as ActiveCamera, Camera}; -use crate::color::Color; -use crate::data_types::dimens::Dimens; -use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig}; -use crate::image::{ColorType as ImageColorType, Image}; -use crate::lighting::{DirectionalLight, GlobalLight, PointLight}; -use crate::material::{Flags as MaterialFlags, Material}; -use crate::matrix::Matrix; -use crate::mesh::Mesh; -use crate::model::Model; -use crate::opengl::buffer::{Buffer, Usage as BufferUsage}; -use crate::opengl::debug::{ - enable_debug_output, +use ecs::system::observer::Observe; +use ecs::{declare_entity, Component, Query}; +use glutin::display::GetGlDisplay; +use glutin::prelude::GlDisplay; +use glutin::surface::GlSurface; +use opengl_bindings::debug::{ set_debug_message_callback, set_debug_message_control, MessageIdsAction, MessageSeverity, MessageSource, MessageType, + SetDebugMessageControlError as GlSetDebugMessageControlError, }; -use crate::opengl::glsl::{ - preprocess as glsl_preprocess, - PreprocessingError as GlslPreprocessingError, +use opengl_bindings::misc::{ + clear_buffers, + enable, + set_enabled, + BufferClearMask, + Capability, + SetViewportError as GlSetViewportError, }; -use crate::opengl::shader::{ +use opengl_bindings::shader::{ Error as GlShaderError, Kind as ShaderKind, Program as GlShaderProgram, Shader as GlShader, }; -use crate::opengl::texture::{ +use opengl_bindings::texture::{ Filtering as GlTextureFiltering, + GenerateError as GlTextureGenerateError, PixelDataFormat as GlTexturePixelDataFormat, Texture as GlTexture, Wrapping as GlTextureWrapping, }; -use crate::opengl::vertex_array::{ - DataType as VertexArrayDataType, +use opengl_bindings::vertex_array::{ + DrawError as GlDrawError, PrimitiveKind, VertexArray, }; -use crate::opengl::{ - clear_buffers, - enable, - get_context_flags as get_opengl_context_flags, - BufferClearMask, - Capability, - ContextFlags, +use opengl_bindings::{ContextWithFns, CurrentContextWithFns}; +use safer_ffi::layout::ReprC; + +use crate::asset::{Assets, Id as AssetId}; +use crate::camera::{Active as ActiveCamera, Camera}; +use crate::color::Color; +use crate::data_types::dimens::Dimens; +use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig}; +use crate::image::{ColorType as ImageColorType, Image}; +use crate::lighting::{DirectionalLight, GlobalLight, PointLight}; +use crate::material::{Flags as MaterialFlags, Material}; +use crate::matrix::Matrix; +use crate::model::Model; +use crate::opengl::glsl::{ + preprocess as glsl_preprocess, + PreprocessingError as GlslPreprocessingError, }; use crate::projection::{ClipVolume, Projection}; -use crate::renderer::opengl::vertex::{AttributeComponentType, Vertex}; -use crate::renderer::RENDER_PHASE; +use crate::renderer::opengl::glutin_compat::{ + DisplayBuilder, + Error as GlutinCompatError, +}; +use crate::renderer::opengl::graphics_mesh::GraphicsMesh; +use crate::renderer::{GraphicsProperties, RENDER_PHASE}; use crate::texture::{ Filtering as TextureFiltering, Properties as TextureProperties, Wrapping as TextureWrapping, }; use crate::transform::{Scale, WorldPosition}; -use crate::util::{defer, Defer, RefOrValue}; +use crate::util::MapVec; use crate::vector::{Vec2, Vec3}; -use crate::window::Window; +use crate::windowing::window::{ + Closed as WindowClosed, + CreationAttributes as WindowCreationAttributes, + CreationReady, + Window, +}; +use crate::windowing::Context as WindowingContext; +mod glutin_compat; +mod graphics_mesh; mod vertex; const AMBIENT_MAP_TEXTURE_UNIT: u32 = 0; @@ -91,9 +108,66 @@ type RenderableEntity<'a> = ( Option<&'a WorldPosition>, Option<&'a Scale>, Option<&'a DrawFlags>, - Option<&'a GlObjects>, + &'a [Pair<DataInGraphicsContext, Wildcard>], +); + +declare_entity!( + pub POST_RENDER_PHASE, + (Phase, Pair::builder().relation::<ChildOf>().target_id(*RENDER_PHASE).build()) ); +#[derive(Debug, Component)] +struct WithGraphicsContext; + +#[derive(Debug, Component)] +struct WindowGlConfig +{ + gl_config: glutin::config::Config, +} + +#[derive(Debug, Component)] +struct WindowGraphicsSurface +{ + surface: glutin::surface::Surface<glutin::surface::WindowSurface>, +} + +#[derive(Component)] +struct GraphicsContext +{ + context: ContextWithFns, + shader_program: Option<GlShaderProgram>, + textures_objs: HashMap<AssetId, GlTexture>, + default_1x1_texture_obj: Option<GlTexture>, + graphics_mesh_store: GraphicsMeshStore, +} + +#[derive(Debug, Default)] +struct GraphicsMeshStore +{ + graphics_meshes: MapVec<GraphicsMeshId, GraphicsMesh>, + next_id: GraphicsMeshId, +} + +impl GraphicsMeshStore +{ + fn insert(&mut self, graphics_mesh: GraphicsMesh) -> GraphicsMeshId + { + let id = self.next_id; + + self.graphics_meshes.insert(id, graphics_mesh); + + self.next_id.inner += 1; + + id + } +} + +#[derive(Debug, Component)] +struct DataInGraphicsContext +{ + graphics_mesh_id: GraphicsMeshId, +} + #[derive(Debug, Default)] #[non_exhaustive] pub struct Extension {} @@ -103,253 +177,877 @@ impl ecs::extension::Extension for Extension fn collect(self, mut collector: ecs::extension::Collector<'_>) { collector.add_declared_entity(&RENDER_PHASE); + collector.add_declared_entity(&POST_RENDER_PHASE); - collector.add_system(*START_PHASE, initialize); + collector.add_system(*RENDER_PHASE, render); - collector.add_system( - *RENDER_PHASE, - render - .into_system() - .initialize((GlobalGlObjects::default(),)), - ); + collector.add_system(*POST_RENDER_PHASE, prepare_windows); + collector.add_system(*POST_RENDER_PHASE, init_window_graphics); + + collector.add_observer(handle_model_removed); + + collector.add_observer(handle_window_changed); + collector.add_observer(handle_window_removed); } } -fn initialize(window: Single<Window>) +#[tracing::instrument(skip_all)] +fn handle_model_removed(observe: Observe<Pair<Removed, Model>>, mut actions: Actions) { - window - .make_context_current() - .expect("Failed to make window context current"); + for evt_match in &observe { + let ent_id = evt_match.id(); - gl::load_with(|symbol| match window.get_proc_address(symbol) { - Ok(addr) => addr as *const c_void, - Err(err) => { - println!( - "FATAL ERROR: Failed to get adress of OpenGL function {symbol}: {err}", - ); + tracing::debug!(entity_id=%ent_id, "Cleaning up after model"); - abort(); - } - }); + let ent = evt_match.get_ent_infallible(); - if get_opengl_context_flags().contains(ContextFlags::DEBUG) { - initialize_debug(); + for data_in_graphics_ctx_pair in + ent.get_wildcard_pair_matches::<DataInGraphicsContext, Wildcard>() + { + actions.remove_components(ent_id, [data_in_graphics_ctx_pair.id()]); + + let Some(graphics_context_ent) = data_in_graphics_ctx_pair.get_target_ent() + else { + tracing::trace!( + concat!( + "Graphics context referenced by pair ({}, {}) does not exist. ", + "Skipping cleanup of this model" + ), + type_name::<DataInGraphicsContext>(), + data_in_graphics_ctx_pair.id().target_entity() + ); + + continue; + }; + + let Some(data_in_graphics_ctx) = + data_in_graphics_ctx_pair.get_data_as_relation() + else { + unreachable!(); + }; + + let Some(mut graphics_context) = + graphics_context_ent.get_mut::<GraphicsContext>() + else { + tracing::trace!( + "Graphics context entity {} does not have a {} component", + graphics_context_ent.uid(), + type_name::<GraphicsContext>() + ); + continue; + }; + + graphics_context + .graphics_mesh_store + .graphics_meshes + .remove(data_in_graphics_ctx.graphics_mesh_id); + } } +} + +#[tracing::instrument(skip_all)] +fn handle_window_changed( + observe: Observe<Pair<Changed, Window>>, + entity_obtainer: EntityObtainer, +) +{ + for evt_match in &observe { + let window_ent = evt_match.get_ent_infallible(); + + tracing::trace!( + new_state = ?evt_match.get_changed_comp(), + "Handling window change" + ); + + let Some(window_graphics_surface) = window_ent.get::<WindowGraphicsSurface>() + else { + continue; + }; + + let Some(graphics_context_ent_id) = window_ent + .get_matching_components( + Pair::builder() + .relation::<WithGraphicsContext>() + .target_id(Wildcard::uid()) + .build() + .id(), + ) + .next() + .map(|comp_ref| comp_ref.id().target_entity()) + else { + continue; + }; - let window_size = window.size().expect("Failed to get window size"); + let Some(graphics_context_ent) = + entity_obtainer.get_entity(graphics_context_ent_id) + else { + tracing::error!("Graphics context entity does not exist"); + continue; + }; - set_viewport(Vec2 { x: 0, y: 0 }, window_size); + let Some(graphics_context) = graphics_context_ent.get::<GraphicsContext>() else { + tracing::error!( + "Graphics context entity does not have a GraphicsContext component" + ); + continue; + }; - window.set_framebuffer_size_callback(|new_window_size| { - set_viewport(Vec2::ZERO, new_window_size); - }); + let Ok(current_graphics_context) = graphics_context + .context + .make_current(&window_graphics_surface.surface) + else { + tracing::error!("Failed to make graphics context current"); + continue; + }; - enable(Capability::DepthTest); - enable(Capability::MultiSample); + if let Err(err) = set_viewport( + ¤t_graphics_context, + Vec2::default(), + evt_match.get_changed_comp().inner_size(), + ) { + tracing::error!("Failed to set viewport: {err}"); + } + } } #[tracing::instrument(skip_all)] -#[allow(clippy::too_many_arguments)] -fn render( - query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>, - point_light_query: Query<(&PointLight, &WorldPosition)>, - directional_lights: Query<(&DirectionalLight,)>, - camera_query: Query<(&Camera, &WorldPosition, &ActiveCamera)>, - window: Single<Window>, - global_light: Single<GlobalLight>, - assets: Single<Assets>, - mut gl_objects: Local<GlobalGlObjects>, +fn handle_window_removed(observe: Observe<Pair<Removed, Window>>, mut actions: Actions) +{ + for evt_match in &observe { + let window_ent_id = evt_match.id(); + + let window_ent = evt_match.get_ent_infallible(); + + tracing::debug!( + entity_id = %window_ent_id, + title = %evt_match.get_removed_comp().title, + "Handling removal of window" + ); + + actions.remove_comps::<(WindowGraphicsSurface, WindowGlConfig)>(window_ent_id); + + let Some(with_graphics_ctx_pair_handle) = + window_ent.get_first_wildcard_pair_match::<WithGraphicsContext, Wildcard>() + else { + tracing::warn!("Window entity is missing a (WithGraphicsContext, *) pair"); + continue; + }; + + let graphics_context_ent_id = with_graphics_ctx_pair_handle.id().target_entity(); + + actions.remove_comps::<(GraphicsContext,)>(graphics_context_ent_id); + + actions.remove_components(window_ent_id, [with_graphics_ctx_pair_handle.id()]); + } +} + +#[derive(Debug, Component)] +struct SetupFailed; + +// fn on_window_creation_attrs_added( +// observe: Observe<Pair<Added, WindowCreationAttributes>>, +// windowing: Single<Windowing>, +// window_store: Single<WindowStore>, +// mut actions: Actions, +// ) +// { +// for evt_match in &observe { +// let Some(ent) = evt_match.get_entity() else { +// unreachable!(); +// }; +// +// if ent.has_component(WindowGlConfig::id()) || +// ent.has_component(WindowClosed::id()) || ent.has_component() {} } +// } + +fn prepare_windows( + window_query: Query< + ( + Option<&Window>, + &mut WindowCreationAttributes, + Option<&GraphicsProperties>, + ), + ( + Without<CreationReady>, + Without<WindowGlConfig>, + Without<WindowClosed>, + Without<SetupFailed>, + ), + >, + windowing_context: Single<WindowingContext>, mut actions: Actions, ) { - let Some((camera, camera_world_pos, _)) = camera_query.iter().next() else { - tracing::warn!("No current camera. Nothing will be rendered"); + let Some(display_handle) = windowing_context.display_handle() else { return; }; - let directional_lights = directional_lights.iter().collect::<Vec<_>>(); + for (window_ent_id, (window, mut window_creation_attrs, graphics_props)) in + window_query.iter_with_euids() + { + tracing::debug!("Preparing window entity {window_ent_id} for use in rendering"); - let GlobalGlObjects { - shader_program, - textures: gl_textures, - default_1x1_texture: default_1x1_gl_texture, - } = &mut *gl_objects; + let mut glutin_config_template_builder = + glutin::config::ConfigTemplateBuilder::new(); - let shader_program = - shader_program.get_or_insert_with(|| create_default_shader_program().unwrap()); + let graphics_props = match graphics_props.as_ref() { + Some(graphics_props) => &*graphics_props, + None => { + actions.add_components(window_ent_id, (GraphicsProperties::default(),)); - clear_buffers(BufferClearMask::COLOR | BufferClearMask::DEPTH); + &GraphicsProperties::default() + } + }; - 'subject_loop: for ( - euid, - (model, material_flags, position, scale, draw_flags, gl_objects), - ) in query.iter_with_euids() + if let Some(multisampling_sample_cnt) = graphics_props.multisampling_sample_cnt { + glutin_config_template_builder = glutin_config_template_builder + .with_multisampling(multisampling_sample_cnt); + } + + let window_handle = match window + .as_ref() + .map(|window| unsafe { + windowing_context.get_window_as_handle(&window.wid()) + }) + .flatten() + .transpose() + { + Ok(window_handle) => window_handle, + Err(err) => { + tracing::error!("Failed to get window handle: {err}"); + actions.add_components(window_ent_id, (SetupFailed,)); + continue; + } + }; + + let (new_window_creation_attrs, gl_config) = match DisplayBuilder::new() + .with_window_attributes(window_creation_attrs.clone()) + .build( + window_handle, + &display_handle, + glutin_config_template_builder, + |mut cfgs| cfgs.next(), + ) { + Ok((new_window_creation_attrs, gl_config)) => { + (new_window_creation_attrs, gl_config) + } + Err(GlutinCompatError::WindowRequired) => { + actions.add_components(window_ent_id, (CreationReady,)); + continue; + } + Err(err) => { + tracing::error!("Failed to create platform graphics display: {err}"); + actions.add_components(window_ent_id, (SetupFailed,)); + continue; + } + }; + + *window_creation_attrs = new_window_creation_attrs; + + // let gl_config_template = glutin_config_template_builder.build(); + // + // let display = match glutin_winit_compat::create_display( + // unsafe { engine_display.as_display_handle() }, + // glutin_winit_compat::ApiPreference::default(), + // None, + // ) { + // Ok(gl_display) => gl_display, + // Err(err) => { + // tracing::error!("Failed to create graphics platform display: {err}"); + // continue; + // } + // }; + // + // let mut gl_configs = match unsafe { display.find_configs(gl_config_template) } + // { Ok(gl_configs) => gl_configs, + // Err(err) => { + // tracing::error!("Failed to find GL configs: {err:?}"); + // continue; + // } + // }; + // + // let Some(first_gl_config) = gl_configs.next() else { + // tracing::error!("No matching GL configuration exists"); + // continue; + // }; + // + // *window_creation_attrs = finalize_window_creation_attrs( + // window_creation_attrs.clone(), + // &first_gl_config, + // ); + + actions.add_components(window_ent_id, (WindowGlConfig { gl_config },)); + + if window.is_none() { + actions.add_components(window_ent_id, (CreationReady,)); + } + } +} + +#[tracing::instrument(skip_all)] +fn init_window_graphics( + window_query: Query< + (&Window, &WindowGlConfig, &GraphicsProperties), + (Without<WindowGraphicsSurface>, Without<SetupFailed>), + >, + mut actions: Actions, + windowing_context: Single<WindowingContext>, +) +{ + for (window_ent_id, (window, window_gl_config, graphics_props)) in + window_query.iter_with_euids() { - let Some(model_data) = assets.get(&model.asset_handle) else { - tracing::trace!("Missing model asset"); + tracing::info!("Initializing graphics for window {window_ent_id}"); + + let display = window_gl_config.gl_config.display(); + + let window_handle = + match unsafe { windowing_context.get_window_as_handle(&window.wid()) } + .transpose() + { + Ok(Some(window_handle)) => window_handle, + Ok(None) => { + tracing::error!( + wid = ?window.wid(), + entity_id = %window_ent_id, + "Windowing context does not contain window" + ); + actions.add_components(window_ent_id, (SetupFailed,)); + continue; + } + Err(err) => { + tracing::error!("Failed to get window handle: {err}"); + actions.add_components(window_ent_id, (SetupFailed,)); + continue; + } + }; + + let Some(window_inner_size) = window.inner_size().try_into_nonzero() else { + tracing::error!( + "Cannot create a surface for a window with a width/height of 0", + ); continue; }; - let material_flags = material_flags - .map(|material_flags| material_flags.clone()) - .unwrap_or_default(); + let surface = match unsafe { + display.create_window_surface( + &window_gl_config.gl_config, + &glutin::surface::SurfaceAttributesBuilder::< + glutin::surface::WindowSurface, + >::new() + .build( + window_handle.as_raw(), + window_inner_size.width, + window_inner_size.height, + ), + ) + } { + Ok(surface) => surface, + Err(err) => { + tracing::error!("Failed to create window surface: {err}"); + continue; + } + }; - let gl_objs = match gl_objects.as_deref() { - Some(gl_objs) => RefOrValue::Ref(gl_objs), - None => RefOrValue::Value(Some(GlObjects::new(&model_data.mesh))), + let context = match unsafe { + display.create_context( + &window_gl_config.gl_config, + &glutin::context::ContextAttributesBuilder::new() + .with_debug(graphics_props.debug) + .build(Some(window_handle.as_raw())), + ) + } { + Ok(context) => context, + Err(err) => { + tracing::error!("Failed to create graphics context: {err}"); + continue; + } }; - defer!(|gl_objs| { - if let RefOrValue::Value(opt_gl_objs) = gl_objs { - actions.add_components(euid, (opt_gl_objs.take().unwrap(),)); - }; - }); + let context = match ContextWithFns::new(context, &surface) { + Ok(context) => context, + Err(err) => { + tracing::error!("Failed to create graphics context: {err}"); + continue; + } + }; - apply_transformation_matrices( - Transformation { - position: position.map(|pos| *pos).unwrap_or_default().position, - scale: scale.map(|scale| *scale).unwrap_or_default().scale, - }, - shader_program, - &camera, - &camera_world_pos, - window.size().expect("Failed to get window size"), - ); + let Ok(current_graphics_context) = context.make_current(&surface) else { + tracing::error!("Failed to make graphics context current"); + continue; + }; - if model_data.materials.len() > 1 { - tracing::warn!(concat!( - "Multiple model materials are not supported ", - "so only the first material will be used" - )); + if let Err(err) = set_viewport( + ¤t_graphics_context, + Vec2 { x: 0, y: 0 }, + window.inner_size(), + ) { + tracing::error!("Failed to set viewport: {err}"); } - let material = match model_data.materials.values().next() { - Some(material) => material, - None => { - tracing::warn!("Model has no materials. Using default material"); + set_enabled( + ¤t_graphics_context, + Capability::DepthTest, + graphics_props.depth_test, + ); - &Material::default() + set_enabled( + ¤t_graphics_context, + Capability::MultiSample, + graphics_props.multisampling_sample_cnt.is_some(), + ); + + if graphics_props.debug { + enable(¤t_graphics_context, Capability::DebugOutput); + enable( + ¤t_graphics_context, + Capability::DebugOutputSynchronous, + ); + + set_debug_message_callback( + ¤t_graphics_context, + opengl_debug_message_cb, + ); + + match set_debug_message_control( + ¤t_graphics_context, + None, + None, + None, + &[], + MessageIdsAction::Disable, + ) { + Ok(()) => {} + Err(GlSetDebugMessageControlError::TooManyIds { + id_cnt: _, + max_id_cnt: _, + }) => { + unreachable!() // No ids are given + } } - }; + } - apply_light( - &material, - &material_flags, - &global_light, - shader_program, - (point_light_query.iter(), point_light_query.iter().count()), - directional_lights - .iter() - .map(|(dir_light,)| &**dir_light) - .collect::<Vec<_>>() - .as_slice(), - &camera_world_pos, + let graphics_context_ent_id = actions.spawn((GraphicsContext { + context, + shader_program: None, + textures_objs: HashMap::new(), + default_1x1_texture_obj: None, + graphics_mesh_store: GraphicsMeshStore::default(), + },)); + + actions.add_components( + window_ent_id, + ( + WindowGraphicsSurface { surface }, + Pair::builder() + .relation::<WithGraphicsContext>() + .target_id(graphics_context_ent_id) + .build(), + ), ); + } +} - let material_texture_maps = [ - (&material.ambient_map, AMBIENT_MAP_TEXTURE_UNIT), - (&material.diffuse_map, DIFFUSE_MAP_TEXTURE_UNIT), - (&material.specular_map, SPECULAR_MAP_TEXTURE_UNIT), - ]; +#[tracing::instrument(skip_all)] +#[allow(clippy::too_many_arguments)] +fn render( + query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>, + point_light_query: Query<(&PointLight, &WorldPosition)>, + directional_lights: Query<(&DirectionalLight,)>, + camera_query: Query<(&Camera, &WorldPosition, &ActiveCamera)>, + window_query: Query<( + &Window, + &WindowGraphicsSurface, + &GraphicsProperties, + Pair<WithGraphicsContext, Wildcard>, + )>, + global_light: Single<GlobalLight>, + assets: Single<Assets>, + mut actions: Actions, +) +{ + for ( + window_ent_id, + (window, window_graphics_surface, window_graphics_props, graphics_context_pair), + ) in window_query.iter_with_euids() + { + let Some(graphics_context_ent) = graphics_context_pair.get_target_ent() else { + tracing::error!("Window's associated graphics context entity does not exist"); + actions.remove_components(window_ent_id, [graphics_context_pair.id()]); + continue; + }; - for (texture, texture_unit) in material_texture_maps { - let Some(texture) = texture else { - let gl_texture = default_1x1_gl_texture.get_or_insert_with(|| { - create_gl_texture( - &Image::from_color(1, Color::WHITE_U8), - &TextureProperties::default(), - ) - }); + let Some(mut graphics_context) = + graphics_context_ent.get_mut::<GraphicsContext>() + else { + tracing::error!( + "Graphics context entity does not have a GraphicsContext component" + ); + return; + }; - gl_texture.bind_to_texture_unit(texture_unit); + let GraphicsContext { + ref context, + ref mut shader_program, + ref mut textures_objs, + ref mut default_1x1_texture_obj, + ref mut graphics_mesh_store, + } = *graphics_context; + + let Some((camera, camera_world_pos, _)) = camera_query.iter().next() else { + tracing::warn!("No current camera. Nothing will be rendered"); + return; + }; + + let Ok(current_graphics_context) = + context.make_current(&window_graphics_surface.surface) + else { + tracing::error!("Failed to make graphics context current"); + continue; + }; + + let directional_lights = directional_lights.iter().collect::<Vec<_>>(); + + let shader_program = shader_program.get_or_insert_with(|| { + create_default_shader_program(¤t_graphics_context).unwrap() + }); + + let mut clear_mask = BufferClearMask::COLOR; + + clear_mask.set(BufferClearMask::DEPTH, window_graphics_props.depth_test); + clear_buffers(¤t_graphics_context, clear_mask); + + for ( + euid, + ( + model, + material_flags, + position, + scale, + draw_flags, + data_in_graphics_ctx_pairs, + ), + ) in query.iter_with_euids() + { + let Some(model_data) = assets.get(&model.asset_handle) else { + tracing::trace!("Missing model asset"); continue; }; - let texture_image_asset_id = texture.asset_handle.id(); + let material_flags = material_flags + .map(|material_flags| material_flags.clone()) + .unwrap_or_default(); + + let graphics_mesh_id = match data_in_graphics_ctx_pairs + .get_with_target_id(graphics_context_ent.uid()) + { + Some(data_in_graphics_ctx_pair) => { + let Some(data_in_graphics_ctx) = + data_in_graphics_ctx_pair.get_data::<DataInGraphicsContext>() + else { + tracing::warn!( + concat!( + "Pair with relation {} ({}) has no data or data with a ", + "wrong type. This pair will be removed" + ), + type_name::<DataInGraphicsContext>(), + data_in_graphics_ctx_pair.id() + ); + + actions.remove_components(euid, [data_in_graphics_ctx_pair.id()]); + continue; + }; - let gl_texture = match gl_textures.get(&texture_image_asset_id) { - Some(gl_texture) => gl_texture, + data_in_graphics_ctx.graphics_mesh_id + } None => { - let Some(image) = assets.get::<Image>(&texture.asset_handle) else { - tracing::trace!("Missing texture asset"); - continue 'subject_loop; + let graphics_mesh = match GraphicsMesh::new( + ¤t_graphics_context, + &model_data.mesh, + ) { + Ok(graphics_mesh) => graphics_mesh, + Err(err) => { + tracing::error!( + "Failed to create {}: {err}", + type_name::<GraphicsMesh>() + ); + + // This system should not try again + actions.add_components(euid, (NoDraw,)); + + continue; + } }; - gl_textures.insert( - texture_image_asset_id, - create_gl_texture(image, &texture.properties), + let graphics_mesh_id = graphics_mesh_store.insert(graphics_mesh); + + actions.add_components( + euid, + (Pair::builder() + .relation_as_data(DataInGraphicsContext { graphics_mesh_id }) + .target_id(graphics_context_ent.uid()) + .build(),), ); - gl_textures - .get(&texture.asset_handle.id()) - .expect("Not possible") + graphics_mesh_id } }; - gl_texture.bind_to_texture_unit(texture_unit); - } - - shader_program.activate(); + let Some(graphics_mesh) = + graphics_mesh_store.graphics_meshes.get(&graphics_mesh_id) + else { + tracing::error!("Graphics mesh with ID: {graphics_mesh_id:?} not found"); + continue; + }; - if let Some(draw_flags) = &draw_flags { - crate::opengl::set_polygon_mode( - draw_flags.polygon_mode_config.face, - draw_flags.polygon_mode_config.mode, + apply_transformation_matrices( + ¤t_graphics_context, + Transformation { + position: position.map(|pos| *pos).unwrap_or_default().position, + scale: scale.map(|scale| *scale).unwrap_or_default().scale, + }, + shader_program, + &camera, + &camera_world_pos, + window.inner_size(), ); - } - draw_mesh(gl_objs.get().unwrap()); + if model_data.materials.len() > 1 { + tracing::warn!(concat!( + "Multiple model materials are not supported ", + "so only the first material will be used" + )); + } - if draw_flags.is_some() { - let default_polygon_mode_config = PolygonModeConfig::default(); + let material = match model_data.materials.values().next() { + Some(material) => material, + None => { + tracing::warn!("Model has no materials. Using default material"); - crate::opengl::set_polygon_mode( - default_polygon_mode_config.face, - default_polygon_mode_config.mode, + &Material::default() + } + }; + + apply_light( + ¤t_graphics_context, + &material, + &material_flags, + &global_light, + shader_program, + (point_light_query.iter(), point_light_query.iter().count()), + directional_lights + .iter() + .map(|(dir_light,)| &**dir_light) + .collect::<Vec<_>>() + .as_slice(), + &camera_world_pos, ); + + match create_bind_material_textures( + ¤t_graphics_context, + &material, + &assets, + textures_objs, + default_1x1_texture_obj, + ) { + Ok(()) => {} + Err(CreateBindMaterialTexturesError::MissingTextureAsset) => { + continue; + } + Err( + err @ CreateBindMaterialTexturesError::CreateTextureFailed { .. }, + ) => { + tracing::error!( + "Creating &/ binding material textures failed: {err}" + ); + + // This system should not try again + actions.add_components(euid, (NoDraw,)); + + continue; + } + } + + shader_program.activate(¤t_graphics_context); + + if let Some(draw_flags) = &draw_flags { + opengl_bindings::misc::set_polygon_mode( + ¤t_graphics_context, + draw_flags.polygon_mode_config.face, + draw_flags.polygon_mode_config.mode, + ); + } + + if let Err(err) = draw_mesh(¤t_graphics_context, &graphics_mesh) { + tracing::error!( + entity_id = %euid, + graphics_context_entity_id = %graphics_context_ent.uid(), + "Failed to draw mesh: {err}", + ); + + // This system should not try again + actions.add_components(euid, (NoDraw,)); + + continue; + }; + + if draw_flags.is_some() { + let default_polygon_mode_config = PolygonModeConfig::default(); + + opengl_bindings::misc::set_polygon_mode( + ¤t_graphics_context, + default_polygon_mode_config.face, + default_polygon_mode_config.mode, + ); + } + } + + if let Err(err) = window_graphics_surface + .surface + .swap_buffers(context.context()) + { + tracing::error!("Failed to swap buffers: {err}"); } } } -#[derive(Debug, Default, Component)] -struct GlobalGlObjects +fn create_default_texture(current_context: &CurrentContextWithFns<'_>) -> GlTexture { - shader_program: Option<GlShaderProgram>, - textures: HashMap<AssetId, GlTexture>, - default_1x1_texture: Option<GlTexture>, + match create_gl_texture( + current_context, + &Image::from_color(Dimens { width: 1, height: 1 }, Color::WHITE_U8), + &TextureProperties::default(), + ) { + Ok(gl_texture) => gl_texture, + Err( + GlTextureGenerateError::SizeWidthValueTooLarge { value: _, max_value: _ } + | GlTextureGenerateError::SizeHeightValueTooLarge { value: _, max_value: _ }, + ) => unreachable!(), + } } -fn set_viewport(position: Vec2<u32>, size: Dimens<u32>) +fn create_bind_material_textures( + current_context: &CurrentContextWithFns<'_>, + material: &Material, + assets: &Assets, + texture_objs: &mut HashMap<AssetId, GlTexture>, + default_1x1_texture_obj: &mut Option<GlTexture>, +) -> Result<(), CreateBindMaterialTexturesError> { - crate::opengl::set_viewport(position, size); + let material_texture_maps = [ + (&material.ambient_map, AMBIENT_MAP_TEXTURE_UNIT), + (&material.diffuse_map, DIFFUSE_MAP_TEXTURE_UNIT), + (&material.specular_map, SPECULAR_MAP_TEXTURE_UNIT), + ]; + + for (texture, texture_unit) in material_texture_maps { + let Some(texture) = texture else { + let gl_texture = default_1x1_texture_obj + .get_or_insert_with(|| create_default_texture(current_context)); + + gl_texture.bind_to_texture_unit(current_context, texture_unit); + + continue; + }; + + let texture_image_asset_id = texture.asset_handle.id(); + + let gl_texture = match texture_objs.get(&texture_image_asset_id) { + Some(gl_texture) => gl_texture, + None => { + let Some(image) = assets.get::<Image>(&texture.asset_handle) else { + tracing::trace!(handle=?texture.asset_handle, "Missing texture asset"); + return Err(CreateBindMaterialTexturesError::MissingTextureAsset); + }; + + texture_objs.entry(texture_image_asset_id).or_insert( + create_gl_texture(current_context, image, &texture.properties) + .map_err(|err| { + CreateBindMaterialTexturesError::CreateTextureFailed { + err, + image_asset_id: texture_image_asset_id, + } + })?, + ) + } + }; + + gl_texture.bind_to_texture_unit(current_context, texture_unit); + } + + Ok(()) } -fn initialize_debug() +#[derive(Debug, thiserror::Error)] +enum CreateBindMaterialTexturesError { - enable_debug_output(); + #[error("Missing texture asset")] + MissingTextureAsset, - set_debug_message_callback(opengl_debug_message_cb); - set_debug_message_control(None, None, None, &[], MessageIdsAction::Disable); + #[error("Failed to create texture from image asset with ID {image_asset_id:?}")] + CreateTextureFailed + { + #[source] + err: GlTextureGenerateError, + image_asset_id: AssetId, + }, } -fn draw_mesh(gl_objects: &GlObjects) +fn set_viewport( + current_context: &CurrentContextWithFns<'_>, + position: Vec2<u32>, + size: &Dimens<u32>, +) -> Result<(), GlSetViewportError> { - gl_objects.vertex_arr.bind(); + let position = + opengl_bindings::data_types::Vec2::<u32> { x: position.x, y: position.y }; - if gl_objects.index_buffer.is_some() { - VertexArray::draw_elements(PrimitiveKind::Triangles, 0, gl_objects.element_cnt); + let size = opengl_bindings::data_types::Dimens::<u32> { + width: size.width, + height: size.height, + }; + + opengl_bindings::misc::set_viewport(current_context, &position, &size) +} + +fn draw_mesh( + current_context: &CurrentContextWithFns<'_>, + graphics_mesh: &GraphicsMesh, +) -> Result<(), GlDrawError> +{ + graphics_mesh.vertex_arr.bind(current_context); + + if graphics_mesh.index_buffer.is_some() { + VertexArray::draw_elements( + current_context, + PrimitiveKind::Triangles, + 0, + graphics_mesh.element_cnt, + )?; } else { - VertexArray::draw_arrays(PrimitiveKind::Triangles, 0, gl_objects.element_cnt); + VertexArray::draw_arrays( + current_context, + PrimitiveKind::Triangles, + 0, + graphics_mesh.element_cnt, + )?; } + + Ok(()) } -fn create_gl_texture(image: &Image, texture_properties: &TextureProperties) -> GlTexture +fn create_gl_texture( + current_context: &CurrentContextWithFns<'_>, + image: &Image, + texture_properties: &TextureProperties, +) -> Result<GlTexture, GlTextureGenerateError> { - let mut gl_texture = GlTexture::new(); + let gl_texture = GlTexture::new(current_context); gl_texture.generate( - image.dimensions(), + current_context, + &image.dimensions().into(), image.as_bytes(), match image.color_type() { ImageColorType::Rgb8 => GlTexturePixelDataFormat::Rgb8, @@ -358,19 +1056,24 @@ fn create_gl_texture(image: &Image, texture_properties: &TextureProperties) -> G unimplemented!(); } }, - ); + )?; - gl_texture.set_wrap(texture_wrapping_to_gl(texture_properties.wrap)); + gl_texture.set_wrap( + current_context, + texture_wrapping_to_gl(texture_properties.wrap), + ); - gl_texture.set_magnifying_filter(texture_filtering_to_gl( - texture_properties.magnifying_filter, - )); + gl_texture.set_magnifying_filter( + current_context, + texture_filtering_to_gl(texture_properties.magnifying_filter), + ); - gl_texture.set_minifying_filter(texture_filtering_to_gl( - texture_properties.minifying_filter, - )); + gl_texture.set_minifying_filter( + current_context, + texture_filtering_to_gl(texture_properties.minifying_filter), + ); - gl_texture + Ok(gl_texture) } const VERTEX_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex.glsl"); @@ -379,32 +1082,34 @@ const FRAGMENT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/fragment.glsl") const VERTEX_DATA_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex_data.glsl"); const LIGHT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/light.glsl"); -fn create_default_shader_program() -> Result<GlShaderProgram, CreateShaderError> +fn create_default_shader_program( + current_context: &CurrentContextWithFns<'_>, +) -> Result<GlShaderProgram, CreateShaderError> { - let mut vertex_shader = GlShader::new(ShaderKind::Vertex); + let vertex_shader = GlShader::new(current_context, ShaderKind::Vertex); - vertex_shader.set_source(&*glsl_preprocess( - VERTEX_GLSL_SHADER_SRC, - &get_glsl_shader_content, - )?)?; + vertex_shader.set_source( + current_context, + &*glsl_preprocess(VERTEX_GLSL_SHADER_SRC, &get_glsl_shader_content)?, + )?; - vertex_shader.compile()?; + vertex_shader.compile(current_context)?; - let mut fragment_shader = GlShader::new(ShaderKind::Fragment); + let fragment_shader = GlShader::new(current_context, ShaderKind::Fragment); - fragment_shader.set_source(&*glsl_preprocess( - FRAGMENT_GLSL_SHADER_SRC, - &get_glsl_shader_content, - )?)?; + fragment_shader.set_source( + current_context, + &*glsl_preprocess(FRAGMENT_GLSL_SHADER_SRC, &get_glsl_shader_content)?, + )?; - fragment_shader.compile()?; + fragment_shader.compile(current_context)?; - let mut gl_shader_program = GlShaderProgram::new(); + let gl_shader_program = GlShaderProgram::new(current_context); - gl_shader_program.attach(&vertex_shader); - gl_shader_program.attach(&fragment_shader); + gl_shader_program.attach(current_context, &vertex_shader); + gl_shader_program.attach(current_context, &fragment_shader); - gl_shader_program.link()?; + gl_shader_program.link(current_context)?; Ok(gl_shader_program) } @@ -435,108 +1140,36 @@ fn get_glsl_shader_content(path: &Path) -> Result<Vec<u8>, std::io::Error> )) } -#[derive(Debug, Component)] -struct GlObjects -{ - /// Vertex and index buffer has to live as long as the vertex array - _vertex_buffer: Buffer<Vertex>, - index_buffer: Option<Buffer<u32>>, - element_cnt: u32, - - vertex_arr: VertexArray, -} - -impl GlObjects +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct GraphicsMeshId { - #[tracing::instrument(skip_all)] - fn new(mesh: &Mesh) -> Self - { - tracing::trace!( - "Creating vertex array, vertex buffer{}", - if mesh.indices().is_some() { - " and index buffer" - } else { - "" - } - ); - - let mut vertex_arr = VertexArray::new(); - let mut vertex_buffer = Buffer::new(); - - vertex_buffer.store_mapped(mesh.vertices(), BufferUsage::Static, |vertex| { - Vertex { - pos: vertex.pos, - texture_coords: vertex.texture_coords, - normal: vertex.normal, - } - }); - - vertex_arr.bind_vertex_buffer(0, &vertex_buffer, 0); - - let mut offset = 0u32; - - for attrib in Vertex::attrs() { - vertex_arr.enable_attrib(attrib.index); - - vertex_arr.set_attrib_format( - attrib.index, - match attrib.component_type { - AttributeComponentType::Float => VertexArrayDataType::Float, - }, - false, - offset, - ); - - vertex_arr.set_attrib_vertex_buf_binding(attrib.index, 0); - - offset += attrib.component_size * attrib.component_cnt as u32; - } - - if let Some(indices) = mesh.indices() { - let mut index_buffer = Buffer::new(); - - index_buffer.store(indices, BufferUsage::Static); - - vertex_arr.bind_element_buffer(&index_buffer); - - return Self { - _vertex_buffer: vertex_buffer, - index_buffer: Some(index_buffer), - element_cnt: indices - .len() - .try_into() - .expect("Mesh index count does not fit into a 32-bit unsigned int"), - vertex_arr, - }; - } - - Self { - _vertex_buffer: vertex_buffer, - index_buffer: None, - element_cnt: mesh - .vertices() - .len() - .try_into() - .expect("Mesh vertex count does not fit into a 32-bit unsigned int"), - vertex_arr, - } - } + inner: usize, } fn apply_transformation_matrices( + current_context: &CurrentContextWithFns<'_>, transformation: Transformation, gl_shader_program: &mut GlShaderProgram, camera: &Camera, camera_world_pos: &WorldPosition, - window_size: Dimens<u32>, + window_size: &Dimens<u32>, ) { - gl_shader_program - .set_uniform(c"model", &create_transformation_matrix(transformation)); + gl_shader_program.set_uniform( + current_context, + c"model", + &opengl_bindings::data_types::Matrix { + items: create_transformation_matrix(transformation).items, + }, + ); let view_matrix = create_view_matrix(camera, &camera_world_pos.position); - gl_shader_program.set_uniform(c"view", &view_matrix); + gl_shader_program.set_uniform( + current_context, + c"view", + &opengl_bindings::data_types::Matrix { items: view_matrix.items }, + ); #[allow(clippy::cast_precision_loss)] let proj_matrix = match &camera.projection { @@ -548,10 +1181,15 @@ fn apply_transformation_matrices( .to_matrix_rh(&camera_world_pos.position, ClipVolume::NegOneToOne), }; - gl_shader_program.set_uniform(c"projection", &proj_matrix); + gl_shader_program.set_uniform( + current_context, + c"projection", + &opengl_bindings::data_types::Matrix { items: proj_matrix.items }, + ); } fn apply_light<'point_light>( + current_context: &CurrentContextWithFns<'_>, material: &Material, material_flags: &MaterialFlags, global_light: &GlobalLight, @@ -580,16 +1218,20 @@ fn apply_light<'point_light>( ); for (dir_light_index, dir_light) in directional_lights.iter().enumerate() { + let direction: opengl_bindings::data_types::Vec3<_> = dir_light.direction.into(); + gl_shader_program.set_uniform( + current_context, &create_light_uniform_name( "directional_lights", dir_light_index, "direction", ), - &dir_light.direction, + &direction, ); set_light_phong_uniforms( + current_context, gl_shader_program, "directional_lights", dir_light_index, @@ -599,18 +1241,26 @@ fn apply_light<'point_light>( // There probably won't be more than 2147483648 directional lights #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] - gl_shader_program - .set_uniform(c"directional_light_cnt", &(directional_lights.len() as i32)); + gl_shader_program.set_uniform( + current_context, + c"directional_light_cnt", + &(directional_lights.len() as i32), + ); for (point_light_index, (point_light, point_light_world_pos)) in point_light_iter.enumerate() { + let pos: opengl_bindings::data_types::Vec3<_> = + (point_light_world_pos.position + point_light.local_position).into(); + gl_shader_program.set_uniform( + current_context, &create_light_uniform_name("point_lights", point_light_index, "position"), - &(point_light_world_pos.position + point_light.local_position), + &pos, ); set_light_phong_uniforms( + current_context, gl_shader_program, "point_lights", point_light_index, @@ -618,6 +1268,7 @@ fn apply_light<'point_light>( ); set_light_attenuation_uniforms( + current_context, gl_shader_program, "point_lights", point_light_index, @@ -627,44 +1278,67 @@ fn apply_light<'point_light>( // There probably won't be more than 2147483648 point lights #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] - gl_shader_program.set_uniform(c"point_light_cnt", &(point_light_cnt as i32)); - gl_shader_program.set_uniform( - c"material.ambient", - &Vec3::from(if material_flags.use_ambient_color { + current_context, + c"point_light_cnt", + &(point_light_cnt as i32), + ); + + let ambient: opengl_bindings::data_types::Vec3<_> = + Vec3::from(if material_flags.use_ambient_color { material.ambient.clone() } else { global_light.ambient.clone() - }), - ); + }) + .into(); + + gl_shader_program.set_uniform(current_context, c"material.ambient", &ambient); - gl_shader_program - .set_uniform(c"material.diffuse", &Vec3::from(material.diffuse.clone())); + let diffuse: opengl_bindings::data_types::Vec3<_> = + Vec3::from(material.diffuse.clone()).into(); + + gl_shader_program.set_uniform(current_context, c"material.diffuse", &diffuse); + + let specular: opengl_bindings::data_types::Vec3<_> = + Vec3::from(material.specular.clone()).into(); #[allow(clippy::cast_possible_wrap)] - gl_shader_program - .set_uniform(c"material.specular", &Vec3::from(material.specular.clone())); + gl_shader_program.set_uniform(current_context, c"material.specular", &specular); #[allow(clippy::cast_possible_wrap)] - gl_shader_program - .set_uniform(c"material.ambient_map", &(AMBIENT_MAP_TEXTURE_UNIT as i32)); + gl_shader_program.set_uniform( + current_context, + c"material.ambient_map", + &(AMBIENT_MAP_TEXTURE_UNIT as i32), + ); #[allow(clippy::cast_possible_wrap)] - gl_shader_program - .set_uniform(c"material.diffuse_map", &(DIFFUSE_MAP_TEXTURE_UNIT as i32)); + gl_shader_program.set_uniform( + current_context, + c"material.diffuse_map", + &(DIFFUSE_MAP_TEXTURE_UNIT as i32), + ); #[allow(clippy::cast_possible_wrap)] gl_shader_program.set_uniform( + current_context, c"material.specular_map", &(SPECULAR_MAP_TEXTURE_UNIT as i32), ); - gl_shader_program.set_uniform(c"material.shininess", &material.shininess); + gl_shader_program.set_uniform( + current_context, + c"material.shininess", + &material.shininess, + ); + + let view_pos: opengl_bindings::data_types::Vec3<_> = camera_world_pos.position.into(); - gl_shader_program.set_uniform(c"view_pos", &camera_world_pos.position); + gl_shader_program.set_uniform(current_context, c"view_pos", &view_pos); } fn set_light_attenuation_uniforms( + current_context: &CurrentContextWithFns<'_>, gl_shader_program: &mut GlShaderProgram, light_array: &str, light_index: usize, @@ -672,6 +1346,7 @@ fn set_light_attenuation_uniforms( ) { gl_shader_program.set_uniform( + current_context, &create_light_uniform_name( light_array, light_index, @@ -681,11 +1356,13 @@ fn set_light_attenuation_uniforms( ); gl_shader_program.set_uniform( + current_context, &create_light_uniform_name(light_array, light_index, "attenuation_props.linear"), &light.attenuation_params.linear, ); gl_shader_program.set_uniform( + current_context, &create_light_uniform_name( light_array, light_index, @@ -696,6 +1373,7 @@ fn set_light_attenuation_uniforms( } fn set_light_phong_uniforms( + current_context: &CurrentContextWithFns<'_>, gl_shader_program: &mut GlShaderProgram, light_array: &str, light_index: usize, @@ -703,13 +1381,23 @@ fn set_light_phong_uniforms( ) { gl_shader_program.set_uniform( + current_context, &create_light_uniform_name(light_array, light_index, "phong.diffuse"), - &Vec3::from(light.diffuse().clone()), + &opengl_bindings::data_types::Vec3 { + x: light.diffuse().red, + y: light.diffuse().green, + z: light.diffuse().blue, + }, ); gl_shader_program.set_uniform( + current_context, &create_light_uniform_name(light_array, light_index, "phong.specular"), - &Vec3::from(light.specular().clone()), + &opengl_bindings::data_types::Vec3 { + x: light.specular().red, + y: light.specular().green, + z: light.specular().blue, + }, ); } @@ -797,7 +1485,8 @@ fn opengl_debug_message_cb( let backtrace = Backtrace::capture(); if matches!(backtrace.status(), BacktraceStatus::Captured) { - event!(Level::TRACE, "{backtrace}"); + tracing::error!("{backtrace}"); + // event!(Level::TRACE, "{backtrace}"); } } MessageType::Other => { @@ -845,3 +1534,54 @@ fn texture_filtering_to_gl(texture_filtering: TextureFiltering) -> GlTextureFilt TextureFiltering::Nearest => GlTextureFiltering::Nearest, } } + +impl<Value: ReprC + Copy> From<Vec2<Value>> for opengl_bindings::data_types::Vec2<Value> +{ + fn from(vec2: Vec2<Value>) -> Self + { + Self { x: vec2.x, y: vec2.y } + } +} + +impl<Value: ReprC + Copy> From<Vec3<Value>> for opengl_bindings::data_types::Vec3<Value> +{ + fn from(vec3: Vec3<Value>) -> Self + { + Self { x: vec3.x, y: vec3.y, z: vec3.z } + } +} + +impl<Value: Copy> From<Dimens<Value>> for opengl_bindings::data_types::Dimens<Value> +{ + fn from(dimens: Dimens<Value>) -> Self + { + Self { + width: dimens.width, + height: dimens.height, + } + } +} + +impl From<crate::draw_flags::PolygonMode> for opengl_bindings::misc::PolygonMode +{ + fn from(mode: crate::draw_flags::PolygonMode) -> Self + { + match mode { + crate::draw_flags::PolygonMode::Point => Self::Point, + crate::draw_flags::PolygonMode::Fill => Self::Fill, + crate::draw_flags::PolygonMode::Line => Self::Line, + } + } +} + +impl From<crate::draw_flags::PolygonModeFace> for opengl_bindings::misc::PolygonModeFace +{ + fn from(face: crate::draw_flags::PolygonModeFace) -> Self + { + match face { + crate::draw_flags::PolygonModeFace::Front => Self::Front, + crate::draw_flags::PolygonModeFace::Back => Self::Back, + crate::draw_flags::PolygonModeFace::FrontAndBack => Self::FrontAndBack, + } + } +} |