diff --git a/.gitignore b/.gitignore index 5e62e4b6a..3ca2b4131 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,14 @@ extensions datafiles/data/themes/default options options/extensions -options/mac \ No newline at end of file +options/mac + +datafiles/data/BBMOD +scripts/BBMOD* +scripts/bbmod* +scripts/__bbmod* + +shaders/BBMOD* +shaders/__BBMOD* + +sprites/BBMOD* \ No newline at end of file diff --git a/PixelComposer.resource_order b/PixelComposer.resource_order index c97ade831..271049718 100644 --- a/PixelComposer.resource_order +++ b/PixelComposer.resource_order @@ -1,6 +1,9 @@ { "FolderOrderSettings": [ + {"name":"_Extensions","order":9,"path":"folders/_Extensions.yy",}, + {"name":"MAC","order":7,"path":"folders/_Extensions/MAC.yy",}, {"name":"addons","order":11,"path":"folders/addons.yy",}, + {"name":"custom","order":4,"path":"folders/addons/custom.yy",}, {"name":"key displayer","order":2,"path":"folders/addons/key displayer.yy",}, {"name":"animation_curve","order":10,"path":"folders/animation_curve.yy",}, {"name":"dialog","order":4,"path":"folders/dialog.yy",}, @@ -12,8 +15,6 @@ {"name":"menu","order":10,"path":"folders/dialog/menu.yy",}, {"name":"preview","order":7,"path":"folders/dialog/preview.yy",}, {"name":"widget","order":9,"path":"folders/dialog/widget.yy",}, - {"name":"_Extensions","order":9,"path":"folders/_Extensions.yy",}, - {"name":"MAC","order":7,"path":"folders/_Extensions/MAC.yy",}, {"name":"font","order":7,"path":"folders/font.yy",}, {"name":"functions","order":6,"path":"folders/functions.yy",}, {"name":"animation","order":19,"path":"folders/functions/animation.yy",}, @@ -143,9 +144,58 @@ {"name":"sprites","order":12,"path":"folders/sprites.yy",}, {"name":"gameframe","order":2,"path":"folders/sprites/gameframe.yy",}, {"name":"widgets","order":5,"path":"folders/widgets.yy",}, - {"name":"custom","order":4,"path":"folders/addons/custom.yy",}, + {"name":"for sort","order":5,"path":"folders/nodes/data/iterate/for sort.yy",}, + {"name":"BBMOD","order":9,"path":"folders/_Extensions/BBMOD.yy",}, + {"name":"ColMesh","order":1,"path":"folders/_Extensions/BBMOD/ColMesh.yy",}, + {"name":"Core","order":2,"path":"folders/_Extensions/BBMOD/Core.yy",}, + {"name":"Animation","order":1,"path":"folders/_Extensions/BBMOD/Core/Animation.yy",}, + {"name":"Batching","order":2,"path":"folders/_Extensions/BBMOD/Core/Batching.yy",}, + {"name":"Camera","order":3,"path":"folders/_Extensions/BBMOD/Core/Camera.yy",}, + {"name":"Debug","order":4,"path":"folders/_Extensions/BBMOD/Core/Debug.yy",}, + {"name":"DefaultRenderer","order":5,"path":"folders/_Extensions/BBMOD/Core/DefaultRenderer.yy",}, + {"name":"Deprecated","order":1,"path":"folders/_Extensions/BBMOD/Core/DefaultRenderer/Deprecated.yy",}, + {"name":"Shaders","order":2,"path":"folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy",}, + {"name":"Sprites","order":3,"path":"folders/_Extensions/BBMOD/Core/DefaultRenderer/Sprites.yy",}, + {"name":"Exceptions","order":6,"path":"folders/_Extensions/BBMOD/Core/Exceptions.yy",}, + {"name":"Interfaces","order":7,"path":"folders/_Extensions/BBMOD/Core/Interfaces.yy",}, + {"name":"Lights","order":8,"path":"folders/_Extensions/BBMOD/Core/Lights.yy",}, + {"name":"Math","order":9,"path":"folders/_Extensions/BBMOD/Core/Math.yy",}, + {"name":"Model","order":10,"path":"folders/_Extensions/BBMOD/Core/Model.yy",}, + {"name":"Properties","order":11,"path":"folders/_Extensions/BBMOD/Core/Properties.yy",}, + {"name":"Rendering","order":12,"path":"folders/_Extensions/BBMOD/Core/Rendering.yy",}, + {"name":"Utils","order":13,"path":"folders/_Extensions/BBMOD/Core/Utils.yy",}, + {"name":"Gizmo","order":3,"path":"folders/_Extensions/BBMOD/Gizmo.yy",}, + {"name":"OBJImporter","order":4,"path":"folders/_Extensions/BBMOD/OBJImporter.yy",}, + {"name":"Particles","order":5,"path":"folders/_Extensions/BBMOD/Particles.yy",}, + {"name":"Modules","order":1,"path":"folders/_Extensions/BBMOD/Particles/Modules.yy",}, + {"name":"Collision","order":1,"path":"folders/_Extensions/BBMOD/Particles/Modules/Collision.yy",}, + {"name":"Emission","order":2,"path":"folders/_Extensions/BBMOD/Particles/Modules/Emission.yy",}, + {"name":"Shape","order":1,"path":"folders/_Extensions/BBMOD/Particles/Modules/Emission/Shape.yy",}, + {"name":"Event","order":3,"path":"folders/_Extensions/BBMOD/Particles/Modules/Event.yy",}, + {"name":"Kill","order":4,"path":"folders/_Extensions/BBMOD/Particles/Modules/Kill.yy",}, + {"name":"Physics","order":5,"path":"folders/_Extensions/BBMOD/Particles/Modules/Physics.yy",}, + {"name":"Rotation","order":6,"path":"folders/_Extensions/BBMOD/Particles/Modules/Rotation.yy",}, + {"name":"Universal","order":7,"path":"folders/_Extensions/BBMOD/Particles/Modules/Universal.yy",}, + {"name":"AddPropertyOnCollision","order":1,"path":"folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOnCollision.yy",}, + {"name":"AddPropertyOverTime","order":2,"path":"folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOverTime.yy",}, + {"name":"MixProperty","order":3,"path":"folders/_Extensions/BBMOD/Particles/Modules/Universal/MixProperty.yy",}, + {"name":"MixPropertyFromHealth","order":4,"path":"folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromHealth.yy",}, + {"name":"MixPropertyFromSpeed","order":5,"path":"folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromSpeed.yy",}, + {"name":"MixPropertyOverTime","order":6,"path":"folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyOverTime.yy",}, + {"name":"SetProperty","order":7,"path":"folders/_Extensions/BBMOD/Particles/Modules/Universal/SetProperty.yy",}, + {"name":"Velocity","order":8,"path":"folders/_Extensions/BBMOD/Particles/Modules/Velocity.yy",}, + {"name":"Raycasting","order":6,"path":"folders/_Extensions/BBMOD/Raycasting.yy",}, + {"name":"Rendering","order":7,"path":"folders/_Extensions/BBMOD/Rendering.yy",}, + {"name":"FXAA","order":1,"path":"folders/_Extensions/BBMOD/Rendering/FXAA.yy",}, + {"name":"PostProcessing","order":2,"path":"folders/_Extensions/BBMOD/Rendering/PostProcessing.yy",}, + {"name":"Sky","order":3,"path":"folders/_Extensions/BBMOD/Rendering/Sky.yy",}, + {"name":"SSAO","order":4,"path":"folders/_Extensions/BBMOD/Rendering/SSAO.yy",}, + {"name":"Save","order":8,"path":"folders/_Extensions/BBMOD/Save.yy",}, + {"name":"StateMachine","order":9,"path":"folders/_Extensions/BBMOD/StateMachine.yy",}, + {"name":"Terrain","order":10,"path":"folders/_Extensions/BBMOD/Terrain.yy",}, ], "ResourceOrderSettings": [ + {"name":"__bbmod_path","order":4,"path":"scripts/__bbmod_path/__bbmod_path.yy",}, {"name":"s_node_corner","order":16,"path":"sprites/s_node_corner/s_node_corner.yy",}, {"name":"sh_cell_noise_crystal","order":23,"path":"shaders/sh_cell_noise_crystal/sh_cell_noise_crystal.yy",}, {"name":"panel_function","order":2,"path":"scripts/panel_function/panel_function.yy",}, @@ -180,15 +230,19 @@ {"name":"s_node_fluidSim_turbulence","order":10,"path":"sprites/s_node_fluidSim_turbulence/s_node_fluidSim_turbulence.yy",}, {"name":"node_blur_radial","order":7,"path":"scripts/node_blur_radial/node_blur_radial.yy",}, {"name":"node_2d_light","order":1,"path":"scripts/node_2d_light/node_2d_light.yy",}, + {"name":"BBMOD_SprDefaultSpecularColor","order":2,"path":"sprites/BBMOD_SprDefaultSpecularColor/BBMOD_SprDefaultSpecularColor.yy",}, + {"name":"BBMOD_DefaultLightmapMaterial","order":5,"path":"scripts/BBMOD_DefaultLightmapMaterial/BBMOD_DefaultLightmapMaterial.yy",}, {"name":"node_rigid_activation","order":7,"path":"scripts/node_rigid_activation/node_rigid_activation.yy",}, {"name":"s_node_image_gif","order":7,"path":"sprites/s_node_image_gif/s_node_image_gif.yy",}, {"name":"node_VFX_effect_wind","order":5,"path":"scripts/node_VFX_effect_wind/node_VFX_effect_wind.yy",}, {"name":"s_node_tunnel_out","order":22,"path":"sprites/s_node_tunnel_out/s_node_tunnel_out.yy",}, {"name":"__background_set_element","order":3,"path":"scripts/__background_set_element/__background_set_element.yy",}, + {"name":"BBMOD_ShDefaultSprite","order":8,"path":"shaders/BBMOD_ShDefaultSprite/BBMOD_ShDefaultSprite.yy",}, {"name":"s_node_3d_obj","order":3,"path":"sprites/s_node_3d_obj/s_node_3d_obj.yy",}, {"name":"s_node_scale","order":6,"path":"sprites/s_node_scale/s_node_scale.yy",}, {"name":"sh_color_picker_value","order":32,"path":"shaders/sh_color_picker_value/sh_color_picker_value.yy",}, {"name":"textInput","order":24,"path":"scripts/textInput/textInput.yy",}, + {"name":"BBMOD_Renderer","order":2,"path":"scripts/BBMOD_Renderer/BBMOD_Renderer.yy",}, {"name":"node_dither","order":7,"path":"scripts/node_dither/node_dither.yy",}, {"name":"sh_perlin_smear","order":7,"path":"shaders/sh_perlin_smear/sh_perlin_smear.yy",}, {"name":"node_path_blend","order":2,"path":"scripts/node_path_blend/node_path_blend.yy",}, @@ -196,6 +250,7 @@ {"name":"sh_corner","order":20,"path":"shaders/sh_corner/sh_corner.yy",}, {"name":"node_array","order":8,"path":"scripts/node_array/node_array.yy",}, {"name":"pack_corner","order":3,"path":"scripts/pack_corner/pack_corner.yy",}, + {"name":"BBMOD_SphereEmissionModule","order":1,"path":"scripts/BBMOD_SphereEmissionModule/BBMOD_SphereEmissionModule.yy",}, {"name":"sh_colorize","order":9,"path":"shaders/sh_colorize/sh_colorize.yy",}, {"name":"node_string","order":13,"path":"scripts/node_string/node_string.yy",}, {"name":"cross_product","order":3,"path":"scripts/cross_product/cross_product.yy",}, @@ -205,10 +260,13 @@ {"name":"s_node_loop_input","order":14,"path":"sprites/s_node_loop_input/s_node_loop_input.yy",}, {"name":"node_strand_length_adjust","order":11,"path":"scripts/node_strand_length_adjust/node_strand_length_adjust.yy",}, {"name":"fd_rectangle_add_material_surface","order":25,"path":"scripts/fd_rectangle_add_material_surface/fd_rectangle_add_material_surface.yy",}, + {"name":"BBMOD_ImageBasedLight","order":2,"path":"scripts/BBMOD_ImageBasedLight/BBMOD_ImageBasedLight.yy",}, {"name":"perlin_noise","order":5,"path":"scripts/perlin_noise/perlin_noise.yy",}, {"name":"fd_rectangle_replace_material","order":14,"path":"scripts/fd_rectangle_replace_material/fd_rectangle_replace_material.yy",}, {"name":"node_functions","order":2,"path":"scripts/node_functions/node_functions.yy",}, {"name":"node_math","order":1,"path":"scripts/node_math/node_math.yy",}, + {"name":"BBMOD_MixQuaternionFromSpeedModule","order":1,"path":"scripts/BBMOD_MixQuaternionFromSpeedModule/BBMOD_MixQuaternionFromSpeedModule.yy",}, + {"name":"BBMOD_SprWhite","order":22,"path":"sprites/BBMOD_SprWhite/BBMOD_SprWhite.yy",}, {"name":"fd_rectangle_replace_material_advanced","order":15,"path":"scripts/fd_rectangle_replace_material_advanced/fd_rectangle_replace_material_advanced.yy",}, {"name":"array_functions","order":2,"path":"scripts/array_functions/array_functions.yy",}, {"name":"sh_blur_final","order":2,"path":"shaders/sh_blur_final/sh_blur_final.yy",}, @@ -216,8 +274,10 @@ {"name":"fd_rectangle_set_material_time_step","order":8,"path":"scripts/fd_rectangle_set_material_time_step/fd_rectangle_set_material_time_step.yy",}, {"name":"s_node_path_sample","order":3,"path":"sprites/s_node_path_sample/s_node_path_sample.yy",}, {"name":"s_node_colorize","order":15,"path":"sprites/s_node_colorize/s_node_colorize.yy",}, + {"name":"BBMOD_ShInstanceHighlight","order":4,"path":"shaders/BBMOD_ShInstanceHighlight/BBMOD_ShInstanceHighlight.yy",}, {"name":"s_node_sepearte_shape","order":35,"path":"sprites/s_node_sepearte_shape/s_node_sepearte_shape.yy",}, {"name":"s_node_text_join","order":5,"path":"sprites/s_node_text_join/s_node_text_join.yy",}, + {"name":"BBMOD_SprBlack","order":21,"path":"sprites/BBMOD_SprBlack/BBMOD_SprBlack.yy",}, {"name":"s_node_polar","order":5,"path":"sprites/s_node_polar/s_node_polar.yy",}, {"name":"draw_set_blend_mode_ext","order":2,"path":"scripts/draw_set_blend_mode_ext/draw_set_blend_mode_ext.yy",}, {"name":"s_node_noise_simplex","order":20,"path":"sprites/s_node_noise_simplex/s_node_noise_simplex.yy",}, @@ -228,10 +288,13 @@ {"name":"sh_flip","order":7,"path":"shaders/sh_flip/sh_flip.yy",}, {"name":"libdlgmodule","order":3,"path":"extensions/libdlgmodule/libdlgmodule.yy",}, {"name":"s_node_alpha_grey","order":4,"path":"sprites/s_node_alpha_grey/s_node_alpha_grey.yy",}, + {"name":"BBMOD_MixVec3OverTimeModule","order":4,"path":"scripts/BBMOD_MixVec3OverTimeModule/BBMOD_MixVec3OverTimeModule.yy",}, {"name":"fd_rectangle_set_pressure_iteration_type","order":10,"path":"scripts/fd_rectangle_set_pressure_iteration_type/fd_rectangle_set_pressure_iteration_type.yy",}, + {"name":"s_node_sort_array","order":29,"path":"sprites/s_node_sort_array/s_node_sort_array.yy",}, {"name":"_f_h1","order":11,"path":"fonts/_f_h1/_f_h1.yy",}, {"name":"_f_h2","order":12,"path":"fonts/_f_h2/_f_h2.yy",}, {"name":"s_node_level","order":28,"path":"sprites/s_node_level/s_node_level.yy",}, + {"name":"BBMOD_MixVec2OverTimeModule","order":3,"path":"scripts/BBMOD_MixVec2OverTimeModule/BBMOD_MixVec2OverTimeModule.yy",}, {"name":"node_scatter","order":3,"path":"scripts/node_scatter/node_scatter.yy",}, {"name":"s_node_bloom","order":8,"path":"sprites/s_node_bloom/s_node_bloom.yy",}, {"name":"node_atlas_set","order":2,"path":"scripts/node_atlas_set/node_atlas_set.yy",}, @@ -239,9 +302,14 @@ {"name":"s_node_gradient_data","order":17,"path":"sprites/s_node_gradient_data/s_node_gradient_data.yy",}, {"name":"s_node_vfx_render","order":1,"path":"sprites/s_node_vfx_render/s_node_vfx_render.yy",}, {"name":"node_stack","order":2,"path":"scripts/node_stack/node_stack.yy",}, + {"name":"BBMOD_DLL","order":17,"path":"scripts/BBMOD_DLL/BBMOD_DLL.yy",}, + {"name":"BBMOD_CameraHTML5","order":2,"path":"extensions/BBMOD_CameraHTML5/BBMOD_CameraHTML5.yy",}, {"name":"s_node_rigidSim_object","order":3,"path":"sprites/s_node_rigidSim_object/s_node_rigidSim_object.yy",}, {"name":"sh_blend_max","order":8,"path":"shaders/sh_blend_max/sh_blend_max.yy",}, {"name":"s_node_color_out","order":6,"path":"sprites/s_node_color_out/s_node_color_out.yy",}, + {"name":"__BBMOD_ShCheckVTF","order":5,"path":"shaders/__BBMOD_ShCheckVTF/__BBMOD_ShCheckVTF.yy",}, + {"name":"bbmod_lerp_delta_time","order":1,"path":"scripts/bbmod_lerp_delta_time/bbmod_lerp_delta_time.yy",}, + {"name":"BBMOD_StateMachine","order":3,"path":"scripts/BBMOD_StateMachine/BBMOD_StateMachine.yy",}, {"name":"_3D","order":6,"path":"scripts/_3D/_3D.yy",}, {"name":"node_vector_cross2D","order":29,"path":"scripts/node_vector_cross2D/node_vector_cross2D.yy",}, {"name":"sh_corner_erode","order":48,"path":"shaders/sh_corner_erode/sh_corner_erode.yy",}, @@ -261,6 +329,7 @@ {"name":"o_dialog_panel","order":13,"path":"objects/o_dialog_panel/o_dialog_panel.yy",}, {"name":"s_node_vfx","order":9,"path":"sprites/s_node_vfx/s_node_vfx.yy",}, {"name":"fd_rectangle_get_velocity_maccormack_weight","order":23,"path":"scripts/fd_rectangle_get_velocity_maccormack_weight/fd_rectangle_get_velocity_maccormack_weight.yy",}, + {"name":"BBMOD_SetRealModule","order":2,"path":"scripts/BBMOD_SetRealModule/BBMOD_SetRealModule.yy",}, {"name":"sh_fd_advect_material_rgba_8_glsl","order":5,"path":"shaders/sh_fd_advect_material_rgba_8_glsl/sh_fd_advect_material_rgba_8_glsl.yy",}, {"name":"fd_rectangle_get_material_dissipation_type","order":8,"path":"scripts/fd_rectangle_get_material_dissipation_type/fd_rectangle_get_material_dissipation_type.yy",}, {"name":"draw_surface_blend","order":1,"path":"scripts/draw_surface_blend/draw_surface_blend.yy",}, @@ -279,6 +348,7 @@ {"name":"sh_texture_atlas","order":39,"path":"shaders/sh_texture_atlas/sh_texture_atlas.yy",}, {"name":"s_node_fluidSim_domain","order":6,"path":"sprites/s_node_fluidSim_domain/s_node_fluidSim_domain.yy",}, {"name":"s_node_displace","order":20,"path":"sprites/s_node_displace/s_node_displace.yy",}, + {"name":"BBMOD_ParticleModule","order":6,"path":"scripts/BBMOD_ParticleModule/BBMOD_ParticleModule.yy",}, {"name":"sh_polar","order":1,"path":"shaders/sh_polar/sh_polar.yy",}, {"name":"s_node_warp_mesh","order":10,"path":"sprites/s_node_warp_mesh/s_node_warp_mesh.yy",}, {"name":"sh_pixel_cloud","order":13,"path":"shaders/sh_pixel_cloud/sh_pixel_cloud.yy",}, @@ -302,6 +372,7 @@ {"name":"s_node_2d_light","order":2,"path":"sprites/s_node_2d_light/s_node_2d_light.yy",}, {"name":"s_node_trail","order":40,"path":"sprites/s_node_trail/s_node_trail.yy",}, {"name":"fd_rectangle_update_view","order":5,"path":"scripts/fd_rectangle_update_view/fd_rectangle_update_view.yy",}, + {"name":"BBMOD_ShTerrainUnlit","order":3,"path":"shaders/BBMOD_ShTerrainUnlit/BBMOD_ShTerrainUnlit.yy",}, {"name":"s_node_rigidSim_renderer","order":1,"path":"sprites/s_node_rigidSim_renderer/s_node_rigidSim_renderer.yy",}, {"name":"__init_background","order":4,"path":"scripts/__init_background/__init_background.yy",}, {"name":"__node","order":9,"path":"scripts/__node/__node.yy",}, @@ -332,6 +403,7 @@ {"name":"s_node_3d_cone","order":9,"path":"sprites/s_node_3d_cone/s_node_3d_cone.yy",}, {"name":"s_node_compose","order":1,"path":"sprites/s_node_compose/s_node_compose.yy",}, {"name":"s_node_switch","order":20,"path":"sprites/s_node_switch/s_node_switch.yy",}, + {"name":"BBMOD_Gizmo","order":1,"path":"scripts/BBMOD_Gizmo/BBMOD_Gizmo.yy",}, {"name":"s_node_crop","order":2,"path":"sprites/s_node_crop/s_node_crop.yy",}, {"name":"__VFX","order":2,"path":"scripts/__VFX/__VFX.yy",}, {"name":"s_menu_black","order":2,"path":"sprites/s_menu_black/s_menu_black.yy",}, @@ -341,9 +413,11 @@ {"name":"font_loader","order":8,"path":"scripts/font_loader/font_loader.yy",}, {"name":"node_iterator_filter_input","order":1,"path":"scripts/node_iterator_filter_input/node_iterator_filter_input.yy",}, {"name":"point_rotate","order":1,"path":"scripts/point_rotate/point_rotate.yy",}, + {"name":"BBMOD_MixVec2FromHealthModule","order":3,"path":"scripts/BBMOD_MixVec2FromHealthModule/BBMOD_MixVec2FromHealthModule.yy",}, {"name":"s_node_vfx_variable","order":10,"path":"sprites/s_node_vfx_variable/s_node_vfx_variable.yy",}, {"name":"node_displacement","order":1,"path":"scripts/node_displacement/node_displacement.yy",}, {"name":"mask_function","order":1,"path":"scripts/mask_function/mask_function.yy",}, + {"name":"BBMOD_AnimationPlayer","order":2,"path":"scripts/BBMOD_AnimationPlayer/BBMOD_AnimationPlayer.yy",}, {"name":"text_file","order":5,"path":"scripts/text_file/text_file.yy",}, {"name":"sh_trail_filler_pass1","order":49,"path":"shaders/sh_trail_filler_pass1/sh_trail_filler_pass1.yy",}, {"name":"s_node_vfx_wind","order":3,"path":"sprites/s_node_vfx_wind/s_node_vfx_wind.yy",}, @@ -354,6 +428,7 @@ {"name":"s_node_image_sheet","order":10,"path":"sprites/s_node_image_sheet/s_node_image_sheet.yy",}, {"name":"locale_data","order":1,"path":"scripts/locale_data/locale_data.yy",}, {"name":"o_dialog_scrollbox","order":2,"path":"objects/o_dialog_scrollbox/o_dialog_scrollbox.yy",}, + {"name":"__bbmod_particles","order":2,"path":"scripts/__bbmod_particles/__bbmod_particles.yy",}, {"name":"s_node_palette","order":10,"path":"sprites/s_node_palette/s_node_palette.yy",}, {"name":"curve_damping_function","order":2,"path":"scripts/curve_damping_function/curve_damping_function.yy",}, {"name":"fd_rectangle_get_collision_mask_surface","order":6,"path":"scripts/fd_rectangle_get_collision_mask_surface/fd_rectangle_get_collision_mask_surface.yy",}, @@ -363,8 +438,10 @@ {"name":"fd_draw_surface_to_collision_mask_surface","order":2,"path":"scripts/fd_draw_surface_to_collision_mask_surface/fd_draw_surface_to_collision_mask_surface.yy",}, {"name":"sh_blur_box_contrast","order":3,"path":"shaders/sh_blur_box_contrast/sh_blur_box_contrast.yy",}, {"name":"sh_fd_calculate_pressure_jacobi_glsl","order":8,"path":"shaders/sh_fd_calculate_pressure_jacobi_glsl/sh_fd_calculate_pressure_jacobi_glsl.yy",}, + {"name":"BBMOD_IRenderTarget","order":2,"path":"scripts/BBMOD_IRenderTarget/BBMOD_IRenderTarget.yy",}, {"name":"node_color_from_rgb","order":7,"path":"scripts/node_color_from_rgb/node_color_from_rgb.yy",}, {"name":"node_struct_get","order":1,"path":"scripts/node_struct_get/node_struct_get.yy",}, + {"name":"BBMOD_Class","order":16,"path":"scripts/BBMOD_Class/BBMOD_Class.yy",}, {"name":"curveBox","order":8,"path":"scripts/curveBox/curveBox.yy",}, {"name":"s_node_iterator_length","order":24,"path":"sprites/s_node_iterator_length/s_node_iterator_length.yy",}, {"name":"preview_overlay_vector","order":2,"path":"scripts/preview_overlay_vector/preview_overlay_vector.yy",}, @@ -377,13 +454,17 @@ {"name":"o_dialog_splash","order":9,"path":"objects/o_dialog_splash/o_dialog_splash.yy",}, {"name":"json_file","order":4,"path":"scripts/json_file/json_file.yy",}, {"name":"s_node_curve_edit","order":1,"path":"sprites/s_node_curve_edit/s_node_curve_edit.yy",}, + {"name":"bbmod_vtf_is_supported","order":10,"path":"scripts/bbmod_vtf_is_supported/bbmod_vtf_is_supported.yy",}, {"name":"textBox","order":17,"path":"scripts/textBox/textBox.yy",}, {"name":"pathArrayBox","order":27,"path":"scripts/pathArrayBox/pathArrayBox.yy",}, {"name":"node_statistic","order":7,"path":"scripts/node_statistic/node_statistic.yy",}, {"name":"sh_draw_surface_part_tiled","order":1,"path":"shaders/sh_draw_surface_part_tiled/sh_draw_surface_part_tiled.yy",}, {"name":"o_dialog_add_node","order":1,"path":"objects/o_dialog_add_node/o_dialog_add_node.yy",}, + {"name":"bbmod_cmp","order":1,"path":"scripts/bbmod_cmp/bbmod_cmp.yy",}, + {"name":"BBMOD_ShDefaultUnlitBatched","order":11,"path":"shaders/BBMOD_ShDefaultUnlitBatched/BBMOD_ShDefaultUnlitBatched.yy",}, {"name":"s_node_path_reverse","order":8,"path":"sprites/s_node_path_reverse/s_node_path_reverse.yy",}, {"name":"fd_rectangle_get_pressure_width","order":18,"path":"scripts/fd_rectangle_get_pressure_width/fd_rectangle_get_pressure_width.yy",}, + {"name":"BBMOD_Node","order":3,"path":"scripts/BBMOD_Node/BBMOD_Node.yy",}, {"name":"sh_blend_normal","order":1,"path":"shaders/sh_blend_normal/sh_blend_normal.yy",}, {"name":"node_feedback_output","order":2,"path":"scripts/node_feedback_output/node_feedback_output.yy",}, {"name":"node_lua_surface","order":2,"path":"scripts/node_lua_surface/node_lua_surface.yy",}, @@ -402,13 +483,15 @@ {"name":"s_node_blur","order":9,"path":"sprites/s_node_blur/s_node_blur.yy",}, {"name":"textArea","order":20,"path":"scripts/textArea/textArea.yy",}, {"name":"s_node_gradient_4points","order":2,"path":"sprites/s_node_gradient_4points/s_node_gradient_4points.yy",}, - {"name":"regEdit","order":9,"path":"extensions/regEdit/regEdit.yy",}, + {"name":"BBMOD_PointLight","order":4,"path":"scripts/BBMOD_PointLight/BBMOD_PointLight.yy",}, {"name":"s_node_gradient_out","order":9,"path":"sprites/s_node_gradient_out/s_node_gradient_out.yy",}, {"name":"s_node_vec3","order":8,"path":"sprites/s_node_vec3/s_node_vec3.yy",}, {"name":"s_node_strandSim_create","order":2,"path":"sprites/s_node_strandSim_create/s_node_strandSim_create.yy",}, + {"name":"__bbmod_render_pass","order":2,"path":"scripts/__bbmod_render_pass/__bbmod_render_pass.yy",}, {"name":"node_gradient_shift","order":10,"path":"scripts/node_gradient_shift/node_gradient_shift.yy",}, {"name":"sh_vertex_normal_pass","order":5,"path":"shaders/sh_vertex_normal_pass/sh_vertex_normal_pass.yy",}, {"name":"node_vector_cross3D","order":28,"path":"scripts/node_vector_cross3D/node_vector_cross3D.yy",}, + {"name":"BBMOD_ShSSAO","order":1,"path":"shaders/BBMOD_ShSSAO/BBMOD_ShSSAO.yy",}, {"name":"s_node_pack_sprite","order":5,"path":"sprites/s_node_pack_sprite/s_node_pack_sprite.yy",}, {"name":"surface_valid","order":6,"path":"scripts/surface_valid/surface_valid.yy",}, {"name":"sh_blur_zoom","order":5,"path":"shaders/sh_blur_zoom/sh_blur_zoom.yy",}, @@ -418,11 +501,14 @@ {"name":"s_node_invert","order":27,"path":"sprites/s_node_invert/s_node_invert.yy",}, {"name":"draw_text_delimiter","order":14,"path":"scripts/draw_text_delimiter/draw_text_delimiter.yy",}, {"name":"s_node_path_anchor","order":13,"path":"sprites/s_node_path_anchor/s_node_path_anchor.yy",}, + {"name":"BBMOD_NotImplementedException","order":1,"path":"scripts/BBMOD_NotImplementedException/BBMOD_NotImplementedException.yy",}, {"name":"node_array_get","order":10,"path":"scripts/node_array_get/node_array_get.yy",}, + {"name":"BBMOD_ShExtractSplatmapLayer","order":1,"path":"shaders/BBMOD_ShExtractSplatmapLayer/BBMOD_ShExtractSplatmapLayer.yy",}, {"name":"Apollo","order":6,"path":"extensions/Apollo/Apollo.yy",}, {"name":"sh_grid","order":14,"path":"shaders/sh_grid/sh_grid.yy",}, {"name":"sh_twirl","order":3,"path":"shaders/sh_twirl/sh_twirl.yy",}, {"name":"s_node_shape","order":14,"path":"sprites/s_node_shape/s_node_shape.yy",}, + {"name":"BBMOD_MixEmissionModule","order":4,"path":"scripts/BBMOD_MixEmissionModule/BBMOD_MixEmissionModule.yy",}, {"name":"time_source","order":25,"path":"scripts/time_source/time_source.yy",}, {"name":"string_formatting","order":5,"path":"scripts/string_formatting/string_formatting.yy",}, {"name":"node_level_selector","order":8,"path":"scripts/node_level_selector/node_level_selector.yy",}, @@ -432,6 +518,7 @@ {"name":"node_color_mix","order":14,"path":"scripts/node_color_mix/node_color_mix.yy",}, {"name":"s_node_array_range","order":6,"path":"sprites/s_node_array_range/s_node_array_range.yy",}, {"name":"sh_fd_advect_material_a_16_glsl","order":2,"path":"shaders/sh_fd_advect_material_a_16_glsl/sh_fd_advect_material_a_16_glsl.yy",}, + {"name":"BBMOD_MixVec3FromHealthModule","order":4,"path":"scripts/BBMOD_MixVec3FromHealthModule/BBMOD_MixVec3FromHealthModule.yy",}, {"name":"distribution_function","order":12,"path":"scripts/distribution_function/distribution_function.yy",}, {"name":"sh_blur_radial","order":9,"path":"shaders/sh_blur_radial/sh_blur_radial.yy",}, {"name":"node_iterator_each_input","order":1,"path":"scripts/node_iterator_each_input/node_iterator_each_input.yy",}, @@ -440,10 +527,14 @@ {"name":"s_node_loop","order":5,"path":"sprites/s_node_loop/s_node_loop.yy",}, {"name":"node_zigzag","order":12,"path":"scripts/node_zigzag/node_zigzag.yy",}, {"name":"node_equation","order":18,"path":"scripts/node_equation/node_equation.yy",}, + {"name":"BBMOD_ShGizmoSelect","order":3,"path":"shaders/BBMOD_ShGizmoSelect/BBMOD_ShGizmoSelect.yy",}, + {"name":"BBMOD_ResourceManager","order":20,"path":"scripts/BBMOD_ResourceManager/BBMOD_ResourceManager.yy",}, + {"name":"BBMOD_RaycastResult","order":6,"path":"scripts/BBMOD_RaycastResult/BBMOD_RaycastResult.yy",}, {"name":"fd_rectangle_get_visualization_shader","order":27,"path":"scripts/fd_rectangle_get_visualization_shader/fd_rectangle_get_visualization_shader.yy",}, {"name":"preset_data","order":8,"path":"scripts/preset_data/preset_data.yy",}, {"name":"_f_h5","order":2,"path":"fonts/_f_h5/_f_h5.yy",}, {"name":"node_string_split","order":14,"path":"scripts/node_string_split/node_string_split.yy",}, + {"name":"BBMOD_PunctualLight","order":5,"path":"scripts/BBMOD_PunctualLight/BBMOD_PunctualLight.yy",}, {"name":"meta_data","order":12,"path":"scripts/meta_data/meta_data.yy",}, {"name":"node_find_pixel","order":1,"path":"scripts/node_find_pixel/node_find_pixel.yy",}, {"name":"node_scatter_points","order":24,"path":"scripts/node_scatter_points/node_scatter_points.yy",}, @@ -457,7 +548,10 @@ {"name":"sh_sample_points","order":26,"path":"shaders/sh_sample_points/sh_sample_points.yy",}, {"name":"node_combine_rgb","order":1,"path":"scripts/node_combine_rgb/node_combine_rgb.yy",}, {"name":"fd_rectangle_draw_part","order":10,"path":"scripts/fd_rectangle_draw_part/fd_rectangle_draw_part.yy",}, + {"name":"BBMOD_SetVec3Module","order":4,"path":"scripts/BBMOD_SetVec3Module/BBMOD_SetVec3Module.yy",}, + {"name":"BBMOD_SprParticle","order":12,"path":"sprites/BBMOD_SprParticle/BBMOD_SprParticle.yy",}, {"name":"checkbox","order":7,"path":"scripts/checkbox/checkbox.yy",}, + {"name":"BBMOD_EmissionModule","order":2,"path":"scripts/BBMOD_EmissionModule/BBMOD_EmissionModule.yy",}, {"name":"node_string_regex_replace","order":24,"path":"scripts/node_string_regex_replace/node_string_regex_replace.yy",}, {"name":"s_node_particle","order":12,"path":"sprites/s_node_particle/s_node_particle.yy",}, {"name":"s_node_random","order":3,"path":"sprites/s_node_random/s_node_random.yy",}, @@ -469,6 +563,7 @@ {"name":"node_path_anchor","order":13,"path":"scripts/node_path_anchor/node_path_anchor.yy",}, {"name":"s_node_path_wave","order":7,"path":"sprites/s_node_path_wave/s_node_path_wave.yy",}, {"name":"string_function","order":6,"path":"scripts/string_function/string_function.yy",}, + {"name":"BBMOD_MixVec4Module","order":5,"path":"scripts/BBMOD_MixVec4Module/BBMOD_MixVec4Module.yy",}, {"name":"o_dialog_palette","order":2,"path":"objects/o_dialog_palette/o_dialog_palette.yy",}, {"name":"sh_blend_hue","order":17,"path":"shaders/sh_blend_hue/sh_blend_hue.yy",}, {"name":"s_node_threshold","order":47,"path":"sprites/s_node_threshold/s_node_threshold.yy",}, @@ -493,6 +588,7 @@ {"name":"node_chromatic_aberration","order":4,"path":"scripts/node_chromatic_aberration/node_chromatic_aberration.yy",}, {"name":"draw_line_elbow_diag","order":18,"path":"scripts/draw_line_elbow_diag/draw_line_elbow_diag.yy",}, {"name":"node_pin","order":2,"path":"scripts/node_pin/node_pin.yy",}, + {"name":"BBMOD_IRenderable","order":1,"path":"scripts/BBMOD_IRenderable/BBMOD_IRenderable.yy",}, {"name":"_node_fluid_nodes","order":7,"path":"scripts/_node_fluid_nodes/_node_fluid_nodes.yy",}, {"name":"sh_noise","order":16,"path":"shaders/sh_noise/sh_noise.yy",}, {"name":"sh_skew","order":6,"path":"shaders/sh_skew/sh_skew.yy",}, @@ -506,6 +602,7 @@ {"name":"fd_rectangle_get_collision_mask_sprite_image","order":5,"path":"scripts/fd_rectangle_get_collision_mask_sprite_image/fd_rectangle_get_collision_mask_sprite_image.yy",}, {"name":"s_node_stripe","order":16,"path":"sprites/s_node_stripe/s_node_stripe.yy",}, {"name":"s_node_lua_global","order":19,"path":"sprites/s_node_lua_global/s_node_lua_global.yy",}, + {"name":"BBMOD_MixVec4FromHealthModule","order":5,"path":"scripts/BBMOD_MixVec4FromHealthModule/BBMOD_MixVec4FromHealthModule.yy",}, {"name":"sh_grey_alpha","order":14,"path":"shaders/sh_grey_alpha/sh_grey_alpha.yy",}, {"name":"sh_normal","order":1,"path":"shaders/sh_normal/sh_normal.yy",}, {"name":"s_node_time_map","order":39,"path":"sprites/s_node_time_map/s_node_time_map.yy",}, @@ -514,27 +611,35 @@ {"name":"s_node_lua_compute","order":17,"path":"sprites/s_node_lua_compute/s_node_lua_compute.yy",}, {"name":"buttonPalette","order":6,"path":"scripts/buttonPalette/buttonPalette.yy",}, {"name":"fd_rectangle_draw_stretched","order":11,"path":"scripts/fd_rectangle_draw_stretched/fd_rectangle_draw_stretched.yy",}, + {"name":"__bbmod_fog","order":1,"path":"scripts/__bbmod_fog/__bbmod_fog.yy",}, {"name":"s_node_wiggler","order":2,"path":"sprites/s_node_wiggler/s_node_wiggler.yy",}, {"name":"sh_edge_detect","order":30,"path":"shaders/sh_edge_detect/sh_edge_detect.yy",}, {"name":"s_node_area","order":4,"path":"sprites/s_node_area/s_node_area.yy",}, {"name":"type_conversion","order":8,"path":"scripts/type_conversion/type_conversion.yy",}, {"name":"node_keyframe","order":4,"path":"scripts/node_keyframe/node_keyframe.yy",}, {"name":"sh_threshold","order":42,"path":"shaders/sh_threshold/sh_threshold.yy",}, + {"name":"BBMOD_AnimationInstance","order":1,"path":"scripts/BBMOD_AnimationInstance/BBMOD_AnimationInstance.yy",}, {"name":"fd_rectangle_set_initial_value_pressure","order":3,"path":"scripts/fd_rectangle_set_initial_value_pressure/fd_rectangle_set_initial_value_pressure.yy",}, {"name":"o_dialog_drag_folder","order":2,"path":"objects/o_dialog_drag_folder/o_dialog_drag_folder.yy",}, {"name":"node_VFX_effect_repel","order":9,"path":"scripts/node_VFX_effect_repel/node_VFX_effect_repel.yy",}, + {"name":"BBMOD_ShSky","order":1,"path":"shaders/BBMOD_ShSky/BBMOD_ShSky.yy",}, {"name":"s_node_alpha_cut","order":3,"path":"sprites/s_node_alpha_cut/s_node_alpha_cut.yy",}, {"name":"node_iterator_length","order":4,"path":"scripts/node_iterator_length/node_iterator_length.yy",}, + {"name":"BBMOD_DefaultLightmapShader","order":6,"path":"scripts/BBMOD_DefaultLightmapShader/BBMOD_DefaultLightmapShader.yy",}, {"name":"node_VFX_effect_attract","order":8,"path":"scripts/node_VFX_effect_attract/node_VFX_effect_attract.yy",}, {"name":"s_node_text_splice","order":6,"path":"sprites/s_node_text_splice/s_node_text_splice.yy",}, {"name":"__atlas","order":6,"path":"scripts/__atlas/__atlas.yy",}, {"name":"node_3d_object_transform","order":11,"path":"scripts/node_3d_object_transform/node_3d_object_transform.yy",}, + {"name":"BBMOD_PlaneCollider","order":4,"path":"scripts/BBMOD_PlaneCollider/BBMOD_PlaneCollider.yy",}, {"name":"sh_draw_single_channel","order":3,"path":"shaders/sh_draw_single_channel/sh_draw_single_channel.yy",}, {"name":"draw_sprite_ext_override","order":9,"path":"scripts/draw_sprite_ext_override/draw_sprite_ext_override.yy",}, {"name":"node_array_add","order":9,"path":"scripts/node_array_add/node_array_add.yy",}, {"name":"s_node_array_set","order":9,"path":"sprites/s_node_array_set/s_node_array_set.yy",}, {"name":"node_noise_cell","order":2,"path":"scripts/node_noise_cell/node_noise_cell.yy",}, {"name":"__background_get_internal","order":2,"path":"scripts/__background_get_internal/__background_get_internal.yy",}, + {"name":"BBMOD_Light","order":3,"path":"scripts/BBMOD_Light/BBMOD_Light.yy",}, + {"name":"BBMOD_ShPostProcess","order":2,"path":"shaders/BBMOD_ShPostProcess/BBMOD_ShPostProcess.yy",}, + {"name":"BBMOD_Color","order":6,"path":"scripts/BBMOD_Color/BBMOD_Color.yy",}, {"name":"sh_combine_hsv","order":41,"path":"shaders/sh_combine_hsv/sh_combine_hsv.yy",}, {"name":"s_node_array_insert","order":4,"path":"sprites/s_node_array_insert/s_node_array_insert.yy",}, {"name":"addon_lua","order":1,"path":"scripts/addon_lua/addon_lua.yy",}, @@ -549,31 +654,39 @@ {"name":"buttonColor","order":3,"path":"scripts/buttonColor/buttonColor.yy",}, {"name":"notification_system","order":7,"path":"scripts/notification_system/notification_system.yy",}, {"name":"node_color_from_hsv","order":8,"path":"scripts/node_color_from_hsv/node_color_from_hsv.yy",}, + {"name":"BBMOD_SetVec4Module","order":5,"path":"scripts/BBMOD_SetVec4Module/BBMOD_SetVec4Module.yy",}, {"name":"s_node_image_copy","order":6,"path":"sprites/s_node_image_copy/s_node_image_copy.yy",}, {"name":"node_boolean","order":17,"path":"scripts/node_boolean/node_boolean.yy",}, {"name":"node_grid_tri","order":21,"path":"scripts/node_grid_tri/node_grid_tri.yy",}, {"name":"node_average","order":5,"path":"scripts/node_average/node_average.yy",}, + {"name":"BBMOD_Model","order":2,"path":"scripts/BBMOD_Model/BBMOD_Model.yy",}, {"name":"node_mesh_transform","order":2,"path":"scripts/node_mesh_transform/node_mesh_transform.yy",}, {"name":"node_fluid_turbulence","order":10,"path":"scripts/node_fluid_turbulence/node_fluid_turbulence.yy",}, {"name":"sh_sdf","order":1,"path":"shaders/sh_sdf/sh_sdf.yy",}, {"name":"slider","order":15,"path":"scripts/slider/slider.yy",}, {"name":"s_node_convolution","order":48,"path":"sprites/s_node_convolution/s_node_convolution.yy",}, + {"name":"BBMOD_DefaultRenderer","order":8,"path":"scripts/BBMOD_DefaultRenderer/BBMOD_DefaultRenderer.yy",}, {"name":"fd_rectangle_get_velocity_time_step","order":25,"path":"scripts/fd_rectangle_get_velocity_time_step/fd_rectangle_get_velocity_time_step.yy",}, {"name":"node_string_regex_match","order":25,"path":"scripts/node_string_regex_match/node_string_regex_match.yy",}, {"name":"node_9slice","order":5,"path":"scripts/node_9slice/node_9slice.yy",}, {"name":"fd_rectangle_add_velocity_surface","order":24,"path":"scripts/fd_rectangle_add_velocity_surface/fd_rectangle_add_velocity_surface.yy",}, + {"name":"BBMOD_Vec2","order":4,"path":"scripts/BBMOD_Vec2/BBMOD_Vec2.yy",}, {"name":"sh_grid_hex","order":21,"path":"shaders/sh_grid_hex/sh_grid_hex.yy",}, {"name":"s_node_lua_surface","order":18,"path":"sprites/s_node_lua_surface/s_node_lua_surface.yy",}, {"name":"node_data","order":1,"path":"scripts/node_data/node_data.yy",}, {"name":"node_wiggler","order":2,"path":"scripts/node_wiggler/node_wiggler.yy",}, {"name":"fd_GUIDE","order":5,"path":"scripts/fd_GUIDE/fd_GUIDE.yy",}, {"name":"node_fluid_update","order":3,"path":"scripts/node_fluid_update/node_fluid_update.yy",}, + {"name":"BBMOD_Collider","order":2,"path":"scripts/BBMOD_Collider/BBMOD_Collider.yy",}, {"name":"s_node_grid_noise","order":5,"path":"sprites/s_node_grid_noise/s_node_grid_noise.yy",}, {"name":"fd_rectangle_set_velocity_size","order":16,"path":"scripts/fd_rectangle_set_velocity_size/fd_rectangle_set_velocity_size.yy",}, + {"name":"__bbmod_d3d11","order":2,"path":"scripts/__bbmod_d3d11/__bbmod_d3d11.yy",}, {"name":"node_image_gif","order":6,"path":"scripts/node_image_gif/node_image_gif.yy",}, {"name":"node_iterator_each_output","order":2,"path":"scripts/node_iterator_each_output/node_iterator_each_output.yy",}, {"name":"s_node_strandSim_render_texture","order":5,"path":"sprites/s_node_strandSim_render_texture/s_node_strandSim_render_texture.yy",}, + {"name":"BBMOD_LightmapShader","order":1,"path":"scripts/BBMOD_LightmapShader/BBMOD_LightmapShader.yy",}, {"name":"sh_fd_advect_material_a_8_glsl","order":3,"path":"shaders/sh_fd_advect_material_a_8_glsl/sh_fd_advect_material_a_8_glsl.yy",}, + {"name":"BBMOD_BaseShader","order":5,"path":"scripts/BBMOD_BaseShader/BBMOD_BaseShader.yy",}, {"name":"node_color_remove","order":2,"path":"scripts/node_color_remove/node_color_remove.yy",}, {"name":"spr_gameframe_pixel","order":3,"path":"sprites/spr_gameframe_pixel/spr_gameframe_pixel.yy",}, {"name":"s_node_radial","order":53,"path":"sprites/s_node_radial/s_node_radial.yy",}, @@ -583,6 +696,7 @@ {"name":"node_feedback_input","order":1,"path":"scripts/node_feedback_input/node_feedback_input.yy",}, {"name":"s_node_base_conversion","order":10,"path":"sprites/s_node_base_conversion/s_node_base_conversion.yy",}, {"name":"s_node_loop_array","order":23,"path":"sprites/s_node_loop_array/s_node_loop_array.yy",}, + {"name":"BBMOD_ShSSAOBlur","order":2,"path":"shaders/BBMOD_ShSSAOBlur/BBMOD_ShSSAOBlur.yy",}, {"name":"s_node_iterator_amount","order":26,"path":"sprites/s_node_iterator_amount/s_node_iterator_amount.yy",}, {"name":"sh_color_adjust","order":6,"path":"shaders/sh_color_adjust/sh_color_adjust.yy",}, {"name":"sh_fd_visualize_thick_smoke_glsl","order":17,"path":"shaders/sh_fd_visualize_thick_smoke_glsl/sh_fd_visualize_thick_smoke_glsl.yy",}, @@ -601,6 +715,7 @@ {"name":"o_dialog_gradient","order":1,"path":"objects/o_dialog_gradient/o_dialog_gradient.yy",}, {"name":"sh_channel_R_grey","order":7,"path":"shaders/sh_channel_R_grey/sh_channel_R_grey.yy",}, {"name":"sh_blend_subtract","order":5,"path":"shaders/sh_blend_subtract/sh_blend_subtract.yy",}, + {"name":"BBMOD_Vec4","order":6,"path":"scripts/BBMOD_Vec4/BBMOD_Vec4.yy",}, {"name":"panel_animation","order":1,"path":"scripts/panel_animation/panel_animation.yy",}, {"name":"node_surface_replace","order":29,"path":"scripts/node_surface_replace/node_surface_replace.yy",}, {"name":"node_strand_create","order":1,"path":"scripts/node_strand_create/node_strand_create.yy",}, @@ -614,12 +729,15 @@ {"name":"sh_clean_shape","order":37,"path":"shaders/sh_clean_shape/sh_clean_shape.yy",}, {"name":"fd_rectangle_update","order":20,"path":"scripts/fd_rectangle_update/fd_rectangle_update.yy",}, {"name":"sh_posterize","order":19,"path":"shaders/sh_posterize/sh_posterize.yy",}, + {"name":"BBMOD_DirectionalLight","order":1,"path":"scripts/BBMOD_DirectionalLight/BBMOD_DirectionalLight.yy",}, {"name":"s_node_mirror","order":3,"path":"sprites/s_node_mirror/s_node_mirror.yy",}, {"name":"s_node_blur_simple","order":43,"path":"sprites/s_node_blur_simple/s_node_blur_simple.yy",}, {"name":"node_VFX_spawner","order":1,"path":"scripts/node_VFX_spawner/node_VFX_spawner.yy",}, + {"name":"__bbmod_default_material","order":4,"path":"scripts/__bbmod_default_material/__bbmod_default_material.yy",}, {"name":"_draw_defines","order":21,"path":"scripts/_draw_defines/_draw_defines.yy",}, {"name":"sh_color_replace","order":8,"path":"shaders/sh_color_replace/sh_color_replace.yy",}, {"name":"__surface","order":8,"path":"scripts/__surface/__surface.yy",}, + {"name":"BBMOD_AddVec4OverTimeModule","order":3,"path":"scripts/BBMOD_AddVec4OverTimeModule/BBMOD_AddVec4OverTimeModule.yy",}, {"name":"rotator","order":11,"path":"scripts/rotator/rotator.yy",}, {"name":"s_node_edge_detect","order":22,"path":"sprites/s_node_edge_detect/s_node_edge_detect.yy",}, {"name":"node_fluid_add_collider","order":6,"path":"scripts/node_fluid_add_collider/node_fluid_add_collider.yy",}, @@ -630,11 +748,14 @@ {"name":"node_strand_render_texture","order":7,"path":"scripts/node_strand_render_texture/node_strand_render_texture.yy",}, {"name":"luaRenderer","order":2,"path":"scripts/luaRenderer/luaRenderer.yy",}, {"name":"node_blur_zoom","order":3,"path":"scripts/node_blur_zoom/node_blur_zoom.yy",}, + {"name":"BBMOD_Ray","order":5,"path":"scripts/BBMOD_Ray/BBMOD_Ray.yy",}, {"name":"node_bloom","order":3,"path":"scripts/node_bloom/node_bloom.yy",}, {"name":"sh_bevel","order":3,"path":"shaders/sh_bevel/sh_bevel.yy",}, {"name":"sh_blend_overlay","order":15,"path":"shaders/sh_blend_overlay/sh_blend_overlay.yy",}, {"name":"node_sprite_stack","order":4,"path":"scripts/node_sprite_stack/node_sprite_stack.yy",}, {"name":"s_node_json_file_read","order":14,"path":"sprites/s_node_json_file_read/s_node_json_file_read.yy",}, + {"name":"BBMOD_Vertex","order":4,"path":"scripts/BBMOD_Vertex/BBMOD_Vertex.yy",}, + {"name":"BBMOD_OutOfRangeException","order":2,"path":"scripts/BBMOD_OutOfRangeException/BBMOD_OutOfRangeException.yy",}, {"name":"__strandSim","order":2,"path":"scripts/__strandSim/__strandSim.yy",}, {"name":"sh_shape","order":3,"path":"shaders/sh_shape/sh_shape.yy",}, {"name":"draw_line_width2","order":2,"path":"scripts/draw_line_width2/draw_line_width2.yy",}, @@ -651,6 +772,7 @@ {"name":"o_dialog_preset","order":1,"path":"objects/o_dialog_preset/o_dialog_preset.yy",}, {"name":"s_node_mesh_transform","order":1,"path":"sprites/s_node_mesh_transform/s_node_mesh_transform.yy",}, {"name":"o_dialog_tunnels","order":6,"path":"objects/o_dialog_tunnels/o_dialog_tunnels.yy",}, + {"name":"BBMOD_ShParticleLit","order":10,"path":"shaders/BBMOD_ShParticleLit/BBMOD_ShParticleLit.yy",}, {"name":"s_node_erode","order":23,"path":"sprites/s_node_erode/s_node_erode.yy",}, {"name":"node_particle","order":14,"path":"scripts/node_particle/node_particle.yy",}, {"name":"histogram_drawer","order":2,"path":"scripts/histogram_drawer/histogram_drawer.yy",}, @@ -665,6 +787,7 @@ {"name":"s_icon_64","order":2,"path":"sprites/s_icon_64/s_icon_64.yy",}, {"name":"s_node_gradient_replace","order":18,"path":"sprites/s_node_gradient_replace/s_node_gradient_replace.yy",}, {"name":"node_perlin_smear","order":6,"path":"scripts/node_perlin_smear/node_perlin_smear.yy",}, + {"name":"BBMOD_MixQuaternionOverTimeModule","order":1,"path":"scripts/BBMOD_MixQuaternionOverTimeModule/BBMOD_MixQuaternionOverTimeModule.yy",}, {"name":"node_alpha_cutoff","order":10,"path":"scripts/node_alpha_cutoff/node_alpha_cutoff.yy",}, {"name":"sh_channel_R","order":4,"path":"shaders/sh_channel_R/sh_channel_R.yy",}, {"name":"draw_circle_border","order":3,"path":"scripts/draw_circle_border/draw_circle_border.yy",}, @@ -672,33 +795,41 @@ {"name":"sliderRange","order":16,"path":"scripts/sliderRange/sliderRange.yy",}, {"name":"point_rect_overlap","order":2,"path":"scripts/point_rect_overlap/point_rect_overlap.yy",}, {"name":"s_node_bevel","order":6,"path":"sprites/s_node_bevel/s_node_bevel.yy",}, + {"name":"BBMOD_MixVec2Module","order":3,"path":"scripts/BBMOD_MixVec2Module/BBMOD_MixVec2Module.yy",}, {"name":"color_selector","order":4,"path":"scripts/color_selector/color_selector.yy",}, + {"name":"BBMOD_SprGizmo","order":9,"path":"sprites/BBMOD_SprGizmo/BBMOD_SprGizmo.yy",}, {"name":"node_trigger_bool","order":1,"path":"scripts/node_trigger_bool/node_trigger_bool.yy",}, {"name":"s_node_strandSim_gravity","order":4,"path":"sprites/s_node_strandSim_gravity/s_node_strandSim_gravity.yy",}, {"name":"node_VFX_effect_turbulence","order":11,"path":"scripts/node_VFX_effect_turbulence/node_VFX_effect_turbulence.yy",}, {"name":"sh_surface_replace_replace","order":1,"path":"shaders/sh_surface_replace_replace/sh_surface_replace_replace.yy",}, {"name":"node_ase_file_read","order":14,"path":"scripts/node_ase_file_read/node_ase_file_read.yy",}, + {"name":"BBMOD_MixVec4OverTimeModule","order":5,"path":"scripts/BBMOD_MixVec4OverTimeModule/BBMOD_MixVec4OverTimeModule.yy",}, {"name":"sh_seperate_shape_counter","order":1,"path":"shaders/sh_seperate_shape_counter/sh_seperate_shape_counter.yy",}, {"name":"s_node_stack","order":37,"path":"sprites/s_node_stack/s_node_stack.yy",}, {"name":"s_fade_up","order":3,"path":"sprites/s_fade_up/s_fade_up.yy",}, {"name":"panel_globalvar","order":4,"path":"scripts/panel_globalvar/panel_globalvar.yy",}, {"name":"node_guide","order":18,"path":"scripts/node_guide/node_guide.yy",}, {"name":"fd_rectangle_get_velocity_surface","order":24,"path":"scripts/fd_rectangle_get_velocity_surface/fd_rectangle_get_velocity_surface.yy",}, + {"name":"BBMOD_DragModule","order":1,"path":"scripts/BBMOD_DragModule/BBMOD_DragModule.yy",}, {"name":"s_node_text_char_get","order":2,"path":"sprites/s_node_text_char_get/s_node_text_char_get.yy",}, {"name":"fd_rectangle_get_initial_value_pressure","order":7,"path":"scripts/fd_rectangle_get_initial_value_pressure/fd_rectangle_get_initial_value_pressure.yy",}, {"name":"node_timeline_preview","order":2,"path":"scripts/node_timeline_preview/node_timeline_preview.yy",}, {"name":"__polygon","order":2,"path":"scripts/__polygon/__polygon.yy",}, + {"name":"BBMOD_DefaultMaterial","order":7,"path":"scripts/BBMOD_DefaultMaterial/BBMOD_DefaultMaterial.yy",}, {"name":"o_dialog_fontscrollbox","order":4,"path":"objects/o_dialog_fontscrollbox/o_dialog_fontscrollbox.yy",}, {"name":"s_node_vfx_output","order":11,"path":"sprites/s_node_vfx_output/s_node_vfx_output.yy",}, {"name":"sh_greyscale","order":10,"path":"shaders/sh_greyscale/sh_greyscale.yy",}, {"name":"gameframe_native","order":2,"path":"extensions/gameframe_native/gameframe_native.yy",}, {"name":"node_global","order":3,"path":"scripts/node_global/node_global.yy",}, + {"name":"BBMOD_MixQuaternionFromHealthModule","order":1,"path":"scripts/BBMOD_MixQuaternionFromHealthModule/BBMOD_MixQuaternionFromHealthModule.yy",}, {"name":"spr_gameframe_buttons","order":1,"path":"sprites/spr_gameframe_buttons/spr_gameframe_buttons.yy",}, {"name":"s_node_draw_stack","order":4,"path":"sprites/s_node_draw_stack/s_node_draw_stack.yy",}, {"name":"delaunay","order":1,"path":"scripts/delaunay/delaunay.yy",}, {"name":"s_node_noise_fbm","order":26,"path":"sprites/s_node_noise_fbm/s_node_noise_fbm.yy",}, {"name":"sh_color_select_content","order":1,"path":"shaders/sh_color_select_content/sh_color_select_content.yy",}, {"name":"sh_outline","order":18,"path":"shaders/sh_outline/sh_outline.yy",}, + {"name":"BBMOD_Sprite","order":14,"path":"scripts/BBMOD_Sprite/BBMOD_Sprite.yy",}, + {"name":"BBMOD_MixQuaternionModule","order":1,"path":"scripts/BBMOD_MixQuaternionModule/BBMOD_MixQuaternionModule.yy",}, {"name":"s_node_strandSim_break","order":8,"path":"sprites/s_node_strandSim_break/s_node_strandSim_break.yy",}, {"name":"s_node_scatter_point","order":4,"path":"sprites/s_node_scatter_point/s_node_scatter_point.yy",}, {"name":"node_checkerboard","order":13,"path":"scripts/node_checkerboard/node_checkerboard.yy",}, @@ -707,9 +838,12 @@ {"name":"panel_workspace","order":1,"path":"scripts/panel_workspace/panel_workspace.yy",}, {"name":"fd_rectangle_assure_surfaces_exist","order":2,"path":"scripts/fd_rectangle_assure_surfaces_exist/fd_rectangle_assure_surfaces_exist.yy",}, {"name":"fd_rectangle_material_surface_was_created","order":13,"path":"scripts/fd_rectangle_material_surface_was_created/fd_rectangle_material_surface_was_created.yy",}, + {"name":"bbmod_set_instance_id","order":12,"path":"scripts/bbmod_set_instance_id/bbmod_set_instance_id.yy",}, {"name":"s_node_fluidSim_apply_velocity","order":2,"path":"sprites/s_node_fluidSim_apply_velocity/s_node_fluidSim_apply_velocity.yy",}, + {"name":"BBMOD_SetQuaternionModule","order":1,"path":"scripts/BBMOD_SetQuaternionModule/BBMOD_SetQuaternionModule.yy",}, {"name":"fd_rectangle_destroy","order":8,"path":"scripts/fd_rectangle_destroy/fd_rectangle_destroy.yy",}, {"name":"s_node_grey_alpha","order":25,"path":"sprites/s_node_grey_alpha/s_node_grey_alpha.yy",}, + {"name":"BBMOD_ParticleMaterial","order":5,"path":"scripts/BBMOD_ParticleMaterial/BBMOD_ParticleMaterial.yy",}, {"name":"sh_bloom_pass","order":2,"path":"shaders/sh_bloom_pass/sh_bloom_pass.yy",}, {"name":"fd_rectangle_draw_view","order":4,"path":"scripts/fd_rectangle_draw_view/fd_rectangle_draw_view.yy",}, {"name":"fd_rectangle_set_material_type","order":9,"path":"scripts/fd_rectangle_set_material_type/fd_rectangle_set_material_type.yy",}, @@ -725,6 +859,7 @@ {"name":"s_node_flood_fill","order":24,"path":"sprites/s_node_flood_fill/s_node_flood_fill.yy",}, {"name":"node_rigid_force_apply","order":4,"path":"scripts/node_rigid_force_apply/node_rigid_force_apply.yy",}, {"name":"s_node_rigid_variable","order":8,"path":"sprites/s_node_rigid_variable/s_node_rigid_variable.yy",}, + {"name":"BBMOD_MixVec4FromSpeedModule","order":5,"path":"scripts/BBMOD_MixVec4FromSpeedModule/BBMOD_MixVec4FromSpeedModule.yy",}, {"name":"safe_operation","order":6,"path":"scripts/safe_operation/safe_operation.yy",}, {"name":"s_node_rigid_override","order":9,"path":"sprites/s_node_rigid_override/s_node_rigid_override.yy",}, {"name":"s_node_array_get","order":3,"path":"sprites/s_node_array_get/s_node_array_get.yy",}, @@ -736,19 +871,24 @@ {"name":"s_node_fluidSim_domain_queue","order":9,"path":"sprites/s_node_fluidSim_domain_queue/s_node_fluidSim_domain_queue.yy",}, {"name":"s_node_vfx_spawn","order":2,"path":"sprites/s_node_vfx_spawn/s_node_vfx_spawn.yy",}, {"name":"texture_set_repeat","order":1,"path":"scripts/texture_set_repeat/texture_set_repeat.yy",}, + {"name":"BBMOD_DefaultSpriteShader","order":10,"path":"scripts/BBMOD_DefaultSpriteShader/BBMOD_DefaultSpriteShader.yy",}, {"name":"fd_rectangle_update_velocity","order":22,"path":"scripts/fd_rectangle_update_velocity/fd_rectangle_update_velocity.yy",}, {"name":"sh_draw_mapping","order":4,"path":"shaders/sh_draw_mapping/sh_draw_mapping.yy",}, + {"name":"BBMOD_Cubemap","order":7,"path":"scripts/BBMOD_Cubemap/BBMOD_Cubemap.yy",}, {"name":"__bbox","order":5,"path":"scripts/__bbox/__bbox.yy",}, {"name":"node_shadow","order":14,"path":"scripts/node_shadow/node_shadow.yy",}, {"name":"s_node_color_data","order":3,"path":"sprites/s_node_color_data/s_node_color_data.yy",}, {"name":"fd_rectangle_set_repeat","order":12,"path":"scripts/fd_rectangle_set_repeat/fd_rectangle_set_repeat.yy",}, {"name":"sh_fd_calculate_velocity_divergence_glsl","order":10,"path":"shaders/sh_fd_calculate_velocity_divergence_glsl/sh_fd_calculate_velocity_divergence_glsl.yy",}, {"name":"node_simple_shape","order":4,"path":"scripts/node_simple_shape/node_simple_shape.yy",}, + {"name":"BBMOD_ShParticleDepth","order":9,"path":"shaders/BBMOD_ShParticleDepth/BBMOD_ShParticleDepth.yy",}, {"name":"node_random","order":20,"path":"scripts/node_random/node_random.yy",}, {"name":"node_atlas","order":2,"path":"scripts/node_atlas/node_atlas.yy",}, {"name":"sh_blend_sat","order":18,"path":"shaders/sh_blend_sat/sh_blend_sat.yy",}, {"name":"s_node_feedback","order":3,"path":"sprites/s_node_feedback/s_node_feedback.yy",}, + {"name":"BBMOD_RenderQueue","order":11,"path":"scripts/BBMOD_RenderQueue/BBMOD_RenderQueue.yy",}, {"name":"getGradientData","order":1,"path":"scripts/getGradientData/getGradientData.yy",}, + {"name":"BBMOD_Terrain","order":4,"path":"scripts/BBMOD_Terrain/BBMOD_Terrain.yy",}, {"name":"node_glow","order":10,"path":"scripts/node_glow/node_glow.yy",}, {"name":"sh_blend_min","order":9,"path":"shaders/sh_blend_min/sh_blend_min.yy",}, {"name":"node_polar","order":4,"path":"scripts/node_polar/node_polar.yy",}, @@ -759,6 +899,7 @@ {"name":"s_node_solid","order":15,"path":"sprites/s_node_solid/s_node_solid.yy",}, {"name":"s_node_fluidSim_vortex","order":8,"path":"sprites/s_node_fluidSim_vortex/s_node_fluidSim_vortex.yy",}, {"name":"string_hexadecimal","order":1,"path":"scripts/string_hexadecimal/string_hexadecimal.yy",}, + {"name":"BBMOD_SprDefaultNormalW","order":1,"path":"sprites/BBMOD_SprDefaultNormalW/BBMOD_SprDefaultNormalW.yy",}, {"name":"s_node_camera","order":3,"path":"sprites/s_node_camera/s_node_camera.yy",}, {"name":"sh_find_boundary","order":2,"path":"shaders/sh_find_boundary/sh_find_boundary.yy",}, {"name":"_node_strand_affector","order":10,"path":"scripts/_node_strand_affector/_node_strand_affector.yy",}, @@ -771,8 +912,10 @@ {"name":"s_node_repeat","order":23,"path":"sprites/s_node_repeat/s_node_repeat.yy",}, {"name":"s_node_array_length","order":5,"path":"sprites/s_node_array_length/s_node_array_length.yy",}, {"name":"string_splice","order":3,"path":"scripts/string_splice/string_splice.yy",}, + {"name":"__bbmod_string","order":6,"path":"scripts/__bbmod_string/__bbmod_string.yy",}, {"name":"s_node_vfx_input","order":12,"path":"sprites/s_node_vfx_input/s_node_vfx_input.yy",}, {"name":"node_strand_force_apply","order":6,"path":"scripts/node_strand_force_apply/node_strand_force_apply.yy",}, + {"name":"BBMOD_ShGizmo","order":2,"path":"shaders/BBMOD_ShGizmo/BBMOD_ShGizmo.yy",}, {"name":"sh_seperate_shape_ite","order":2,"path":"shaders/sh_seperate_shape_ite/sh_seperate_shape_ite.yy",}, {"name":"o_main","order":1,"path":"objects/o_main/o_main.yy",}, {"name":"area_function","order":2,"path":"scripts/area_function/area_function.yy",}, @@ -781,6 +924,7 @@ {"name":"number_function","order":14,"path":"scripts/number_function/number_function.yy",}, {"name":"s_node_array_shift","order":12,"path":"sprites/s_node_array_shift/s_node_array_shift.yy",}, {"name":"s_node_statistic","order":5,"path":"sprites/s_node_statistic/s_node_statistic.yy",}, + {"name":"BBMOD_ParticleSystem","order":8,"path":"scripts/BBMOD_ParticleSystem/BBMOD_ParticleSystem.yy",}, {"name":"s_node_cache_array","order":25,"path":"sprites/s_node_cache_array/s_node_cache_array.yy",}, {"name":"sh_blend_alpha_addition","order":13,"path":"shaders/sh_blend_alpha_addition/sh_blend_alpha_addition.yy",}, {"name":"widget","order":25,"path":"scripts/widget/widget.yy",}, @@ -791,6 +935,7 @@ {"name":"s_node_noise","order":8,"path":"sprites/s_node_noise/s_node_noise.yy",}, {"name":"sh_erode","order":17,"path":"shaders/sh_erode/sh_erode.yy",}, {"name":"fd_rectangle_get_velocity_dissipation_value","order":21,"path":"scripts/fd_rectangle_get_velocity_dissipation_value/fd_rectangle_get_velocity_dissipation_value.yy",}, + {"name":"BBMOD_MeshBuilder","order":1,"path":"scripts/BBMOD_MeshBuilder/BBMOD_MeshBuilder.yy",}, {"name":"node_pixel_cloud","order":15,"path":"scripts/node_pixel_cloud/node_pixel_cloud.yy",}, {"name":"s_node_crop_content","order":13,"path":"sprites/s_node_crop_content/s_node_crop_content.yy",}, {"name":"draw_line_zigzag","order":20,"path":"scripts/draw_line_zigzag/draw_line_zigzag.yy",}, @@ -801,10 +946,12 @@ {"name":"s_node_3d_plane","order":6,"path":"sprites/s_node_3d_plane/s_node_3d_plane.yy",}, {"name":"Regex","order":8,"path":"extensions/Regex/Regex.yy",}, {"name":"s_node_path_shift","order":4,"path":"sprites/s_node_path_shift/s_node_path_shift.yy",}, + {"name":"BBMOD_MixRealFromHealthModule","order":2,"path":"scripts/BBMOD_MixRealFromHealthModule/BBMOD_MixRealFromHealthModule.yy",}, {"name":"s_node_grid_tri","order":6,"path":"sprites/s_node_grid_tri/s_node_grid_tri.yy",}, {"name":"s_node_local_analyze","order":52,"path":"sprites/s_node_local_analyze/s_node_local_analyze.yy",}, {"name":"_f_p0b","order":5,"path":"fonts/_f_p0b/_f_p0b.yy",}, {"name":"o_dialog_keyframe_curve","order":2,"path":"objects/o_dialog_keyframe_curve/o_dialog_keyframe_curve.yy",}, + {"name":"BBMOD_AnimationStateMachine","order":1,"path":"scripts/BBMOD_AnimationStateMachine/BBMOD_AnimationStateMachine.yy",}, {"name":"sh_fd_visualize_pressure_glsl","order":16,"path":"shaders/sh_fd_visualize_pressure_glsl/sh_fd_visualize_pressure_glsl.yy",}, {"name":"s_node_rigidSim_force","order":2,"path":"sprites/s_node_rigidSim_force/s_node_rigidSim_force.yy",}, {"name":"s_node_vec2","order":7,"path":"sprites/s_node_vec2/s_node_vec2.yy",}, @@ -819,6 +966,7 @@ {"name":"sh_blend_add_alpha_adj","order":2,"path":"shaders/sh_blend_add_alpha_adj/sh_blend_add_alpha_adj.yy",}, {"name":"preview_overlay_area","order":3,"path":"scripts/preview_overlay_area/preview_overlay_area.yy",}, {"name":"node_text","order":10,"path":"scripts/node_text/node_text.yy",}, + {"name":"BBMOD_MixRealOverTimeModule","order":2,"path":"scripts/BBMOD_MixRealOverTimeModule/BBMOD_MixRealOverTimeModule.yy",}, {"name":"panel_collection","order":3,"path":"scripts/panel_collection/panel_collection.yy",}, {"name":"node_string_trim","order":15,"path":"scripts/node_string_trim/node_string_trim.yy",}, {"name":"lerp_float","order":1,"path":"scripts/lerp_float/lerp_float.yy",}, @@ -826,9 +974,11 @@ {"name":"fd_rectangle_get_velocity_width","order":26,"path":"scripts/fd_rectangle_get_velocity_width/fd_rectangle_get_velocity_width.yy",}, {"name":"node_convolution","order":5,"path":"scripts/node_convolution/node_convolution.yy",}, {"name":"sh_outline_only","order":35,"path":"shaders/sh_outline_only/sh_outline_only.yy",}, + {"name":"BBMOD_GravityModule","order":2,"path":"scripts/BBMOD_GravityModule/BBMOD_GravityModule.yy",}, {"name":"lcd_function","order":13,"path":"scripts/lcd_function/lcd_function.yy",}, {"name":"s_node_destray","order":18,"path":"sprites/s_node_destray/s_node_destray.yy",}, {"name":"node_color_sampler","order":3,"path":"scripts/node_color_sampler/node_color_sampler.yy",}, + {"name":"node_iterator_sort_output","order":2,"path":"scripts/node_iterator_sort_output/node_iterator_sort_output.yy",}, {"name":"s_node_fluidSim_update","order":5,"path":"sprites/s_node_fluidSim_update/s_node_fluidSim_update.yy",}, {"name":"ase_reader","order":1,"path":"scripts/ase_reader/ase_reader.yy",}, {"name":"fd_rectangle_inherit_velocity","order":12,"path":"scripts/fd_rectangle_inherit_velocity/fd_rectangle_inherit_velocity.yy",}, @@ -848,6 +998,7 @@ {"name":"fd_rectangle_set_velocity_maccormack_weight","order":15,"path":"scripts/fd_rectangle_set_velocity_maccormack_weight/fd_rectangle_set_velocity_maccormack_weight.yy",}, {"name":"s_node_array_sort","order":11,"path":"sprites/s_node_array_sort/s_node_array_sort.yy",}, {"name":"node_trail","order":16,"path":"scripts/node_trail/node_trail.yy",}, + {"name":"BBMOD_AddVec3OnCollisionModule","order":2,"path":"scripts/BBMOD_AddVec3OnCollisionModule/BBMOD_AddVec3OnCollisionModule.yy",}, {"name":"json_prettify","order":7,"path":"scripts/json_prettify/json_prettify.yy",}, {"name":"s_node_loop_output","order":13,"path":"sprites/s_node_loop_output/s_node_loop_output.yy",}, {"name":"panel_notification","order":4,"path":"scripts/panel_notification/panel_notification.yy",}, @@ -855,7 +1006,10 @@ {"name":"o_dialog_graph_view","order":4,"path":"objects/o_dialog_graph_view/o_dialog_graph_view.yy",}, {"name":"fd_rectangle_get_velocity_height","order":22,"path":"scripts/fd_rectangle_get_velocity_height/fd_rectangle_get_velocity_height.yy",}, {"name":"logger","order":1,"path":"scripts/logger/logger.yy",}, + {"name":"BBMOD_ShDefaultDepth","order":3,"path":"shaders/BBMOD_ShDefaultDepth/BBMOD_ShDefaultDepth.yy",}, + {"name":"BBMOD_ShTerrain","order":2,"path":"shaders/BBMOD_ShTerrain/BBMOD_ShTerrain.yy",}, {"name":"s_node_blur_directional","order":11,"path":"sprites/s_node_blur_directional/s_node_blur_directional.yy",}, + {"name":"BBMOD_Camera","order":1,"path":"scripts/BBMOD_Camera/BBMOD_Camera.yy",}, {"name":"s_node_average","order":50,"path":"sprites/s_node_average/s_node_average.yy",}, {"name":"s_node_sprite_sheet","order":1,"path":"sprites/s_node_sprite_sheet/s_node_sprite_sheet.yy",}, {"name":"s_node_text_length","order":9,"path":"sprites/s_node_text_length/s_node_text_length.yy",}, @@ -866,13 +1020,16 @@ {"name":"__background_get_element","order":1,"path":"scripts/__background_get_element/__background_get_element.yy",}, {"name":"window_functions","order":27,"path":"scripts/window_functions/window_functions.yy",}, {"name":"node_mesh_create_path","order":1,"path":"scripts/node_mesh_create_path/node_mesh_create_path.yy",}, + {"name":"__bbmod_async","order":1,"path":"scripts/__bbmod_async/__bbmod_async.yy",}, {"name":"sh_fd_calculate_pressure_srj_glsl","order":9,"path":"shaders/sh_fd_calculate_pressure_srj_glsl/sh_fd_calculate_pressure_srj_glsl.yy",}, {"name":"curve_bounce_function","order":1,"path":"scripts/curve_bounce_function/curve_bounce_function.yy",}, {"name":"fd_rectangle_replace_material_surface","order":23,"path":"scripts/fd_rectangle_replace_material_surface/fd_rectangle_replace_material_surface.yy",}, {"name":"pack_skyline","order":4,"path":"scripts/pack_skyline/pack_skyline.yy",}, + {"name":"BBMOD_ShDefaultBatched","order":2,"path":"shaders/BBMOD_ShDefaultBatched/BBMOD_ShDefaultBatched.yy",}, {"name":"fd_x","order":4,"path":"scripts/fd_x/fd_x.yy",}, {"name":"s_node_number","order":2,"path":"sprites/s_node_number/s_node_number.yy",}, {"name":"gif_reader","order":4,"path":"scripts/gif_reader/gif_reader.yy",}, + {"name":"BBMOD_ShDefaultAnimated","order":1,"path":"shaders/BBMOD_ShDefaultAnimated/BBMOD_ShDefaultAnimated.yy",}, {"name":"_f_p3","order":7,"path":"fonts/_f_p3/_f_p3.yy",}, {"name":"node_group_input","order":1,"path":"scripts/node_group_input/node_group_input.yy",}, {"name":"fd_rectangle_set_material_dissipation_type","order":4,"path":"scripts/fd_rectangle_set_material_dissipation_type/fd_rectangle_set_material_dissipation_type.yy",}, @@ -882,6 +1039,7 @@ {"name":"draw_rect_border","order":6,"path":"scripts/draw_rect_border/draw_rect_border.yy",}, {"name":"node_composite","order":1,"path":"scripts/node_composite/node_composite.yy",}, {"name":"s_node_path_map","order":2,"path":"sprites/s_node_path_map/s_node_path_map.yy",}, + {"name":"BBMOD_MixRealFromSpeedModule","order":2,"path":"scripts/BBMOD_MixRealFromSpeedModule/BBMOD_MixRealFromSpeedModule.yy",}, {"name":"s_node_greyscale","order":26,"path":"sprites/s_node_greyscale/s_node_greyscale.yy",}, {"name":"sh_dilate","order":4,"path":"shaders/sh_dilate/sh_dilate.yy",}, {"name":"node_image_sequence","order":1,"path":"scripts/node_image_sequence/node_image_sequence.yy",}, @@ -892,6 +1050,7 @@ {"name":"node_iterator_input","order":1,"path":"scripts/node_iterator_input/node_iterator_input.yy",}, {"name":"s_node_atlas","order":5,"path":"sprites/s_node_atlas/s_node_atlas.yy",}, {"name":"node_fluid_add","order":4,"path":"scripts/node_fluid_add/node_fluid_add.yy",}, + {"name":"BBMOD_Quaternion","order":3,"path":"scripts/BBMOD_Quaternion/BBMOD_Quaternion.yy",}, {"name":"o_dialog_output_visibility","order":2,"path":"objects/o_dialog_output_visibility/o_dialog_output_visibility.yy",}, {"name":"node_normal","order":1,"path":"scripts/node_normal/node_normal.yy",}, {"name":"s_node_glow","order":24,"path":"sprites/s_node_glow/s_node_glow.yy",}, @@ -903,7 +1062,10 @@ {"name":"draw_circle_angle","order":15,"path":"scripts/draw_circle_angle/draw_circle_angle.yy",}, {"name":"node_wrap_mesh","order":6,"path":"scripts/node_wrap_mesh/node_wrap_mesh.yy",}, {"name":"sh_alpha_hash","order":43,"path":"shaders/sh_alpha_hash/sh_alpha_hash.yy",}, + {"name":"BBMOD_MixVec3Module","order":4,"path":"scripts/BBMOD_MixVec3Module/BBMOD_MixVec3Module.yy",}, {"name":"sh_blur_alpha","order":1,"path":"shaders/sh_blur_alpha/sh_blur_alpha.yy",}, + {"name":"BBMOD_Importer","order":18,"path":"scripts/BBMOD_Importer/BBMOD_Importer.yy",}, + {"name":"BBMOD_ERenderCommand","order":8,"path":"scripts/BBMOD_ERenderCommand/BBMOD_ERenderCommand.yy",}, {"name":"value_snap","order":10,"path":"scripts/value_snap/value_snap.yy",}, {"name":"file_dropper","order":1,"path":"extensions/file_dropper/file_dropper.yy",}, {"name":"sh_mirror_mask","order":8,"path":"shaders/sh_mirror_mask/sh_mirror_mask.yy",}, @@ -913,17 +1075,20 @@ {"name":"fd_rectangle_set_velocity_dissipation_type","order":13,"path":"scripts/fd_rectangle_set_velocity_dissipation_type/fd_rectangle_set_velocity_dissipation_type.yy",}, {"name":"node_color_replacement","order":3,"path":"scripts/node_color_replacement/node_color_replacement.yy",}, {"name":"path_reader","order":5,"path":"scripts/path_reader/path_reader.yy",}, + {"name":"BBMOD_SetVec2Module","order":3,"path":"scripts/BBMOD_SetVec2Module/BBMOD_SetVec2Module.yy",}, {"name":"node_3d_repeat","order":12,"path":"scripts/node_3d_repeat/node_3d_repeat.yy",}, {"name":"point_direction_positive","order":4,"path":"scripts/point_direction_positive/point_direction_positive.yy",}, {"name":"s_node_fluidSim_add_fluid","order":3,"path":"sprites/s_node_fluidSim_add_fluid/s_node_fluidSim_add_fluid.yy",}, {"name":"node_blur_contrast","order":1,"path":"scripts/node_blur_contrast/node_blur_contrast.yy",}, {"name":"sh_trail_filler","order":50,"path":"shaders/sh_trail_filler/sh_trail_filler.yy",}, + {"name":"BBMOD_Resouce","order":19,"path":"scripts/BBMOD_Resouce/BBMOD_Resouce.yy",}, {"name":"s_node_border","order":12,"path":"sprites/s_node_border/s_node_border.yy",}, {"name":"d3_vector","order":1,"path":"scripts/d3_vector/d3_vector.yy",}, {"name":"s_node_pin","order":2,"path":"sprites/s_node_pin/s_node_pin.yy",}, {"name":"node_local_analyze","order":17,"path":"scripts/node_local_analyze/node_local_analyze.yy",}, {"name":"s_node_blur_contrast","order":10,"path":"sprites/s_node_blur_contrast/s_node_blur_contrast.yy",}, {"name":"fd_y","order":5,"path":"scripts/fd_y/fd_y.yy",}, + {"name":"bbmod_gpu_get_default_state","order":9,"path":"scripts/bbmod_gpu_get_default_state/bbmod_gpu_get_default_state.yy",}, {"name":"node_string_regex_search","order":23,"path":"scripts/node_string_regex_search/node_string_regex_search.yy",}, {"name":"s_node_text_render","order":17,"path":"sprites/s_node_text_render/s_node_text_render.yy",}, {"name":"__init_global","order":8,"path":"scripts/__init_global/__init_global.yy",}, @@ -937,9 +1102,12 @@ {"name":"string_cut","order":2,"path":"scripts/string_cut/string_cut.yy",}, {"name":"node_3d_prim_cylinder","order":7,"path":"scripts/node_3d_prim_cylinder/node_3d_prim_cylinder.yy",}, {"name":"panel_nodes","order":2,"path":"scripts/panel_nodes/panel_nodes.yy",}, + {"name":"__bbmod_init","order":15,"path":"scripts/__bbmod_init/__bbmod_init.yy",}, {"name":"sh_gradient_points","order":19,"path":"shaders/sh_gradient_points/sh_gradient_points.yy",}, {"name":"s_node_vfx_turb","order":5,"path":"sprites/s_node_vfx_turb/s_node_vfx_turb.yy",}, + {"name":"BBMOD_ShDefaultUnlit","order":9,"path":"shaders/BBMOD_ShDefaultUnlit/BBMOD_ShDefaultUnlit.yy",}, {"name":"o_dialog_l_system","order":1,"path":"objects/o_dialog_l_system/o_dialog_l_system.yy",}, + {"name":"__bbmod_defines","order":14,"path":"scripts/__bbmod_defines/__bbmod_defines.yy",}, {"name":"sh_shadow_cast_light_sep","order":1,"path":"shaders/sh_shadow_cast_light_sep/sh_shadow_cast_light_sep.yy",}, {"name":"node_grid","order":16,"path":"scripts/node_grid/node_grid.yy",}, {"name":"node_edge_detect","order":8,"path":"scripts/node_edge_detect/node_edge_detect.yy",}, @@ -953,6 +1121,7 @@ {"name":"directory_object","order":3,"path":"scripts/directory_object/directory_object.yy",}, {"name":"fd_rectangle_get_acceleration_b","order":1,"path":"scripts/fd_rectangle_get_acceleration_b/fd_rectangle_get_acceleration_b.yy",}, {"name":"surface_draw_functions","order":7,"path":"scripts/surface_draw_functions/surface_draw_functions.yy",}, + {"name":"BBMOD_Matrix","order":2,"path":"scripts/BBMOD_Matrix/BBMOD_Matrix.yy",}, {"name":"pack_shelf","order":1,"path":"scripts/pack_shelf/pack_shelf.yy",}, {"name":"s_node_path_trim","order":6,"path":"sprites/s_node_path_trim/s_node_path_trim.yy",}, {"name":"libfilesystem","order":4,"path":"extensions/libfilesystem/libfilesystem.yy",}, @@ -963,6 +1132,7 @@ {"name":"sh_blend_contrast","order":16,"path":"shaders/sh_blend_contrast/sh_blend_contrast.yy",}, {"name":"node_3d_prim_cube","order":5,"path":"scripts/node_3d_prim_cube/node_3d_prim_cube.yy",}, {"name":"s_node_fluidSim_add_collider","order":1,"path":"sprites/s_node_fluidSim_add_collider/s_node_fluidSim_add_collider.yy",}, + {"name":"BBMOD_ShDefaultUnlitAnimated","order":10,"path":"shaders/BBMOD_ShDefaultUnlitAnimated/BBMOD_ShDefaultUnlitAnimated.yy",}, {"name":"node_3d_plane","order":2,"path":"scripts/node_3d_plane/node_3d_plane.yy",}, {"name":"node_display_text","order":3,"path":"scripts/node_display_text/node_display_text.yy",}, {"name":"node_strand_update","order":3,"path":"scripts/node_strand_update/node_strand_update.yy",}, @@ -973,11 +1143,14 @@ {"name":"panel_color","order":1,"path":"scripts/panel_color/panel_color.yy",}, {"name":"s_node_line","order":7,"path":"sprites/s_node_line/s_node_line.yy",}, {"name":"fd_rectangle_get_material_dissipation_value","order":9,"path":"scripts/fd_rectangle_get_material_dissipation_value/fd_rectangle_get_material_dissipation_value.yy",}, + {"name":"BBMOD_MixVec2FromSpeedModule","order":3,"path":"scripts/BBMOD_MixVec2FromSpeedModule/BBMOD_MixVec2FromSpeedModule.yy",}, {"name":"node_counter","order":1,"path":"scripts/node_counter/node_counter.yy",}, {"name":"s_node_shape_polygon","order":25,"path":"sprites/s_node_shape_polygon/s_node_shape_polygon.yy",}, {"name":"sh_color_picker_hue","order":7,"path":"shaders/sh_color_picker_hue/sh_color_picker_hue.yy",}, {"name":"node_path_eval","order":4,"path":"scripts/node_path_eval/node_path_eval.yy",}, + {"name":"BBMOD_StaticBatch","order":1,"path":"scripts/BBMOD_StaticBatch/BBMOD_StaticBatch.yy",}, {"name":"node_vector_dot","order":27,"path":"scripts/node_vector_dot/node_vector_dot.yy",}, + {"name":"BBMOD_SprColorGradingLUT","order":3,"path":"sprites/BBMOD_SprColorGradingLUT/BBMOD_SprColorGradingLUT.yy",}, {"name":"node_path_map_area","order":1,"path":"scripts/node_path_map_area/node_path_map_area.yy",}, {"name":"s_node_timeline_preview","order":2,"path":"sprites/s_node_timeline_preview/s_node_timeline_preview.yy",}, {"name":"node_csv_file_read","order":9,"path":"scripts/node_csv_file_read/node_csv_file_read.yy",}, @@ -990,7 +1163,9 @@ {"name":"node_fluid_apply_velo","order":5,"path":"scripts/node_fluid_apply_velo/node_fluid_apply_velo.yy",}, {"name":"s_node_path_transform","order":5,"path":"sprites/s_node_path_transform/s_node_path_transform.yy",}, {"name":"s_node_path_blend","order":1,"path":"sprites/s_node_path_blend/s_node_path_blend.yy",}, + {"name":"BBMOD_ShDefaultDepthLightmap","order":6,"path":"shaders/BBMOD_ShDefaultDepthLightmap/BBMOD_ShDefaultDepthLightmap.yy",}, {"name":"s_node_3d_extrude","order":2,"path":"sprites/s_node_3d_extrude/s_node_3d_extrude.yy",}, + {"name":"BBMOD_EParticle","order":3,"path":"scripts/BBMOD_EParticle/BBMOD_EParticle.yy",}, {"name":"buttonGradient","order":4,"path":"scripts/buttonGradient/buttonGradient.yy",}, {"name":"sh_draw_downsample","order":2,"path":"shaders/sh_draw_downsample/sh_draw_downsample.yy",}, {"name":"__view_get","order":1,"path":"scripts/__view_get/__view_get.yy",}, @@ -998,26 +1173,37 @@ {"name":"fd_rectangle_create","order":7,"path":"scripts/fd_rectangle_create/fd_rectangle_create.yy",}, {"name":"s_node_level_selector","order":29,"path":"sprites/s_node_level_selector/s_node_level_selector.yy",}, {"name":"random_function","order":3,"path":"scripts/random_function/random_function.yy",}, + {"name":"BBMOD_MixVec3FromSpeedModule","order":4,"path":"scripts/BBMOD_MixVec3FromSpeedModule/BBMOD_MixVec3FromSpeedModule.yy",}, {"name":"s_node_input","order":6,"path":"sprites/s_node_input/s_node_input.yy",}, {"name":"addon","order":1,"path":"objects/addon/addon.yy",}, + {"name":"BBMOD_EmissionOverTimeModule","order":3,"path":"scripts/BBMOD_EmissionOverTimeModule/BBMOD_EmissionOverTimeModule.yy",}, {"name":"_f_h3","order":3,"path":"fonts/_f_h3/_f_h3.yy",}, {"name":"s_node_math","order":1,"path":"sprites/s_node_math/s_node_math.yy",}, {"name":"node_iterator_index","order":2,"path":"scripts/node_iterator_index/node_iterator_index.yy",}, + {"name":"BBMOD_AddVec3OverTimeModule","order":2,"path":"scripts/BBMOD_AddVec3OverTimeModule/BBMOD_AddVec3OverTimeModule.yy",}, {"name":"s_node_palette_sort","order":14,"path":"sprites/s_node_palette_sort/s_node_palette_sort.yy",}, + {"name":"BBMOD_ShDefaultDepthAnimated","order":4,"path":"shaders/BBMOD_ShDefaultDepthAnimated/BBMOD_ShDefaultDepthAnimated.yy",}, + {"name":"BBMOD_BaseRenderer","order":4,"path":"scripts/BBMOD_BaseRenderer/BBMOD_BaseRenderer.yy",}, {"name":"node_greyscale","order":5,"path":"scripts/node_greyscale/node_greyscale.yy",}, {"name":"s_node_cross_product_3d","order":11,"path":"sprites/s_node_cross_product_3d/s_node_cross_product_3d.yy",}, {"name":"node_color_adjustment","order":1,"path":"scripts/node_color_adjustment/node_color_adjustment.yy",}, {"name":"s_node_strandSim_force","order":6,"path":"sprites/s_node_strandSim_force/s_node_strandSim_force.yy",}, + {"name":"BBMOD_ParticleEmitter","order":4,"path":"scripts/BBMOD_ParticleEmitter/BBMOD_ParticleEmitter.yy",}, {"name":"s_node_array_reverse","order":8,"path":"sprites/s_node_array_reverse/s_node_array_reverse.yy",}, + {"name":"BBMOD_Shader","order":13,"path":"scripts/BBMOD_Shader/BBMOD_Shader.yy",}, {"name":"sh_ani_noise","order":4,"path":"shaders/sh_ani_noise/sh_ani_noise.yy",}, {"name":"sh_level","order":11,"path":"shaders/sh_level/sh_level.yy",}, {"name":"sh_grid_tri","order":20,"path":"shaders/sh_grid_tri/sh_grid_tri.yy",}, {"name":"s_node_text","order":1,"path":"sprites/s_node_text/s_node_text.yy",}, + {"name":"BBMOD_AddVec2OnCollisionModule","order":1,"path":"scripts/BBMOD_AddVec2OnCollisionModule/BBMOD_AddVec2OnCollisionModule.yy",}, + {"name":"__bbmod_logging","order":3,"path":"scripts/__bbmod_logging/__bbmod_logging.yy",}, {"name":"s_node_ase_file","order":18,"path":"sprites/s_node_ase_file/s_node_ase_file.yy",}, {"name":"draw_line_round","order":4,"path":"scripts/draw_line_round/draw_line_round.yy",}, {"name":"vectorBox","order":18,"path":"scripts/vectorBox/vectorBox.yy",}, {"name":"sh_blend_normal_dim","order":10,"path":"shaders/sh_blend_normal_dim/sh_blend_normal_dim.yy",}, + {"name":"BBMOD_AddVec4OnCollisionModule","order":3,"path":"scripts/BBMOD_AddVec4OnCollisionModule/BBMOD_AddVec4OnCollisionModule.yy",}, {"name":"node_skew","order":11,"path":"scripts/node_skew/node_skew.yy",}, + {"name":"bbmod_surface_check","order":9,"path":"scripts/bbmod_surface_check/bbmod_surface_check.yy",}, {"name":"s_node_dot_product","order":13,"path":"sprites/s_node_dot_product/s_node_dot_product.yy",}, {"name":"s_node_canvas","order":3,"path":"sprites/s_node_canvas/s_node_canvas.yy",}, {"name":"sh_downsample","order":1,"path":"shaders/sh_downsample/sh_downsample.yy",}, @@ -1034,23 +1220,29 @@ {"name":"s_node_array_add","order":1,"path":"sprites/s_node_array_add/s_node_array_add.yy",}, {"name":"s_node_image_sequence_to_anim","order":9,"path":"sprites/s_node_image_sequence_to_anim/s_node_image_sequence_to_anim.yy",}, {"name":"_node_VFX_spawner","order":13,"path":"scripts/_node_VFX_spawner/_node_VFX_spawner.yy",}, + {"name":"BBMOD_ShDefaultLightmap","order":7,"path":"shaders/BBMOD_ShDefaultLightmap/BBMOD_ShDefaultLightmap.yy",}, {"name":"sh_channel_B_grey","order":9,"path":"shaders/sh_channel_B_grey/sh_channel_B_grey.yy",}, {"name":"draw_UI_scale","order":8,"path":"scripts/draw_UI_scale/draw_UI_scale.yy",}, + {"name":"BBMOD_Material","order":10,"path":"scripts/BBMOD_Material/BBMOD_Material.yy",}, {"name":"s_node_strandSim_update","order":1,"path":"sprites/s_node_strandSim_update/s_node_strandSim_update.yy",}, {"name":"s_node_RGB_combine","order":45,"path":"sprites/s_node_RGB_combine/s_node_RGB_combine.yy",}, {"name":"s_node_shadow_cast","order":49,"path":"sprites/s_node_shadow_cast/s_node_shadow_cast.yy",}, {"name":"path_function","order":4,"path":"scripts/path_function/path_function.yy",}, {"name":"fd_rectangle_set_velocity_dissipation_value","order":14,"path":"scripts/fd_rectangle_set_velocity_dissipation_value/fd_rectangle_set_velocity_dissipation_value.yy",}, + {"name":"BBMOD_ShDefaultDepthBatched","order":5,"path":"shaders/BBMOD_ShDefaultDepthBatched/BBMOD_ShDefaultDepthBatched.yy",}, {"name":"panel_history","order":2,"path":"scripts/panel_history/panel_history.yy",}, {"name":"sh_channel_H","order":3,"path":"shaders/sh_channel_H/sh_channel_H.yy",}, {"name":"s_node_RGB","order":34,"path":"sprites/s_node_RGB/s_node_RGB.yy",}, {"name":"fd_rectangle_draw","order":9,"path":"scripts/fd_rectangle_draw/fd_rectangle_draw.yy",}, + {"name":"BBMOD_MixRealModule","order":2,"path":"scripts/BBMOD_MixRealModule/BBMOD_MixRealModule.yy",}, + {"name":"BBMOD_ShInstanceIDAnimated","order":6,"path":"shaders/BBMOD_ShInstanceIDAnimated/BBMOD_ShInstanceIDAnimated.yy",}, {"name":"oRigidbody","order":2,"path":"objects/oRigidbody/oRigidbody.yy",}, {"name":"node_grey_to_alpha","order":4,"path":"scripts/node_grey_to_alpha/node_grey_to_alpha.yy",}, {"name":"sh_blend_add","order":7,"path":"shaders/sh_blend_add/sh_blend_add.yy",}, {"name":"node_de_stray","order":1,"path":"scripts/node_de_stray/node_de_stray.yy",}, {"name":"sh_channel_G","order":2,"path":"shaders/sh_channel_G/sh_channel_G.yy",}, {"name":"s_node_dilate","order":19,"path":"sprites/s_node_dilate/s_node_dilate.yy",}, + {"name":"BBMOD_State","order":2,"path":"scripts/BBMOD_State/BBMOD_State.yy",}, {"name":"node_value","order":6,"path":"scripts/node_value/node_value.yy",}, {"name":"draw_line_curve","order":5,"path":"scripts/draw_line_curve/draw_line_curve.yy",}, {"name":"sh_blend_screen","order":3,"path":"shaders/sh_blend_screen/sh_blend_screen.yy",}, @@ -1064,11 +1256,13 @@ {"name":"sh_warp_4points","order":9,"path":"shaders/sh_warp_4points/sh_warp_4points.yy",}, {"name":"_f_p2","order":4,"path":"fonts/_f_p2/_f_p2.yy",}, {"name":"fd_rectangle_get_pressure_iteration_type","order":17,"path":"scripts/fd_rectangle_get_pressure_iteration_type/fd_rectangle_get_pressure_iteration_type.yy",}, + {"name":"BBMOD_PostProcessor","order":1,"path":"scripts/BBMOD_PostProcessor/BBMOD_PostProcessor.yy",}, {"name":"node_text_file_write","order":11,"path":"scripts/node_text_file_write/node_text_file_write.yy",}, {"name":"sh_lum2alpha","order":38,"path":"shaders/sh_lum2alpha/sh_lum2alpha.yy",}, {"name":"button","order":2,"path":"scripts/button/button.yy",}, {"name":"s_node_3d_sphere","order":8,"path":"sprites/s_node_3d_sphere/s_node_3d_sphere.yy",}, {"name":"fd_rectangle_replace_velocity","order":16,"path":"scripts/fd_rectangle_replace_velocity/fd_rectangle_replace_velocity.yy",}, + {"name":"BBMOD_ShInstanceID","order":5,"path":"shaders/BBMOD_ShInstanceID/BBMOD_ShInstanceID.yy",}, {"name":"node_VFX_effector","order":6,"path":"scripts/node_VFX_effector/node_VFX_effector.yy",}, {"name":"node_path_shift","order":5,"path":"scripts/node_path_shift/node_path_shift.yy",}, {"name":"s_node_3d_cylinder","order":1,"path":"sprites/s_node_3d_cylinder/s_node_3d_cylinder.yy",}, @@ -1077,6 +1271,7 @@ {"name":"s_node_regex_replace","order":10,"path":"sprites/s_node_regex_replace/s_node_regex_replace.yy",}, {"name":"paddingBox","order":9,"path":"scripts/paddingBox/paddingBox.yy",}, {"name":"fd_rectangle_set_visualization_shader","order":18,"path":"scripts/fd_rectangle_set_visualization_shader/fd_rectangle_set_visualization_shader.yy",}, + {"name":"BBMOD_ParticleShader","order":7,"path":"scripts/BBMOD_ParticleShader/BBMOD_ParticleShader.yy",}, {"name":"s_node_ase_layer","order":17,"path":"sprites/s_node_ase_layer/s_node_ase_layer.yy",}, {"name":"_f_p1","order":1,"path":"fonts/_f_p1/_f_p1.yy",}, {"name":"tuple_functions","order":5,"path":"scripts/tuple_functions/tuple_functions.yy",}, @@ -1088,10 +1283,12 @@ {"name":"bin_function","order":11,"path":"scripts/bin_function/bin_function.yy",}, {"name":"s_node_feedback_output","order":9,"path":"sprites/s_node_feedback_output/s_node_feedback_output.yy",}, {"name":"sh_surface_replace_fast_find","order":2,"path":"shaders/sh_surface_replace_fast_find/sh_surface_replace_fast_find.yy",}, + {"name":"BBMOD_BaseMaterial","order":3,"path":"scripts/BBMOD_BaseMaterial/BBMOD_BaseMaterial.yy",}, {"name":"node_3d_combine","order":10,"path":"scripts/node_3d_combine/node_3d_combine.yy",}, {"name":"fd_rectangle_add_material","order":4,"path":"scripts/fd_rectangle_add_material/fd_rectangle_add_material.yy",}, {"name":"fd_rectangle_set_target","order":18,"path":"scripts/fd_rectangle_set_target/fd_rectangle_set_target.yy",}, {"name":"draw_line_dashed","order":7,"path":"scripts/draw_line_dashed/draw_line_dashed.yy",}, + {"name":"BBMOD_SpotLight","order":6,"path":"scripts/BBMOD_SpotLight/BBMOD_SpotLight.yy",}, {"name":"pseudo_regex","order":7,"path":"scripts/pseudo_regex/pseudo_regex.yy",}, {"name":"node_invert","order":6,"path":"scripts/node_invert/node_invert.yy",}, {"name":"o_dialog_history","order":3,"path":"objects/o_dialog_history/o_dialog_history.yy",}, @@ -1108,12 +1305,15 @@ {"name":"s_node_cache","order":27,"path":"sprites/s_node_cache/s_node_cache.yy",}, {"name":"node_repeat","order":26,"path":"scripts/node_repeat/node_repeat.yy",}, {"name":"sh_fd_advect_velocity_1_glsl","order":7,"path":"shaders/sh_fd_advect_velocity_1_glsl/sh_fd_advect_velocity_1_glsl.yy",}, + {"name":"BBMOD_SphereCollider","order":7,"path":"scripts/BBMOD_SphereCollider/BBMOD_SphereCollider.yy",}, {"name":"mac_window_step","order":1,"path":"scripts/mac_window_step/mac_window_step.yy",}, {"name":"s_node_image","order":4,"path":"sprites/s_node_image/s_node_image.yy",}, {"name":"__node_value_processor","order":7,"path":"scripts/__node_value_processor/__node_value_processor.yy",}, + {"name":"BBMOD_ShParticleUnlit","order":11,"path":"shaders/BBMOD_ShParticleUnlit/BBMOD_ShParticleUnlit.yy",}, {"name":"s_node_group_input","order":16,"path":"sprites/s_node_group_input/s_node_group_input.yy",}, {"name":"sample_projects","order":6,"path":"scripts/sample_projects/sample_projects.yy",}, {"name":"load_function","order":2,"path":"scripts/load_function/load_function.yy",}, + {"name":"BBMOD_VertexFormat","order":5,"path":"scripts/BBMOD_VertexFormat/BBMOD_VertexFormat.yy",}, {"name":"draw_fit","order":12,"path":"scripts/draw_fit/draw_fit.yy",}, {"name":"s_node_morph_surface","order":56,"path":"sprites/s_node_morph_surface/s_node_morph_surface.yy",}, {"name":"fd_rectangle_get_velocity_dissipation_type","order":20,"path":"scripts/fd_rectangle_get_velocity_dissipation_type/fd_rectangle_get_velocity_dissipation_type.yy",}, @@ -1124,6 +1324,7 @@ {"name":"node_path_array","order":11,"path":"scripts/node_path_array/node_path_array.yy",}, {"name":"node_scale","order":8,"path":"scripts/node_scale/node_scale.yy",}, {"name":"sh_displace","order":5,"path":"shaders/sh_displace/sh_displace.yy",}, + {"name":"BBMOD_DefaultShader","order":9,"path":"scripts/BBMOD_DefaultShader/BBMOD_DefaultShader.yy",}, {"name":"fd_rectangle_get_material_height","order":10,"path":"scripts/fd_rectangle_get_material_height/fd_rectangle_get_material_height.yy",}, {"name":"rangeBox","order":10,"path":"scripts/rangeBox/rangeBox.yy",}, {"name":"node_VFX_effect_accelerate","order":7,"path":"scripts/node_VFX_effect_accelerate/node_VFX_effect_accelerate.yy",}, @@ -1133,6 +1334,7 @@ {"name":"sh_sdf_dist","order":4,"path":"shaders/sh_sdf_dist/sh_sdf_dist.yy",}, {"name":"node_path_plot","order":12,"path":"scripts/node_path_plot/node_path_plot.yy",}, {"name":"sh_de_corner","order":15,"path":"shaders/sh_de_corner/sh_de_corner.yy",}, + {"name":"BBMOD_Vec3","order":5,"path":"scripts/BBMOD_Vec3/BBMOD_Vec3.yy",}, {"name":"node_rigid_object_spawner","order":6,"path":"scripts/node_rigid_object_spawner/node_rigid_object_spawner.yy",}, {"name":"o_dialog_graph_connection","order":5,"path":"objects/o_dialog_graph_connection/o_dialog_graph_connection.yy",}, {"name":"sh_fd_advect_velocity_0_glsl","order":6,"path":"shaders/sh_fd_advect_velocity_0_glsl/sh_fd_advect_velocity_0_glsl.yy",}, @@ -1140,6 +1342,7 @@ {"name":"sprite_loader","order":11,"path":"scripts/sprite_loader/sprite_loader.yy",}, {"name":"fd_rectangle_get_material_surface","order":12,"path":"scripts/fd_rectangle_get_material_surface/fd_rectangle_get_material_surface.yy",}, {"name":"fd_rectangle_clear","order":6,"path":"scripts/fd_rectangle_clear/fd_rectangle_clear.yy",}, + {"name":"bbmod_get_calling_function_name","order":7,"path":"scripts/bbmod_get_calling_function_name/bbmod_get_calling_function_name.yy",}, {"name":"__rectangle","order":7,"path":"scripts/__rectangle/__rectangle.yy",}, {"name":"s_node_iterator_index","order":8,"path":"sprites/s_node_iterator_index/s_node_iterator_index.yy",}, {"name":"preferences","order":5,"path":"scripts/preferences/preferences.yy",}, @@ -1148,12 +1351,14 @@ {"name":"s_node_json_file_write","order":13,"path":"sprites/s_node_json_file_write/s_node_json_file_write.yy",}, {"name":"fd_rectangle_get_material_time_step","order":13,"path":"scripts/fd_rectangle_get_material_time_step/fd_rectangle_get_material_time_step.yy",}, {"name":"node_pack_sprites","order":4,"path":"scripts/node_pack_sprites/node_pack_sprites.yy",}, + {"name":"BBMOD_AddVec2OverTimeModule","order":1,"path":"scripts/BBMOD_AddVec2OverTimeModule/BBMOD_AddVec2OverTimeModule.yy",}, {"name":"font_data","order":10,"path":"scripts/font_data/font_data.yy",}, {"name":"node_camera","order":3,"path":"scripts/node_camera/node_camera.yy",}, {"name":"s_node_color_replace","order":8,"path":"sprites/s_node_color_replace/s_node_color_replace.yy",}, {"name":"draw_surface_functions","order":4,"path":"scripts/draw_surface_functions/draw_surface_functions.yy",}, {"name":"node_rigid_variable","order":8,"path":"scripts/node_rigid_variable/node_rigid_variable.yy",}, {"name":"s_node_zoom","order":54,"path":"sprites/s_node_zoom/s_node_zoom.yy",}, + {"name":"BBMOD_Property","order":1,"path":"scripts/BBMOD_Property/BBMOD_Property.yy",}, {"name":"node_noise_fbm","order":30,"path":"scripts/node_noise_fbm/node_noise_fbm.yy",}, {"name":"sh_channel_V","order":6,"path":"shaders/sh_channel_V/sh_channel_V.yy",}, {"name":"__shapes","order":3,"path":"scripts/__shapes/__shapes.yy",}, @@ -1161,9 +1366,12 @@ {"name":"shell_helper","order":20,"path":"scripts/shell_helper/shell_helper.yy",}, {"name":"node_strand_collision","order":8,"path":"scripts/node_strand_collision/node_strand_collision.yy",}, {"name":"s_node_atlas_get","order":2,"path":"sprites/s_node_atlas_get/s_node_atlas_get.yy",}, + {"name":"BBMOD_ShInstanceIDLightmap","order":8,"path":"shaders/BBMOD_ShInstanceIDLightmap/BBMOD_ShInstanceIDLightmap.yy",}, {"name":"sh_flood_fill_it","order":1,"path":"shaders/sh_flood_fill_it/sh_flood_fill_it.yy",}, + {"name":"node_iterator_sort_input","order":1,"path":"scripts/node_iterator_sort_input/node_iterator_sort_input.yy",}, {"name":"draw_corner","order":19,"path":"scripts/draw_corner/draw_corner.yy",}, {"name":"o_dialog_preference","order":7,"path":"objects/o_dialog_preference/o_dialog_preference.yy",}, + {"name":"BBMOD_ShInstanceIDBatched","order":7,"path":"shaders/BBMOD_ShInstanceIDBatched/BBMOD_ShInstanceIDBatched.yy",}, {"name":"node_wrap","order":2,"path":"scripts/node_wrap/node_wrap.yy",}, {"name":"node_3d_prim_sphere","order":13,"path":"scripts/node_3d_prim_sphere/node_3d_prim_sphere.yy",}, {"name":"dialog_management","order":2,"path":"scripts/dialog_management/dialog_management.yy",}, @@ -1185,9 +1393,11 @@ {"name":"sh_fd_visualize_pixel_art_fiery_smoke_glsl","order":14,"path":"shaders/sh_fd_visualize_pixel_art_fiery_smoke_glsl/sh_fd_visualize_pixel_art_fiery_smoke_glsl.yy",}, {"name":"node_ase_layer","order":15,"path":"scripts/node_ase_layer/node_ase_layer.yy",}, {"name":"sh_local_analyze","order":47,"path":"shaders/sh_local_analyze/sh_local_analyze.yy",}, + {"name":"bbmod_json_load","order":8,"path":"scripts/bbmod_json_load/bbmod_json_load.yy",}, {"name":"s_node_array_zip","order":13,"path":"sprites/s_node_array_zip/s_node_array_zip.yy",}, {"name":"fd_rectangle_get_material_width","order":15,"path":"scripts/fd_rectangle_get_material_width/fd_rectangle_get_material_width.yy",}, {"name":"font_sprite_loader","order":10,"path":"scripts/font_sprite_loader/font_sprite_loader.yy",}, + {"name":"BBMOD_FrustumCollider","order":3,"path":"scripts/BBMOD_FrustumCollider/BBMOD_FrustumCollider.yy",}, {"name":"s_node_noise_aniso","order":9,"path":"sprites/s_node_noise_aniso/s_node_noise_aniso.yy",}, {"name":"s_node_gradient_palette","order":16,"path":"sprites/s_node_gradient_palette/s_node_gradient_palette.yy",}, {"name":"node_array_shuffle","order":22,"path":"scripts/node_array_shuffle/node_array_shuffle.yy",}, diff --git a/PixelComposer.yyp b/PixelComposer.yyp index 8795ddc31..bcf4343e1 100644 --- a/PixelComposer.yyp +++ b/PixelComposer.yyp @@ -19,7 +19,11 @@ }, "defaultScriptType": 1, "Folders": [ + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"_Extensions","folderPath":"folders/_Extensions.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Gameframe","folderPath":"folders/_Extensions/Gameframe.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"MAC","folderPath":"folders/_Extensions/MAC.yy",}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"addons","folderPath":"folders/addons.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"custom","folderPath":"folders/addons/custom.yy",}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"key displayer","folderPath":"folders/addons/key displayer.yy",}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"animation_curve","folderPath":"folders/animation_curve.yy",}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"dialog","folderPath":"folders/dialog.yy",}, @@ -31,9 +35,6 @@ {"resourceType":"GMFolder","resourceVersion":"1.0","name":"menu","folderPath":"folders/dialog/menu.yy",}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"preview","folderPath":"folders/dialog/preview.yy",}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"widget","folderPath":"folders/dialog/widget.yy",}, - {"resourceType":"GMFolder","resourceVersion":"1.0","name":"_Extensions","folderPath":"folders/_Extensions.yy",}, - {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Gameframe","folderPath":"folders/_Extensions/Gameframe.yy",}, - {"resourceType":"GMFolder","resourceVersion":"1.0","name":"MAC","folderPath":"folders/_Extensions/MAC.yy",}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"font","folderPath":"folders/font.yy",}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"functions","folderPath":"folders/functions.yy",}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"3d","folderPath":"folders/functions/3d.yy",}, @@ -179,11 +180,146 @@ {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Steamworks","folderPath":"folders/Steamworks.yy",}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"UGC","folderPath":"folders/Steamworks/UGC.yy",}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"widgets","folderPath":"folders/widgets.yy",}, - {"resourceType":"GMFolder","resourceVersion":"1.0","name":"custom","folderPath":"folders/addons/custom.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"for sort","folderPath":"folders/nodes/data/iterate/for sort.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"BBMOD","folderPath":"folders/_Extensions/BBMOD.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"ColMesh","folderPath":"folders/_Extensions/BBMOD/ColMesh.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Core","folderPath":"folders/_Extensions/BBMOD/Core.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Animation","folderPath":"folders/_Extensions/BBMOD/Core/Animation.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Batching","folderPath":"folders/_Extensions/BBMOD/Core/Batching.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Camera","folderPath":"folders/_Extensions/BBMOD/Core/Camera.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Debug","folderPath":"folders/_Extensions/BBMOD/Core/Debug.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"DefaultRenderer","folderPath":"folders/_Extensions/BBMOD/Core/DefaultRenderer.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Deprecated","folderPath":"folders/_Extensions/BBMOD/Core/DefaultRenderer/Deprecated.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Shaders","folderPath":"folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Sprites","folderPath":"folders/_Extensions/BBMOD/Core/DefaultRenderer/Sprites.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Exceptions","folderPath":"folders/_Extensions/BBMOD/Core/Exceptions.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Interfaces","folderPath":"folders/_Extensions/BBMOD/Core/Interfaces.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Lights","folderPath":"folders/_Extensions/BBMOD/Core/Lights.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Math","folderPath":"folders/_Extensions/BBMOD/Core/Math.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Model","folderPath":"folders/_Extensions/BBMOD/Core/Model.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Properties","folderPath":"folders/_Extensions/BBMOD/Core/Properties.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Rendering","folderPath":"folders/_Extensions/BBMOD/Core/Rendering.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Utils","folderPath":"folders/_Extensions/BBMOD/Core/Utils.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Gizmo","folderPath":"folders/_Extensions/BBMOD/Gizmo.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"OBJImporter","folderPath":"folders/_Extensions/BBMOD/OBJImporter.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Particles","folderPath":"folders/_Extensions/BBMOD/Particles.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Modules","folderPath":"folders/_Extensions/BBMOD/Particles/Modules.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Collision","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Collision.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Emission","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Emission.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Shape","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Emission/Shape.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Event","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Event.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Kill","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Kill.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Physics","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Physics.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Rotation","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Rotation.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Universal","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Universal.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"AddPropertyOnCollision","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOnCollision.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"AddPropertyOverTime","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOverTime.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"MixProperty","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Universal/MixProperty.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"MixPropertyFromHealth","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromHealth.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"MixPropertyFromSpeed","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromSpeed.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"MixPropertyOverTime","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyOverTime.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"SetProperty","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Universal/SetProperty.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Velocity","folderPath":"folders/_Extensions/BBMOD/Particles/Modules/Velocity.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Raycasting","folderPath":"folders/_Extensions/BBMOD/Raycasting.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Rendering","folderPath":"folders/_Extensions/BBMOD/Rendering.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"FXAA","folderPath":"folders/_Extensions/BBMOD/Rendering/FXAA.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"PostProcessing","folderPath":"folders/_Extensions/BBMOD/Rendering/PostProcessing.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Sky","folderPath":"folders/_Extensions/BBMOD/Rendering/Sky.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"SSAO","folderPath":"folders/_Extensions/BBMOD/Rendering/SSAO.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Save","folderPath":"folders/_Extensions/BBMOD/Save.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"StateMachine","folderPath":"folders/_Extensions/BBMOD/StateMachine.yy",}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Terrain","folderPath":"folders/_Extensions/BBMOD/Terrain.yy",}, ], "IncludedFiles": [ {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"ApolloHelp.html","CopyToMask":-1,"filePath":"datafiles",}, {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Assets.zip","CopyToMask":-1,"filePath":"datafiles/data",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"assimp-vc143-mt.dll","CopyToMask":-1,"filePath":"datafiles/data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"assimp-vc143-mt.dll","CopyToMask":64,"filePath":"datafiles/Data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"BBMOD","CopyToMask":-1,"filePath":"datafiles/data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"BBMOD","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"BBMOD","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"BBMOD.dll","CopyToMask":-1,"filePath":"datafiles/data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"BBMOD.dll","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"BBMOD.exe","CopyToMask":-1,"filePath":"datafiles/data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"BBMOD.exe","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"libassimp.5.dylib","CopyToMask":-1,"filePath":"datafiles/data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"libassimp.5.dylib","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"libassimp.5.dylib","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"libBBMOD.dylib","CopyToMask":-1,"filePath":"datafiles/data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"libBBMOD.dylib","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"libBBMOD.dylib","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"LICENSE-Assimp","CopyToMask":-1,"filePath":"datafiles/data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"LICENSE-Assimp","CopyToMask":64,"filePath":"datafiles/Data/BBMOD",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"gizmo.blend","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Models",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"GizmoMove.bbmod","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Models",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"GizmoMove.bbmod","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Models",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"GizmoRotate.bbmod","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Models",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"GizmoRotate.bbmod","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Models",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"GizmoScale.bbmod","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Models",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"GizmoScale.bbmod","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Models",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"rotateGizmo.fbx","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Models",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sphere.bbmod","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Models",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sphere.bbmod","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Models",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL-10.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL-10.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL-15.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL-15.png","CopyToMask":3035461389054378222,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL-5.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL-5.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+0.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+0.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+10.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+10.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+15.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+15.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+20.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+20.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+30.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+30.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+40.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+40.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+5.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+5.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+50.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+50.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+60.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+60.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+70.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+70.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+80.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+80.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+90.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"IBL+90.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky-10.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky-10.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky-15.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky-15.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky-5.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky-5.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+0.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+0.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+10.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+10.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+15.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+15.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+20.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+20.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+30.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+30.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+40.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+40.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+5.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+5.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+50.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+50.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+60.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+60.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+70.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+70.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+80.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+80.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+90.png","CopyToMask":-1,"filePath":"datafiles/data/BBMOD/Skies",}, + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Sky+90.png","CopyToMask":-1,"filePath":"datafiles/Data/BBMOD/Skies",}, {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Collections.zip","CopyToMask":-1,"filePath":"datafiles/data",}, {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Guides.zip","CopyToMask":-1,"filePath":"datafiles/data",}, {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"icon.png","CopyToMask":-1,"filePath":"datafiles/data",}, @@ -834,9 +970,11 @@ "IDEVersion": "2023.2.0.71", }, "resources": [ + {"id":{"name":"__bbmod_path","path":"scripts/__bbmod_path/__bbmod_path.yy",},}, {"id":{"name":"s_node_corner","path":"sprites/s_node_corner/s_node_corner.yy",},}, {"id":{"name":"sh_cell_noise_crystal","path":"shaders/sh_cell_noise_crystal/sh_cell_noise_crystal.yy",},}, {"id":{"name":"panel_function","path":"scripts/panel_function/panel_function.yy",},}, + {"id":{"name":"BBMOD_EPropertyType","path":"scripts/BBMOD_EPropertyType/BBMOD_EPropertyType.yy",},}, {"id":{"name":"node_time_remap","path":"scripts/node_time_remap/node_time_remap.yy",},}, {"id":{"name":"sh_perlin","path":"shaders/sh_perlin/sh_perlin.yy",},}, {"id":{"name":"sh_normal_light","path":"shaders/sh_normal_light/sh_normal_light.yy",},}, @@ -871,16 +1009,21 @@ {"id":{"name":"s_node_fluidSim_turbulence","path":"sprites/s_node_fluidSim_turbulence/s_node_fluidSim_turbulence.yy",},}, {"id":{"name":"node_blur_radial","path":"scripts/node_blur_radial/node_blur_radial.yy",},}, {"id":{"name":"node_2d_light","path":"scripts/node_2d_light/node_2d_light.yy",},}, + {"id":{"name":"BBMOD_SprDefaultSpecularColor","path":"sprites/BBMOD_SprDefaultSpecularColor/BBMOD_SprDefaultSpecularColor.yy",},}, + {"id":{"name":"BBMOD_DefaultLightmapMaterial","path":"scripts/BBMOD_DefaultLightmapMaterial/BBMOD_DefaultLightmapMaterial.yy",},}, {"id":{"name":"node_rigid_activation","path":"scripts/node_rigid_activation/node_rigid_activation.yy",},}, {"id":{"name":"s_node_color","path":"sprites/s_node_color/s_node_color.yy",},}, {"id":{"name":"s_node_image_gif","path":"sprites/s_node_image_gif/s_node_image_gif.yy",},}, {"id":{"name":"node_VFX_effect_wind","path":"scripts/node_VFX_effect_wind/node_VFX_effect_wind.yy",},}, {"id":{"name":"s_node_tunnel_out","path":"sprites/s_node_tunnel_out/s_node_tunnel_out.yy",},}, {"id":{"name":"__background_set_element","path":"scripts/__background_set_element/__background_set_element.yy",},}, + {"id":{"name":"BBMOD_ShDefaultSprite","path":"shaders/BBMOD_ShDefaultSprite/BBMOD_ShDefaultSprite.yy",},}, {"id":{"name":"s_node_3d_obj","path":"sprites/s_node_3d_obj/s_node_3d_obj.yy",},}, + {"id":{"name":"BBMOD_MixColorOverTimeModule","path":"scripts/BBMOD_MixColorOverTimeModule/BBMOD_MixColorOverTimeModule.yy",},}, {"id":{"name":"s_node_scale","path":"sprites/s_node_scale/s_node_scale.yy",},}, {"id":{"name":"sh_color_picker_value","path":"shaders/sh_color_picker_value/sh_color_picker_value.yy",},}, {"id":{"name":"textInput","path":"scripts/textInput/textInput.yy",},}, + {"id":{"name":"BBMOD_Renderer","path":"scripts/BBMOD_Renderer/BBMOD_Renderer.yy",},}, {"id":{"name":"node_dither","path":"scripts/node_dither/node_dither.yy",},}, {"id":{"name":"sh_perlin_smear","path":"shaders/sh_perlin_smear/sh_perlin_smear.yy",},}, {"id":{"name":"node_iterate_filter","path":"scripts/node_iterate_filter/node_iterate_filter.yy",},}, @@ -890,6 +1033,7 @@ {"id":{"name":"sh_corner","path":"shaders/sh_corner/sh_corner.yy",},}, {"id":{"name":"node_array","path":"scripts/node_array/node_array.yy",},}, {"id":{"name":"pack_corner","path":"scripts/pack_corner/pack_corner.yy",},}, + {"id":{"name":"BBMOD_SphereEmissionModule","path":"scripts/BBMOD_SphereEmissionModule/BBMOD_SphereEmissionModule.yy",},}, {"id":{"name":"sh_colorize","path":"shaders/sh_colorize/sh_colorize.yy",},}, {"id":{"name":"node_string","path":"scripts/node_string/node_string.yy",},}, {"id":{"name":"cross_product","path":"scripts/cross_product/cross_product.yy",},}, @@ -899,11 +1043,15 @@ {"id":{"name":"s_node_loop_input","path":"sprites/s_node_loop_input/s_node_loop_input.yy",},}, {"id":{"name":"node_strand_length_adjust","path":"scripts/node_strand_length_adjust/node_strand_length_adjust.yy",},}, {"id":{"name":"fd_rectangle_add_material_surface","path":"scripts/fd_rectangle_add_material_surface/fd_rectangle_add_material_surface.yy",},}, + {"id":{"name":"BBMOD_ImageBasedLight","path":"scripts/BBMOD_ImageBasedLight/BBMOD_ImageBasedLight.yy",},}, {"id":{"name":"perlin_noise","path":"scripts/perlin_noise/perlin_noise.yy",},}, {"id":{"name":"fd_rectangle_replace_material","path":"scripts/fd_rectangle_replace_material/fd_rectangle_replace_material.yy",},}, + {"id":{"name":"node_iterate_sort","path":"scripts/node_iterate_sort/node_iterate_sort.yy",},}, {"id":{"name":"node_array_set","path":"scripts/node_array_set/node_array_set.yy",},}, {"id":{"name":"node_functions","path":"scripts/node_functions/node_functions.yy",},}, {"id":{"name":"node_math","path":"scripts/node_math/node_math.yy",},}, + {"id":{"name":"BBMOD_MixQuaternionFromSpeedModule","path":"scripts/BBMOD_MixQuaternionFromSpeedModule/BBMOD_MixQuaternionFromSpeedModule.yy",},}, + {"id":{"name":"BBMOD_SprWhite","path":"sprites/BBMOD_SprWhite/BBMOD_SprWhite.yy",},}, {"id":{"name":"fd_rectangle_replace_material_advanced","path":"scripts/fd_rectangle_replace_material_advanced/fd_rectangle_replace_material_advanced.yy",},}, {"id":{"name":"array_functions","path":"scripts/array_functions/array_functions.yy",},}, {"id":{"name":"sh_blur_final","path":"shaders/sh_blur_final/sh_blur_final.yy",},}, @@ -912,8 +1060,10 @@ {"id":{"name":"s_node_path_sample","path":"sprites/s_node_path_sample/s_node_path_sample.yy",},}, {"id":{"name":"s_node_colorize","path":"sprites/s_node_colorize/s_node_colorize.yy",},}, {"id":{"name":"panel_preview","path":"scripts/panel_preview/panel_preview.yy",},}, + {"id":{"name":"BBMOD_ShInstanceHighlight","path":"shaders/BBMOD_ShInstanceHighlight/BBMOD_ShInstanceHighlight.yy",},}, {"id":{"name":"s_node_sepearte_shape","path":"sprites/s_node_sepearte_shape/s_node_sepearte_shape.yy",},}, {"id":{"name":"s_node_text_join","path":"sprites/s_node_text_join/s_node_text_join.yy",},}, + {"id":{"name":"BBMOD_SprBlack","path":"sprites/BBMOD_SprBlack/BBMOD_SprBlack.yy",},}, {"id":{"name":"s_node_polar","path":"sprites/s_node_polar/s_node_polar.yy",},}, {"id":{"name":"draw_set_blend_mode_ext","path":"scripts/draw_set_blend_mode_ext/draw_set_blend_mode_ext.yy",},}, {"id":{"name":"s_node_noise_simplex","path":"sprites/s_node_noise_simplex/s_node_noise_simplex.yy",},}, @@ -926,12 +1076,16 @@ {"id":{"name":"libdlgmodule","path":"extensions/libdlgmodule/libdlgmodule.yy",},}, {"id":{"name":"s_node_alpha_grey","path":"sprites/s_node_alpha_grey/s_node_alpha_grey.yy",},}, {"id":{"name":"node_blend","path":"scripts/node_blend/node_blend.yy",},}, + {"id":{"name":"BBMOD_MixVec3OverTimeModule","path":"scripts/BBMOD_MixVec3OverTimeModule/BBMOD_MixVec3OverTimeModule.yy",},}, {"id":{"name":"fd_rectangle_set_pressure_iteration_type","path":"scripts/fd_rectangle_set_pressure_iteration_type/fd_rectangle_set_pressure_iteration_type.yy",},}, + {"id":{"name":"s_node_sort_array","path":"sprites/s_node_sort_array/s_node_sort_array.yy",},}, {"id":{"name":"node_array_reverse","path":"scripts/node_array_reverse/node_array_reverse.yy",},}, + {"id":{"name":"__bbmod_debug","path":"scripts/__bbmod_debug/__bbmod_debug.yy",},}, {"id":{"name":"Tweener","path":"scripts/Tweener/Tweener.yy",},}, {"id":{"name":"_f_h1","path":"fonts/_f_h1/_f_h1.yy",},}, {"id":{"name":"_f_h2","path":"fonts/_f_h2/_f_h2.yy",},}, {"id":{"name":"s_node_level","path":"sprites/s_node_level/s_node_level.yy",},}, + {"id":{"name":"BBMOD_MixVec2OverTimeModule","path":"scripts/BBMOD_MixVec2OverTimeModule/BBMOD_MixVec2OverTimeModule.yy",},}, {"id":{"name":"node_scatter","path":"scripts/node_scatter/node_scatter.yy",},}, {"id":{"name":"s_node_bloom","path":"sprites/s_node_bloom/s_node_bloom.yy",},}, {"id":{"name":"node_image","path":"scripts/node_image/node_image.yy",},}, @@ -940,10 +1094,15 @@ {"id":{"name":"s_node_gradient_data","path":"sprites/s_node_gradient_data/s_node_gradient_data.yy",},}, {"id":{"name":"s_node_vfx_render","path":"sprites/s_node_vfx_render/s_node_vfx_render.yy",},}, {"id":{"name":"node_stack","path":"scripts/node_stack/node_stack.yy",},}, + {"id":{"name":"BBMOD_DLL","path":"scripts/BBMOD_DLL/BBMOD_DLL.yy",},}, {"id":{"name":"sh_cell_noise","path":"shaders/sh_cell_noise/sh_cell_noise.yy",},}, + {"id":{"name":"BBMOD_CameraHTML5","path":"extensions/BBMOD_CameraHTML5/BBMOD_CameraHTML5.yy",},}, {"id":{"name":"s_node_rigidSim_object","path":"sprites/s_node_rigidSim_object/s_node_rigidSim_object.yy",},}, {"id":{"name":"sh_blend_max","path":"shaders/sh_blend_max/sh_blend_max.yy",},}, {"id":{"name":"s_node_color_out","path":"sprites/s_node_color_out/s_node_color_out.yy",},}, + {"id":{"name":"__BBMOD_ShCheckVTF","path":"shaders/__BBMOD_ShCheckVTF/__BBMOD_ShCheckVTF.yy",},}, + {"id":{"name":"bbmod_lerp_delta_time","path":"scripts/bbmod_lerp_delta_time/bbmod_lerp_delta_time.yy",},}, + {"id":{"name":"BBMOD_StateMachine","path":"scripts/BBMOD_StateMachine/BBMOD_StateMachine.yy",},}, {"id":{"name":"_3D","path":"scripts/_3D/_3D.yy",},}, {"id":{"name":"node_vector_cross2D","path":"scripts/node_vector_cross2D/node_vector_cross2D.yy",},}, {"id":{"name":"sh_corner_erode","path":"shaders/sh_corner_erode/sh_corner_erode.yy",},}, @@ -961,6 +1120,7 @@ {"id":{"name":"shader_functions","path":"scripts/shader_functions/shader_functions.yy",},}, {"id":{"name":"node_gradient_points","path":"scripts/node_gradient_points/node_gradient_points.yy",},}, {"id":{"name":"_f_code","path":"fonts/_f_code/_f_code.yy",},}, + {"id":{"name":"BBMOD_Mesh","path":"scripts/BBMOD_Mesh/BBMOD_Mesh.yy",},}, {"id":{"name":"sh_combine_rgb","path":"shaders/sh_combine_rgb/sh_combine_rgb.yy",},}, {"id":{"name":"s_node_strandSim","path":"sprites/s_node_strandSim/s_node_strandSim.yy",},}, {"id":{"name":"object_get_depth","path":"scripts/object_get_depth/object_get_depth.yy",},}, @@ -968,6 +1128,7 @@ {"id":{"name":"colToVec4","path":"scripts/colToVec4/colToVec4.yy",},}, {"id":{"name":"s_node_vfx","path":"sprites/s_node_vfx/s_node_vfx.yy",},}, {"id":{"name":"fd_rectangle_get_velocity_maccormack_weight","path":"scripts/fd_rectangle_get_velocity_maccormack_weight/fd_rectangle_get_velocity_maccormack_weight.yy",},}, + {"id":{"name":"BBMOD_SetRealModule","path":"scripts/BBMOD_SetRealModule/BBMOD_SetRealModule.yy",},}, {"id":{"name":"s_node_mesh_path","path":"sprites/s_node_mesh_path/s_node_mesh_path.yy",},}, {"id":{"name":"sh_fd_advect_material_rgba_8_glsl","path":"shaders/sh_fd_advect_material_rgba_8_glsl/sh_fd_advect_material_rgba_8_glsl.yy",},}, {"id":{"name":"fd_rectangle_get_material_dissipation_type","path":"scripts/fd_rectangle_get_material_dissipation_type/fd_rectangle_get_material_dissipation_type.yy",},}, @@ -979,6 +1140,7 @@ {"id":{"name":"node_path_reverse","path":"scripts/node_path_reverse/node_path_reverse.yy",},}, {"id":{"name":"fd_rectangle_get_acceleration_y","path":"scripts/fd_rectangle_get_acceleration_y/fd_rectangle_get_acceleration_y.yy",},}, {"id":{"name":"sh_simplex","path":"shaders/sh_simplex/sh_simplex.yy",},}, + {"id":{"name":"__bbmod_array","path":"scripts/__bbmod_array/__bbmod_array.yy",},}, {"id":{"name":"libborderless","path":"extensions/libborderless/libborderless.yy",},}, {"id":{"name":"node_noise_grid_tri","path":"scripts/node_noise_grid_tri/node_noise_grid_tri.yy",},}, {"id":{"name":"blurSurface","path":"scripts/blurSurface/blurSurface.yy",},}, @@ -988,6 +1150,7 @@ {"id":{"name":"sh_texture_atlas","path":"shaders/sh_texture_atlas/sh_texture_atlas.yy",},}, {"id":{"name":"s_node_fluidSim_domain","path":"sprites/s_node_fluidSim_domain/s_node_fluidSim_domain.yy",},}, {"id":{"name":"s_node_displace","path":"sprites/s_node_displace/s_node_displace.yy",},}, + {"id":{"name":"BBMOD_ParticleModule","path":"scripts/BBMOD_ParticleModule/BBMOD_ParticleModule.yy",},}, {"id":{"name":"sh_polar","path":"shaders/sh_polar/sh_polar.yy",},}, {"id":{"name":"s_node_warp_mesh","path":"sprites/s_node_warp_mesh/s_node_warp_mesh.yy",},}, {"id":{"name":"sh_pixel_cloud","path":"shaders/sh_pixel_cloud/sh_pixel_cloud.yy",},}, @@ -1012,10 +1175,12 @@ {"id":{"name":"s_node_2d_light","path":"sprites/s_node_2d_light/s_node_2d_light.yy",},}, {"id":{"name":"s_node_trail","path":"sprites/s_node_trail/s_node_trail.yy",},}, {"id":{"name":"fd_rectangle_update_view","path":"scripts/fd_rectangle_update_view/fd_rectangle_update_view.yy",},}, + {"id":{"name":"BBMOD_ShTerrainUnlit","path":"shaders/BBMOD_ShTerrainUnlit/BBMOD_ShTerrainUnlit.yy",},}, {"id":{"name":"s_node_rigidSim_renderer","path":"sprites/s_node_rigidSim_renderer/s_node_rigidSim_renderer.yy",},}, {"id":{"name":"__init_background","path":"scripts/__init_background/__init_background.yy",},}, {"id":{"name":"__node","path":"scripts/__node/__node.yy",},}, {"id":{"name":"fd_rectangle_add_velocity","path":"scripts/fd_rectangle_add_velocity/fd_rectangle_add_velocity.yy",},}, + {"id":{"name":"BBMOD_MixColorFromSpeedModule","path":"scripts/BBMOD_MixColorFromSpeedModule/BBMOD_MixColorFromSpeedModule.yy",},}, {"id":{"name":"sh_level_selector","path":"shaders/sh_level_selector/sh_level_selector.yy",},}, {"id":{"name":"string_eval","path":"scripts/string_eval/string_eval.yy",},}, {"id":{"name":"s_node_struct","path":"sprites/s_node_struct/s_node_struct.yy",},}, @@ -1049,6 +1214,7 @@ {"id":{"name":"s_node_3d_cone","path":"sprites/s_node_3d_cone/s_node_3d_cone.yy",},}, {"id":{"name":"s_node_compose","path":"sprites/s_node_compose/s_node_compose.yy",},}, {"id":{"name":"s_node_switch","path":"sprites/s_node_switch/s_node_switch.yy",},}, + {"id":{"name":"BBMOD_Gizmo","path":"scripts/BBMOD_Gizmo/BBMOD_Gizmo.yy",},}, {"id":{"name":"s_node_crop","path":"sprites/s_node_crop/s_node_crop.yy",},}, {"id":{"name":"__VFX","path":"scripts/__VFX/__VFX.yy",},}, {"id":{"name":"s_menu_black","path":"sprites/s_menu_black/s_menu_black.yy",},}, @@ -1059,11 +1225,13 @@ {"id":{"name":"font_loader","path":"scripts/font_loader/font_loader.yy",},}, {"id":{"name":"node_iterator_filter_input","path":"scripts/node_iterator_filter_input/node_iterator_filter_input.yy",},}, {"id":{"name":"point_rotate","path":"scripts/point_rotate/point_rotate.yy",},}, + {"id":{"name":"BBMOD_MixVec2FromHealthModule","path":"scripts/BBMOD_MixVec2FromHealthModule/BBMOD_MixVec2FromHealthModule.yy",},}, {"id":{"name":"s_node_vfx_variable","path":"sprites/s_node_vfx_variable/s_node_vfx_variable.yy",},}, {"id":{"name":"node_struct","path":"scripts/node_struct/node_struct.yy",},}, {"id":{"name":"node_displacement","path":"scripts/node_displacement/node_displacement.yy",},}, {"id":{"name":"mask_function","path":"scripts/mask_function/mask_function.yy",},}, {"id":{"name":"panel_palette","path":"scripts/panel_palette/panel_palette.yy",},}, + {"id":{"name":"BBMOD_AnimationPlayer","path":"scripts/BBMOD_AnimationPlayer/BBMOD_AnimationPlayer.yy",},}, {"id":{"name":"text_file","path":"scripts/text_file/text_file.yy",},}, {"id":{"name":"sh_trail_filler_pass1","path":"shaders/sh_trail_filler_pass1/sh_trail_filler_pass1.yy",},}, {"id":{"name":"s_node_vfx_wind","path":"sprites/s_node_vfx_wind/s_node_vfx_wind.yy",},}, @@ -1075,6 +1243,7 @@ {"id":{"name":"locale_data","path":"scripts/locale_data/locale_data.yy",},}, {"id":{"name":"s_workshop_bg","path":"sprites/s_workshop_bg/s_workshop_bg.yy",},}, {"id":{"name":"o_dialog_scrollbox","path":"objects/o_dialog_scrollbox/o_dialog_scrollbox.yy",},}, + {"id":{"name":"__bbmod_particles","path":"scripts/__bbmod_particles/__bbmod_particles.yy",},}, {"id":{"name":"s_node_palette","path":"sprites/s_node_palette/s_node_palette.yy",},}, {"id":{"name":"curve_damping_function","path":"scripts/curve_damping_function/curve_damping_function.yy",},}, {"id":{"name":"fd_rectangle_get_collision_mask_surface","path":"scripts/fd_rectangle_get_collision_mask_surface/fd_rectangle_get_collision_mask_surface.yy",},}, @@ -1084,9 +1253,12 @@ {"id":{"name":"fd_draw_surface_to_collision_mask_surface","path":"scripts/fd_draw_surface_to_collision_mask_surface/fd_draw_surface_to_collision_mask_surface.yy",},}, {"id":{"name":"sh_blur_box_contrast","path":"shaders/sh_blur_box_contrast/sh_blur_box_contrast.yy",},}, {"id":{"name":"sh_fd_calculate_pressure_jacobi_glsl","path":"shaders/sh_fd_calculate_pressure_jacobi_glsl/sh_fd_calculate_pressure_jacobi_glsl.yy",},}, + {"id":{"name":"BBMOD_IRenderTarget","path":"scripts/BBMOD_IRenderTarget/BBMOD_IRenderTarget.yy",},}, {"id":{"name":"node_color_from_rgb","path":"scripts/node_color_from_rgb/node_color_from_rgb.yy",},}, {"id":{"name":"node_struct_get","path":"scripts/node_struct_get/node_struct_get.yy",},}, + {"id":{"name":"BBMOD_MixColorModule","path":"scripts/BBMOD_MixColorModule/BBMOD_MixColorModule.yy",},}, {"id":{"name":"node_transform","path":"scripts/node_transform/node_transform.yy",},}, + {"id":{"name":"BBMOD_Class","path":"scripts/BBMOD_Class/BBMOD_Class.yy",},}, {"id":{"name":"curveBox","path":"scripts/curveBox/curveBox.yy",},}, {"id":{"name":"s_node_iterator_length","path":"sprites/s_node_iterator_length/s_node_iterator_length.yy",},}, {"id":{"name":"preview_overlay_vector","path":"scripts/preview_overlay_vector/preview_overlay_vector.yy",},}, @@ -1100,15 +1272,19 @@ {"id":{"name":"json_file","path":"scripts/json_file/json_file.yy",},}, {"id":{"name":"s_node_curve_edit","path":"sprites/s_node_curve_edit/s_node_curve_edit.yy",},}, {"id":{"name":"node_gradient","path":"scripts/node_gradient/node_gradient.yy",},}, + {"id":{"name":"bbmod_vtf_is_supported","path":"scripts/bbmod_vtf_is_supported/bbmod_vtf_is_supported.yy",},}, {"id":{"name":"textBox","path":"scripts/textBox/textBox.yy",},}, {"id":{"name":"pathArrayBox","path":"scripts/pathArrayBox/pathArrayBox.yy",},}, {"id":{"name":"node_statistic","path":"scripts/node_statistic/node_statistic.yy",},}, {"id":{"name":"sh_draw_surface_part_tiled","path":"shaders/sh_draw_surface_part_tiled/sh_draw_surface_part_tiled.yy",},}, {"id":{"name":"o_dialog_add_node","path":"objects/o_dialog_add_node/o_dialog_add_node.yy",},}, + {"id":{"name":"bbmod_cmp","path":"scripts/bbmod_cmp/bbmod_cmp.yy",},}, {"id":{"name":"s_node_curve_eval","path":"sprites/s_node_curve_eval/s_node_curve_eval.yy",},}, + {"id":{"name":"BBMOD_ShDefaultUnlitBatched","path":"shaders/BBMOD_ShDefaultUnlitBatched/BBMOD_ShDefaultUnlitBatched.yy",},}, {"id":{"name":"s_node_path_reverse","path":"sprites/s_node_path_reverse/s_node_path_reverse.yy",},}, {"id":{"name":"fd_rectangle_get_pressure_width","path":"scripts/fd_rectangle_get_pressure_width/fd_rectangle_get_pressure_width.yy",},}, {"id":{"name":"s_node_strandSim_render","path":"sprites/s_node_strandSim_render/s_node_strandSim_render.yy",},}, + {"id":{"name":"BBMOD_Node","path":"scripts/BBMOD_Node/BBMOD_Node.yy",},}, {"id":{"name":"sh_blend_normal","path":"shaders/sh_blend_normal/sh_blend_normal.yy",},}, {"id":{"name":"node_feedback_output","path":"scripts/node_feedback_output/node_feedback_output.yy",},}, {"id":{"name":"node_lua_surface","path":"scripts/node_lua_surface/node_lua_surface.yy",},}, @@ -1127,13 +1303,15 @@ {"id":{"name":"s_node_blur","path":"sprites/s_node_blur/s_node_blur.yy",},}, {"id":{"name":"textArea","path":"scripts/textArea/textArea.yy",},}, {"id":{"name":"s_node_gradient_4points","path":"sprites/s_node_gradient_4points/s_node_gradient_4points.yy",},}, - {"id":{"name":"regEdit","path":"extensions/regEdit/regEdit.yy",},}, + {"id":{"name":"BBMOD_PointLight","path":"scripts/BBMOD_PointLight/BBMOD_PointLight.yy",},}, {"id":{"name":"s_node_gradient_out","path":"sprites/s_node_gradient_out/s_node_gradient_out.yy",},}, {"id":{"name":"s_node_vec3","path":"sprites/s_node_vec3/s_node_vec3.yy",},}, {"id":{"name":"s_node_strandSim_create","path":"sprites/s_node_strandSim_create/s_node_strandSim_create.yy",},}, + {"id":{"name":"__bbmod_render_pass","path":"scripts/__bbmod_render_pass/__bbmod_render_pass.yy",},}, {"id":{"name":"node_gradient_shift","path":"scripts/node_gradient_shift/node_gradient_shift.yy",},}, {"id":{"name":"sh_vertex_normal_pass","path":"shaders/sh_vertex_normal_pass/sh_vertex_normal_pass.yy",},}, {"id":{"name":"node_vector_cross3D","path":"scripts/node_vector_cross3D/node_vector_cross3D.yy",},}, + {"id":{"name":"BBMOD_ShSSAO","path":"shaders/BBMOD_ShSSAO/BBMOD_ShSSAO.yy",},}, {"id":{"name":"s_node_pack_sprite","path":"sprites/s_node_pack_sprite/s_node_pack_sprite.yy",},}, {"id":{"name":"surface_valid","path":"scripts/surface_valid/surface_valid.yy",},}, {"id":{"name":"steam_ugc_collection","path":"scripts/steam_ugc_collection/steam_ugc_collection.yy",},}, @@ -1144,12 +1322,15 @@ {"id":{"name":"s_node_invert","path":"sprites/s_node_invert/s_node_invert.yy",},}, {"id":{"name":"draw_text_delimiter","path":"scripts/draw_text_delimiter/draw_text_delimiter.yy",},}, {"id":{"name":"s_node_path_anchor","path":"sprites/s_node_path_anchor/s_node_path_anchor.yy",},}, + {"id":{"name":"BBMOD_NotImplementedException","path":"scripts/BBMOD_NotImplementedException/BBMOD_NotImplementedException.yy",},}, {"id":{"name":"node_array_get","path":"scripts/node_array_get/node_array_get.yy",},}, + {"id":{"name":"BBMOD_ShExtractSplatmapLayer","path":"shaders/BBMOD_ShExtractSplatmapLayer/BBMOD_ShExtractSplatmapLayer.yy",},}, {"id":{"name":"Apollo","path":"extensions/Apollo/Apollo.yy",},}, {"id":{"name":"node_lua_compute","path":"scripts/node_lua_compute/node_lua_compute.yy",},}, {"id":{"name":"sh_grid","path":"shaders/sh_grid/sh_grid.yy",},}, {"id":{"name":"sh_twirl","path":"shaders/sh_twirl/sh_twirl.yy",},}, {"id":{"name":"s_node_shape","path":"sprites/s_node_shape/s_node_shape.yy",},}, + {"id":{"name":"BBMOD_MixEmissionModule","path":"scripts/BBMOD_MixEmissionModule/BBMOD_MixEmissionModule.yy",},}, {"id":{"name":"time_source","path":"scripts/time_source/time_source.yy",},}, {"id":{"name":"string_formatting","path":"scripts/string_formatting/string_formatting.yy",},}, {"id":{"name":"node_level_selector","path":"scripts/node_level_selector/node_level_selector.yy",},}, @@ -1159,6 +1340,7 @@ {"id":{"name":"node_color_mix","path":"scripts/node_color_mix/node_color_mix.yy",},}, {"id":{"name":"s_node_array_range","path":"sprites/s_node_array_range/s_node_array_range.yy",},}, {"id":{"name":"sh_fd_advect_material_a_16_glsl","path":"shaders/sh_fd_advect_material_a_16_glsl/sh_fd_advect_material_a_16_glsl.yy",},}, + {"id":{"name":"BBMOD_MixVec3FromHealthModule","path":"scripts/BBMOD_MixVec3FromHealthModule/BBMOD_MixVec3FromHealthModule.yy",},}, {"id":{"name":"distribution_function","path":"scripts/distribution_function/distribution_function.yy",},}, {"id":{"name":"sh_blur_radial","path":"shaders/sh_blur_radial/sh_blur_radial.yy",},}, {"id":{"name":"node_blur","path":"scripts/node_blur/node_blur.yy",},}, @@ -1167,14 +1349,19 @@ {"id":{"name":"json_minify","path":"scripts/json_minify/json_minify.yy",},}, {"id":{"name":"sh_fd_vortex","path":"shaders/sh_fd_vortex/sh_fd_vortex.yy",},}, {"id":{"name":"s_node_loop","path":"sprites/s_node_loop/s_node_loop.yy",},}, + {"id":{"name":"BBMOD_AABBCollider","path":"scripts/BBMOD_AABBCollider/BBMOD_AABBCollider.yy",},}, {"id":{"name":"node_zigzag","path":"scripts/node_zigzag/node_zigzag.yy",},}, {"id":{"name":"node_equation","path":"scripts/node_equation/node_equation.yy",},}, {"id":{"name":"node_string_join","path":"scripts/node_string_join/node_string_join.yy",},}, + {"id":{"name":"BBMOD_ShGizmoSelect","path":"shaders/BBMOD_ShGizmoSelect/BBMOD_ShGizmoSelect.yy",},}, + {"id":{"name":"BBMOD_ResourceManager","path":"scripts/BBMOD_ResourceManager/BBMOD_ResourceManager.yy",},}, + {"id":{"name":"BBMOD_RaycastResult","path":"scripts/BBMOD_RaycastResult/BBMOD_RaycastResult.yy",},}, {"id":{"name":"fd_rectangle_get_visualization_shader","path":"scripts/fd_rectangle_get_visualization_shader/fd_rectangle_get_visualization_shader.yy",},}, {"id":{"name":"preset_data","path":"scripts/preset_data/preset_data.yy",},}, {"id":{"name":"_f_h5","path":"fonts/_f_h5/_f_h5.yy",},}, {"id":{"name":"node_color","path":"scripts/node_color/node_color.yy",},}, {"id":{"name":"node_string_split","path":"scripts/node_string_split/node_string_split.yy",},}, + {"id":{"name":"BBMOD_PunctualLight","path":"scripts/BBMOD_PunctualLight/BBMOD_PunctualLight.yy",},}, {"id":{"name":"meta_data","path":"scripts/meta_data/meta_data.yy",},}, {"id":{"name":"node_find_pixel","path":"scripts/node_find_pixel/node_find_pixel.yy",},}, {"id":{"name":"node_scatter_points","path":"scripts/node_scatter_points/node_scatter_points.yy",},}, @@ -1188,12 +1375,16 @@ {"id":{"name":"sh_sample_points","path":"shaders/sh_sample_points/sh_sample_points.yy",},}, {"id":{"name":"node_combine_rgb","path":"scripts/node_combine_rgb/node_combine_rgb.yy",},}, {"id":{"name":"fd_rectangle_draw_part","path":"scripts/fd_rectangle_draw_part/fd_rectangle_draw_part.yy",},}, + {"id":{"name":"BBMOD_SetVec3Module","path":"scripts/BBMOD_SetVec3Module/BBMOD_SetVec3Module.yy",},}, + {"id":{"name":"BBMOD_SprParticle","path":"sprites/BBMOD_SprParticle/BBMOD_SprParticle.yy",},}, {"id":{"name":"checkbox","path":"scripts/checkbox/checkbox.yy",},}, + {"id":{"name":"BBMOD_EmissionModule","path":"scripts/BBMOD_EmissionModule/BBMOD_EmissionModule.yy",},}, {"id":{"name":"node_string_regex_replace","path":"scripts/node_string_regex_replace/node_string_regex_replace.yy",},}, {"id":{"name":"s_node_particle","path":"sprites/s_node_particle/s_node_particle.yy",},}, {"id":{"name":"sh_flood_fill_thres","path":"shaders/sh_flood_fill_thres/sh_flood_fill_thres.yy",},}, {"id":{"name":"s_node_random","path":"sprites/s_node_random/s_node_random.yy",},}, {"id":{"name":"s_node_color_remove","path":"sprites/s_node_color_remove/s_node_color_remove.yy",},}, + {"id":{"name":"__bbmod_colmesh","path":"scripts/__bbmod_colmesh/__bbmod_colmesh.yy",},}, {"id":{"name":"sh_average","path":"shaders/sh_average/sh_average.yy",},}, {"id":{"name":"sh_warp_4points_pers","path":"shaders/sh_warp_4points_pers/sh_warp_4points_pers.yy",},}, {"id":{"name":"node_mirror","path":"scripts/node_mirror/node_mirror.yy",},}, @@ -1202,6 +1393,7 @@ {"id":{"name":"node_path_anchor","path":"scripts/node_path_anchor/node_path_anchor.yy",},}, {"id":{"name":"s_node_path_wave","path":"sprites/s_node_path_wave/s_node_path_wave.yy",},}, {"id":{"name":"string_function","path":"scripts/string_function/string_function.yy",},}, + {"id":{"name":"BBMOD_MixVec4Module","path":"scripts/BBMOD_MixVec4Module/BBMOD_MixVec4Module.yy",},}, {"id":{"name":"o_dialog_palette","path":"objects/o_dialog_palette/o_dialog_palette.yy",},}, {"id":{"name":"sh_blend_hue","path":"shaders/sh_blend_hue/sh_blend_hue.yy",},}, {"id":{"name":"_f_p0","path":"fonts/_f_p0/_f_p0.yy",},}, @@ -1231,6 +1423,7 @@ {"id":{"name":"node_chromatic_aberration","path":"scripts/node_chromatic_aberration/node_chromatic_aberration.yy",},}, {"id":{"name":"draw_line_elbow_diag","path":"scripts/draw_line_elbow_diag/draw_line_elbow_diag.yy",},}, {"id":{"name":"node_pin","path":"scripts/node_pin/node_pin.yy",},}, + {"id":{"name":"BBMOD_IRenderable","path":"scripts/BBMOD_IRenderable/BBMOD_IRenderable.yy",},}, {"id":{"name":"_node_fluid_nodes","path":"scripts/_node_fluid_nodes/_node_fluid_nodes.yy",},}, {"id":{"name":"sh_noise","path":"shaders/sh_noise/sh_noise.yy",},}, {"id":{"name":"sh_skew","path":"shaders/sh_skew/sh_skew.yy",},}, @@ -1246,6 +1439,7 @@ {"id":{"name":"fd_rectangle_get_collision_mask_sprite_image","path":"scripts/fd_rectangle_get_collision_mask_sprite_image/fd_rectangle_get_collision_mask_sprite_image.yy",},}, {"id":{"name":"s_node_stripe","path":"sprites/s_node_stripe/s_node_stripe.yy",},}, {"id":{"name":"s_node_lua_global","path":"sprites/s_node_lua_global/s_node_lua_global.yy",},}, + {"id":{"name":"BBMOD_MixVec4FromHealthModule","path":"scripts/BBMOD_MixVec4FromHealthModule/BBMOD_MixVec4FromHealthModule.yy",},}, {"id":{"name":"s_node_fluidSim_group","path":"sprites/s_node_fluidSim_group/s_node_fluidSim_group.yy",},}, {"id":{"name":"sh_grey_alpha","path":"shaders/sh_grey_alpha/sh_grey_alpha.yy",},}, {"id":{"name":"sh_normal","path":"shaders/sh_normal/sh_normal.yy",},}, @@ -1255,37 +1449,50 @@ {"id":{"name":"s_node_lua_compute","path":"sprites/s_node_lua_compute/s_node_lua_compute.yy",},}, {"id":{"name":"buttonPalette","path":"scripts/buttonPalette/buttonPalette.yy",},}, {"id":{"name":"fd_rectangle_draw_stretched","path":"scripts/fd_rectangle_draw_stretched/fd_rectangle_draw_stretched.yy",},}, + {"id":{"name":"__bbmod_fog","path":"scripts/__bbmod_fog/__bbmod_fog.yy",},}, {"id":{"name":"s_node_wiggler","path":"sprites/s_node_wiggler/s_node_wiggler.yy",},}, {"id":{"name":"sh_edge_detect","path":"shaders/sh_edge_detect/sh_edge_detect.yy",},}, {"id":{"name":"s_node_area","path":"sprites/s_node_area/s_node_area.yy",},}, {"id":{"name":"type_conversion","path":"scripts/type_conversion/type_conversion.yy",},}, {"id":{"name":"node_keyframe","path":"scripts/node_keyframe/node_keyframe.yy",},}, {"id":{"name":"sh_threshold","path":"shaders/sh_threshold/sh_threshold.yy",},}, + {"id":{"name":"BBMOD_AnimationInstance","path":"scripts/BBMOD_AnimationInstance/BBMOD_AnimationInstance.yy",},}, {"id":{"name":"fd_rectangle_set_initial_value_pressure","path":"scripts/fd_rectangle_set_initial_value_pressure/fd_rectangle_set_initial_value_pressure.yy",},}, {"id":{"name":"ac_flash","path":"animcurves/ac_flash/ac_flash.yy",},}, {"id":{"name":"o_dialog_drag_folder","path":"objects/o_dialog_drag_folder/o_dialog_drag_folder.yy",},}, {"id":{"name":"node_VFX_effect_repel","path":"scripts/node_VFX_effect_repel/node_VFX_effect_repel.yy",},}, + {"id":{"name":"BBMOD_ShSky","path":"shaders/BBMOD_ShSky/BBMOD_ShSky.yy",},}, {"id":{"name":"s_node_alpha_cut","path":"sprites/s_node_alpha_cut/s_node_alpha_cut.yy",},}, {"id":{"name":"node_iterator_length","path":"scripts/node_iterator_length/node_iterator_length.yy",},}, + {"id":{"name":"BBMOD_DefaultLightmapShader","path":"scripts/BBMOD_DefaultLightmapShader/BBMOD_DefaultLightmapShader.yy",},}, {"id":{"name":"node_VFX_effect_attract","path":"scripts/node_VFX_effect_attract/node_VFX_effect_attract.yy",},}, {"id":{"name":"node_fluid_repulse","path":"scripts/node_fluid_repulse/node_fluid_repulse.yy",},}, + {"id":{"name":"BBMOD_AddRealOverTimeModule","path":"scripts/BBMOD_AddRealOverTimeModule/BBMOD_AddRealOverTimeModule.yy",},}, {"id":{"name":"s_node_text_splice","path":"sprites/s_node_text_splice/s_node_text_splice.yy",},}, + {"id":{"name":"BBMOD_MixColorFromHealthModule","path":"scripts/BBMOD_MixColorFromHealthModule/BBMOD_MixColorFromHealthModule.yy",},}, {"id":{"name":"__atlas","path":"scripts/__atlas/__atlas.yy",},}, {"id":{"name":"node_3d_object_transform","path":"scripts/node_3d_object_transform/node_3d_object_transform.yy",},}, + {"id":{"name":"__bbmod_gizmo","path":"scripts/__bbmod_gizmo/__bbmod_gizmo.yy",},}, + {"id":{"name":"BBMOD_PlaneCollider","path":"scripts/BBMOD_PlaneCollider/BBMOD_PlaneCollider.yy",},}, {"id":{"name":"sh_draw_single_channel","path":"shaders/sh_draw_single_channel/sh_draw_single_channel.yy",},}, {"id":{"name":"draw_sprite_ext_override","path":"scripts/draw_sprite_ext_override/draw_sprite_ext_override.yy",},}, {"id":{"name":"node_array_add","path":"scripts/node_array_add/node_array_add.yy",},}, {"id":{"name":"s_node_array_set","path":"sprites/s_node_array_set/s_node_array_set.yy",},}, {"id":{"name":"node_noise_cell","path":"scripts/node_noise_cell/node_noise_cell.yy",},}, {"id":{"name":"__background_get_internal","path":"scripts/__background_get_internal/__background_get_internal.yy",},}, + {"id":{"name":"BBMOD_Light","path":"scripts/BBMOD_Light/BBMOD_Light.yy",},}, + {"id":{"name":"BBMOD_ShPostProcess","path":"shaders/BBMOD_ShPostProcess/BBMOD_ShPostProcess.yy",},}, + {"id":{"name":"BBMOD_Color","path":"scripts/BBMOD_Color/BBMOD_Color.yy",},}, {"id":{"name":"sh_combine_hsv","path":"shaders/sh_combine_hsv/sh_combine_hsv.yy",},}, {"id":{"name":"s_node_array_insert","path":"sprites/s_node_array_insert/s_node_array_insert.yy",},}, {"id":{"name":"addon_lua","path":"scripts/addon_lua/addon_lua.yy",},}, {"id":{"name":"nodeValue_drawer","path":"scripts/nodeValue_drawer/nodeValue_drawer.yy",},}, + {"id":{"name":"BBMOD_IEventListener","path":"scripts/BBMOD_IEventListener/BBMOD_IEventListener.yy",},}, {"id":{"name":"node_noise","path":"scripts/node_noise/node_noise.yy",},}, {"id":{"name":"mtl_reader","path":"scripts/mtl_reader/mtl_reader.yy",},}, {"id":{"name":"s_node_3d_array","path":"sprites/s_node_3d_array/s_node_3d_array.yy",},}, {"id":{"name":"sh_fd_velocity_combine","path":"shaders/sh_fd_velocity_combine/sh_fd_velocity_combine.yy",},}, + {"id":{"name":"BBMOD_AABBEmissionModule","path":"scripts/BBMOD_AABBEmissionModule/BBMOD_AABBEmissionModule.yy",},}, {"id":{"name":"sh_scale2x","path":"shaders/sh_scale2x/sh_scale2x.yy",},}, {"id":{"name":"ds_priority","path":"scripts/ds_priority/ds_priority.yy",},}, {"id":{"name":"node_vector","path":"scripts/node_vector/node_vector.yy",},}, @@ -1294,19 +1501,23 @@ {"id":{"name":"notification_system","path":"scripts/notification_system/notification_system.yy",},}, {"id":{"name":"sh_shadow_cast","path":"shaders/sh_shadow_cast/sh_shadow_cast.yy",},}, {"id":{"name":"node_color_from_hsv","path":"scripts/node_color_from_hsv/node_color_from_hsv.yy",},}, + {"id":{"name":"BBMOD_SetVec4Module","path":"scripts/BBMOD_SetVec4Module/BBMOD_SetVec4Module.yy",},}, {"id":{"name":"s_node_image_copy","path":"sprites/s_node_image_copy/s_node_image_copy.yy",},}, {"id":{"name":"node_boolean","path":"scripts/node_boolean/node_boolean.yy",},}, {"id":{"name":"node_grid_tri","path":"scripts/node_grid_tri/node_grid_tri.yy",},}, {"id":{"name":"node_average","path":"scripts/node_average/node_average.yy",},}, + {"id":{"name":"BBMOD_Model","path":"scripts/BBMOD_Model/BBMOD_Model.yy",},}, {"id":{"name":"node_mesh_transform","path":"scripts/node_mesh_transform/node_mesh_transform.yy",},}, {"id":{"name":"node_fluid_turbulence","path":"scripts/node_fluid_turbulence/node_fluid_turbulence.yy",},}, {"id":{"name":"sh_sdf","path":"shaders/sh_sdf/sh_sdf.yy",},}, {"id":{"name":"slider","path":"scripts/slider/slider.yy",},}, {"id":{"name":"s_node_convolution","path":"sprites/s_node_convolution/s_node_convolution.yy",},}, + {"id":{"name":"BBMOD_DefaultRenderer","path":"scripts/BBMOD_DefaultRenderer/BBMOD_DefaultRenderer.yy",},}, {"id":{"name":"fd_rectangle_get_velocity_time_step","path":"scripts/fd_rectangle_get_velocity_time_step/fd_rectangle_get_velocity_time_step.yy",},}, {"id":{"name":"node_string_regex_match","path":"scripts/node_string_regex_match/node_string_regex_match.yy",},}, {"id":{"name":"node_9slice","path":"scripts/node_9slice/node_9slice.yy",},}, {"id":{"name":"fd_rectangle_add_velocity_surface","path":"scripts/fd_rectangle_add_velocity_surface/fd_rectangle_add_velocity_surface.yy",},}, + {"id":{"name":"BBMOD_Vec2","path":"scripts/BBMOD_Vec2/BBMOD_Vec2.yy",},}, {"id":{"name":"sh_grid_hex","path":"shaders/sh_grid_hex/sh_grid_hex.yy",},}, {"id":{"name":"s_node_lua_surface","path":"sprites/s_node_lua_surface/s_node_lua_surface.yy",},}, {"id":{"name":"node_data","path":"scripts/node_data/node_data.yy",},}, @@ -1314,15 +1525,19 @@ {"id":{"name":"fd_GUIDE","path":"scripts/fd_GUIDE/fd_GUIDE.yy",},}, {"id":{"name":"node_fluid_update","path":"scripts/node_fluid_update/node_fluid_update.yy",},}, {"id":{"name":"s_node_checker","path":"sprites/s_node_checker/s_node_checker.yy",},}, + {"id":{"name":"BBMOD_Collider","path":"scripts/BBMOD_Collider/BBMOD_Collider.yy",},}, {"id":{"name":"s_node_grid_noise","path":"sprites/s_node_grid_noise/s_node_grid_noise.yy",},}, {"id":{"name":"o_dialog_preview_grid","path":"objects/o_dialog_preview_grid/o_dialog_preview_grid.yy",},}, {"id":{"name":"fd_rectangle_set_velocity_size","path":"scripts/fd_rectangle_set_velocity_size/fd_rectangle_set_velocity_size.yy",},}, + {"id":{"name":"__bbmod_d3d11","path":"scripts/__bbmod_d3d11/__bbmod_d3d11.yy",},}, {"id":{"name":"node_image_gif","path":"scripts/node_image_gif/node_image_gif.yy",},}, {"id":{"name":"node_iterator_each_output","path":"scripts/node_iterator_each_output/node_iterator_each_output.yy",},}, {"id":{"name":"o_dialog_anim_time_scaler","path":"objects/o_dialog_anim_time_scaler/o_dialog_anim_time_scaler.yy",},}, {"id":{"name":"boneObject","path":"scripts/boneObject/boneObject.yy",},}, {"id":{"name":"s_node_strandSim_render_texture","path":"sprites/s_node_strandSim_render_texture/s_node_strandSim_render_texture.yy",},}, + {"id":{"name":"BBMOD_LightmapShader","path":"scripts/BBMOD_LightmapShader/BBMOD_LightmapShader.yy",},}, {"id":{"name":"sh_fd_advect_material_a_8_glsl","path":"shaders/sh_fd_advect_material_a_8_glsl/sh_fd_advect_material_a_8_glsl.yy",},}, + {"id":{"name":"BBMOD_BaseShader","path":"scripts/BBMOD_BaseShader/BBMOD_BaseShader.yy",},}, {"id":{"name":"node_color_remove","path":"scripts/node_color_remove/node_color_remove.yy",},}, {"id":{"name":"FileDropper","path":"extensions/FileDropper/FileDropper.yy",},}, {"id":{"name":"spr_gameframe_pixel","path":"sprites/spr_gameframe_pixel/spr_gameframe_pixel.yy",},}, @@ -1336,6 +1551,7 @@ {"id":{"name":"assets_data","path":"scripts/assets_data/assets_data.yy",},}, {"id":{"name":"s_node_base_conversion","path":"sprites/s_node_base_conversion/s_node_base_conversion.yy",},}, {"id":{"name":"s_node_loop_array","path":"sprites/s_node_loop_array/s_node_loop_array.yy",},}, + {"id":{"name":"BBMOD_ShSSAOBlur","path":"shaders/BBMOD_ShSSAOBlur/BBMOD_ShSSAOBlur.yy",},}, {"id":{"name":"s_node_iterator_amount","path":"sprites/s_node_iterator_amount/s_node_iterator_amount.yy",},}, {"id":{"name":"sh_color_adjust","path":"shaders/sh_color_adjust/sh_color_adjust.yy",},}, {"id":{"name":"sh_fd_visualize_thick_smoke_glsl","path":"shaders/sh_fd_visualize_thick_smoke_glsl/sh_fd_visualize_thick_smoke_glsl.yy",},}, @@ -1346,6 +1562,7 @@ {"id":{"name":"fd_rectangle_set_material_size","path":"scripts/fd_rectangle_set_material_size/fd_rectangle_set_material_size.yy",},}, {"id":{"name":"fd_rectangle_shift_content","path":"scripts/fd_rectangle_shift_content/fd_rectangle_shift_content.yy",},}, {"id":{"name":"sh_texture_remap","path":"shaders/sh_texture_remap/sh_texture_remap.yy",},}, + {"id":{"name":"BBMOD_ShDefault","path":"shaders/BBMOD_ShDefault/BBMOD_ShDefault.yy",},}, {"id":{"name":"obj_fd_rectangle","path":"objects/obj_fd_rectangle/obj_fd_rectangle.yy",},}, {"id":{"name":"node_noise_simplex","path":"scripts/node_noise_simplex/node_noise_simplex.yy",},}, {"id":{"name":"sh_fd_visualize_velocity_divergence_glsl","path":"shaders/sh_fd_visualize_velocity_divergence_glsl/sh_fd_visualize_velocity_divergence_glsl.yy",},}, @@ -1354,6 +1571,7 @@ {"id":{"name":"o_dialog_gradient","path":"objects/o_dialog_gradient/o_dialog_gradient.yy",},}, {"id":{"name":"sh_channel_R_grey","path":"shaders/sh_channel_R_grey/sh_channel_R_grey.yy",},}, {"id":{"name":"sh_blend_subtract","path":"shaders/sh_blend_subtract/sh_blend_subtract.yy",},}, + {"id":{"name":"BBMOD_Vec4","path":"scripts/BBMOD_Vec4/BBMOD_Vec4.yy",},}, {"id":{"name":"o_dialog_file_name","path":"objects/o_dialog_file_name/o_dialog_file_name.yy",},}, {"id":{"name":"panel_animation","path":"scripts/panel_animation/panel_animation.yy",},}, {"id":{"name":"node_surface_replace","path":"scripts/node_surface_replace/node_surface_replace.yy",},}, @@ -1368,14 +1586,18 @@ {"id":{"name":"sh_clean_shape","path":"shaders/sh_clean_shape/sh_clean_shape.yy",},}, {"id":{"name":"fd_rectangle_update","path":"scripts/fd_rectangle_update/fd_rectangle_update.yy",},}, {"id":{"name":"sh_posterize","path":"shaders/sh_posterize/sh_posterize.yy",},}, + {"id":{"name":"BBMOD_DirectionalLight","path":"scripts/BBMOD_DirectionalLight/BBMOD_DirectionalLight.yy",},}, {"id":{"name":"s_node_mirror","path":"sprites/s_node_mirror/s_node_mirror.yy",},}, {"id":{"name":"draw_enable_alphablend","path":"scripts/draw_enable_alphablend/draw_enable_alphablend.yy",},}, {"id":{"name":"s_node_blur_simple","path":"sprites/s_node_blur_simple/s_node_blur_simple.yy",},}, {"id":{"name":"node_VFX_spawner","path":"scripts/node_VFX_spawner/node_VFX_spawner.yy",},}, {"id":{"name":"s_node_equation","path":"sprites/s_node_equation/s_node_equation.yy",},}, + {"id":{"name":"__bbmod_default_material","path":"scripts/__bbmod_default_material/__bbmod_default_material.yy",},}, {"id":{"name":"_draw_defines","path":"scripts/_draw_defines/_draw_defines.yy",},}, + {"id":{"name":"__bbmod_save","path":"scripts/__bbmod_save/__bbmod_save.yy",},}, {"id":{"name":"sh_color_replace","path":"shaders/sh_color_replace/sh_color_replace.yy",},}, {"id":{"name":"__surface","path":"scripts/__surface/__surface.yy",},}, + {"id":{"name":"BBMOD_AddVec4OverTimeModule","path":"scripts/BBMOD_AddVec4OverTimeModule/BBMOD_AddVec4OverTimeModule.yy",},}, {"id":{"name":"rotator","path":"scripts/rotator/rotator.yy",},}, {"id":{"name":"s_node_edge_detect","path":"sprites/s_node_edge_detect/s_node_edge_detect.yy",},}, {"id":{"name":"node_fluid_add_collider","path":"scripts/node_fluid_add_collider/node_fluid_add_collider.yy",},}, @@ -1388,11 +1610,15 @@ {"id":{"name":"node_strand_render_texture","path":"scripts/node_strand_render_texture/node_strand_render_texture.yy",},}, {"id":{"name":"luaRenderer","path":"scripts/luaRenderer/luaRenderer.yy",},}, {"id":{"name":"node_blur_zoom","path":"scripts/node_blur_zoom/node_blur_zoom.yy",},}, + {"id":{"name":"BBMOD_Ray","path":"scripts/BBMOD_Ray/BBMOD_Ray.yy",},}, {"id":{"name":"node_bloom","path":"scripts/node_bloom/node_bloom.yy",},}, {"id":{"name":"sh_bevel","path":"shaders/sh_bevel/sh_bevel.yy",},}, {"id":{"name":"sh_blend_overlay","path":"shaders/sh_blend_overlay/sh_blend_overlay.yy",},}, {"id":{"name":"node_sprite_stack","path":"scripts/node_sprite_stack/node_sprite_stack.yy",},}, {"id":{"name":"s_node_json_file_read","path":"sprites/s_node_json_file_read/s_node_json_file_read.yy",},}, + {"id":{"name":"BBMOD_Vertex","path":"scripts/BBMOD_Vertex/BBMOD_Vertex.yy",},}, + {"id":{"name":"BBMOD_OutOfRangeException","path":"scripts/BBMOD_OutOfRangeException/BBMOD_OutOfRangeException.yy",},}, + {"id":{"name":"BBMOD_LightmapMaterial","path":"scripts/BBMOD_LightmapMaterial/BBMOD_LightmapMaterial.yy",},}, {"id":{"name":"__strandSim","path":"scripts/__strandSim/__strandSim.yy",},}, {"id":{"name":"sh_shape","path":"shaders/sh_shape/sh_shape.yy",},}, {"id":{"name":"draw_line_width2","path":"scripts/draw_line_width2/draw_line_width2.yy",},}, @@ -1410,6 +1636,7 @@ {"id":{"name":"o_dialog_preset","path":"objects/o_dialog_preset/o_dialog_preset.yy",},}, {"id":{"name":"s_node_mesh_transform","path":"sprites/s_node_mesh_transform/s_node_mesh_transform.yy",},}, {"id":{"name":"o_dialog_tunnels","path":"objects/o_dialog_tunnels/o_dialog_tunnels.yy",},}, + {"id":{"name":"BBMOD_ShParticleLit","path":"shaders/BBMOD_ShParticleLit/BBMOD_ShParticleLit.yy",},}, {"id":{"name":"s_node_erode","path":"sprites/s_node_erode/s_node_erode.yy",},}, {"id":{"name":"node_particle","path":"scripts/node_particle/node_particle.yy",},}, {"id":{"name":"histogram_drawer","path":"scripts/histogram_drawer/histogram_drawer.yy",},}, @@ -1420,12 +1647,14 @@ {"id":{"name":"fd_rectangle_reset_target","path":"scripts/fd_rectangle_reset_target/fd_rectangle_reset_target.yy",},}, {"id":{"name":"obj_reader","path":"scripts/obj_reader/obj_reader.yy",},}, {"id":{"name":"sh_invert","path":"shaders/sh_invert/sh_invert.yy",},}, + {"id":{"name":"__bbmod_terrain","path":"scripts/__bbmod_terrain/__bbmod_terrain.yy",},}, {"id":{"name":"preview_overlay_puppet","path":"scripts/preview_overlay_puppet/preview_overlay_puppet.yy",},}, {"id":{"name":"__mesh","path":"scripts/__mesh/__mesh.yy",},}, {"id":{"name":"s_icon_64","path":"sprites/s_icon_64/s_icon_64.yy",},}, {"id":{"name":"save_function","path":"scripts/save_function/save_function.yy",},}, {"id":{"name":"s_node_gradient_replace","path":"sprites/s_node_gradient_replace/s_node_gradient_replace.yy",},}, {"id":{"name":"node_perlin_smear","path":"scripts/node_perlin_smear/node_perlin_smear.yy",},}, + {"id":{"name":"BBMOD_MixQuaternionOverTimeModule","path":"scripts/BBMOD_MixQuaternionOverTimeModule/BBMOD_MixQuaternionOverTimeModule.yy",},}, {"id":{"name":"node_alpha_cutoff","path":"scripts/node_alpha_cutoff/node_alpha_cutoff.yy",},}, {"id":{"name":"pack_best_fit","path":"scripts/pack_best_fit/pack_best_fit.yy",},}, {"id":{"name":"sh_channel_R","path":"shaders/sh_channel_R/sh_channel_R.yy",},}, @@ -1434,35 +1663,46 @@ {"id":{"name":"sliderRange","path":"scripts/sliderRange/sliderRange.yy",},}, {"id":{"name":"point_rect_overlap","path":"scripts/point_rect_overlap/point_rect_overlap.yy",},}, {"id":{"name":"s_node_bevel","path":"sprites/s_node_bevel/s_node_bevel.yy",},}, + {"id":{"name":"BBMOD_MixVec2Module","path":"scripts/BBMOD_MixVec2Module/BBMOD_MixVec2Module.yy",},}, {"id":{"name":"color_selector","path":"scripts/color_selector/color_selector.yy",},}, + {"id":{"name":"BBMOD_SprGizmo","path":"sprites/BBMOD_SprGizmo/BBMOD_SprGizmo.yy",},}, {"id":{"name":"node_trigger_bool","path":"scripts/node_trigger_bool/node_trigger_bool.yy",},}, {"id":{"name":"s_node_strandSim_gravity","path":"sprites/s_node_strandSim_gravity/s_node_strandSim_gravity.yy",},}, {"id":{"name":"node_VFX_effect_turbulence","path":"scripts/node_VFX_effect_turbulence/node_VFX_effect_turbulence.yy",},}, {"id":{"name":"sh_surface_replace_replace","path":"shaders/sh_surface_replace_replace/sh_surface_replace_replace.yy",},}, {"id":{"name":"node_ase_file_read","path":"scripts/node_ase_file_read/node_ase_file_read.yy",},}, + {"id":{"name":"BBMOD_MixVec4OverTimeModule","path":"scripts/BBMOD_MixVec4OverTimeModule/BBMOD_MixVec4OverTimeModule.yy",},}, {"id":{"name":"s_workshop_frame","path":"sprites/s_workshop_frame/s_workshop_frame.yy",},}, + {"id":{"name":"BBMOD_ShFXAA","path":"shaders/BBMOD_ShFXAA/BBMOD_ShFXAA.yy",},}, {"id":{"name":"sh_seperate_shape_counter","path":"shaders/sh_seperate_shape_counter/sh_seperate_shape_counter.yy",},}, {"id":{"name":"s_node_stack","path":"sprites/s_node_stack/s_node_stack.yy",},}, {"id":{"name":"s_fade_up","path":"sprites/s_fade_up/s_fade_up.yy",},}, {"id":{"name":"panel_globalvar","path":"scripts/panel_globalvar/panel_globalvar.yy",},}, {"id":{"name":"node_guide","path":"scripts/node_guide/node_guide.yy",},}, {"id":{"name":"fd_rectangle_get_velocity_surface","path":"scripts/fd_rectangle_get_velocity_surface/fd_rectangle_get_velocity_surface.yy",},}, + {"id":{"name":"BBMOD_DragModule","path":"scripts/BBMOD_DragModule/BBMOD_DragModule.yy",},}, {"id":{"name":"s_node_text_char_get","path":"sprites/s_node_text_char_get/s_node_text_char_get.yy",},}, {"id":{"name":"fd_rectangle_get_initial_value_pressure","path":"scripts/fd_rectangle_get_initial_value_pressure/fd_rectangle_get_initial_value_pressure.yy",},}, {"id":{"name":"node_timeline_preview","path":"scripts/node_timeline_preview/node_timeline_preview.yy",},}, {"id":{"name":"__polygon","path":"scripts/__polygon/__polygon.yy",},}, + {"id":{"name":"BBMOD_DefaultMaterial","path":"scripts/BBMOD_DefaultMaterial/BBMOD_DefaultMaterial.yy",},}, {"id":{"name":"sh_morph_surface","path":"shaders/sh_morph_surface/sh_morph_surface.yy",},}, {"id":{"name":"o_dialog_fontscrollbox","path":"objects/o_dialog_fontscrollbox/o_dialog_fontscrollbox.yy",},}, {"id":{"name":"s_node_vfx_output","path":"sprites/s_node_vfx_output/s_node_vfx_output.yy",},}, {"id":{"name":"sh_greyscale","path":"shaders/sh_greyscale/sh_greyscale.yy",},}, {"id":{"name":"gameframe_native","path":"extensions/gameframe_native/gameframe_native.yy",},}, {"id":{"name":"node_global","path":"scripts/node_global/node_global.yy",},}, + {"id":{"name":"BBMOD_RandomRotationModule","path":"scripts/BBMOD_RandomRotationModule/BBMOD_RandomRotationModule.yy",},}, + {"id":{"name":"__bbmod_sky","path":"scripts/__bbmod_sky/__bbmod_sky.yy",},}, + {"id":{"name":"BBMOD_MixQuaternionFromHealthModule","path":"scripts/BBMOD_MixQuaternionFromHealthModule/BBMOD_MixQuaternionFromHealthModule.yy",},}, {"id":{"name":"spr_gameframe_buttons","path":"sprites/spr_gameframe_buttons/spr_gameframe_buttons.yy",},}, {"id":{"name":"s_node_draw_stack","path":"sprites/s_node_draw_stack/s_node_draw_stack.yy",},}, {"id":{"name":"delaunay","path":"scripts/delaunay/delaunay.yy",},}, {"id":{"name":"s_node_noise_fbm","path":"sprites/s_node_noise_fbm/s_node_noise_fbm.yy",},}, {"id":{"name":"sh_color_select_content","path":"shaders/sh_color_select_content/sh_color_select_content.yy",},}, {"id":{"name":"sh_outline","path":"shaders/sh_outline/sh_outline.yy",},}, + {"id":{"name":"BBMOD_Sprite","path":"scripts/BBMOD_Sprite/BBMOD_Sprite.yy",},}, + {"id":{"name":"BBMOD_MixQuaternionModule","path":"scripts/BBMOD_MixQuaternionModule/BBMOD_MixQuaternionModule.yy",},}, {"id":{"name":"s_node_strandSim_break","path":"sprites/s_node_strandSim_break/s_node_strandSim_break.yy",},}, {"id":{"name":"s_node_scatter_point","path":"sprites/s_node_scatter_point/s_node_scatter_point.yy",},}, {"id":{"name":"node_checkerboard","path":"scripts/node_checkerboard/node_checkerboard.yy",},}, @@ -1471,10 +1711,13 @@ {"id":{"name":"panel_workspace","path":"scripts/panel_workspace/panel_workspace.yy",},}, {"id":{"name":"fd_rectangle_assure_surfaces_exist","path":"scripts/fd_rectangle_assure_surfaces_exist/fd_rectangle_assure_surfaces_exist.yy",},}, {"id":{"name":"fd_rectangle_material_surface_was_created","path":"scripts/fd_rectangle_material_surface_was_created/fd_rectangle_material_surface_was_created.yy",},}, + {"id":{"name":"bbmod_set_instance_id","path":"scripts/bbmod_set_instance_id/bbmod_set_instance_id.yy",},}, {"id":{"name":"s_node_fluidSim_apply_velocity","path":"sprites/s_node_fluidSim_apply_velocity/s_node_fluidSim_apply_velocity.yy",},}, + {"id":{"name":"BBMOD_SetQuaternionModule","path":"scripts/BBMOD_SetQuaternionModule/BBMOD_SetQuaternionModule.yy",},}, {"id":{"name":"fd_rectangle_destroy","path":"scripts/fd_rectangle_destroy/fd_rectangle_destroy.yy",},}, {"id":{"name":"s_node_grey_alpha","path":"sprites/s_node_grey_alpha/s_node_grey_alpha.yy",},}, {"id":{"name":"o_dialog_color_selector","path":"objects/o_dialog_color_selector/o_dialog_color_selector.yy",},}, + {"id":{"name":"BBMOD_ParticleMaterial","path":"scripts/BBMOD_ParticleMaterial/BBMOD_ParticleMaterial.yy",},}, {"id":{"name":"sh_bloom_pass","path":"shaders/sh_bloom_pass/sh_bloom_pass.yy",},}, {"id":{"name":"fd_rectangle_draw_view","path":"scripts/fd_rectangle_draw_view/fd_rectangle_draw_view.yy",},}, {"id":{"name":"fd_rectangle_set_material_type","path":"scripts/fd_rectangle_set_material_type/fd_rectangle_set_material_type.yy",},}, @@ -1490,6 +1733,7 @@ {"id":{"name":"s_node_flood_fill","path":"sprites/s_node_flood_fill/s_node_flood_fill.yy",},}, {"id":{"name":"node_rigid_force_apply","path":"scripts/node_rigid_force_apply/node_rigid_force_apply.yy",},}, {"id":{"name":"s_node_rigid_variable","path":"sprites/s_node_rigid_variable/s_node_rigid_variable.yy",},}, + {"id":{"name":"BBMOD_MixVec4FromSpeedModule","path":"scripts/BBMOD_MixVec4FromSpeedModule/BBMOD_MixVec4FromSpeedModule.yy",},}, {"id":{"name":"safe_operation","path":"scripts/safe_operation/safe_operation.yy",},}, {"id":{"name":"s_node_rigid_override","path":"sprites/s_node_rigid_override/s_node_rigid_override.yy",},}, {"id":{"name":"s_node_array_get","path":"sprites/s_node_array_get/s_node_array_get.yy",},}, @@ -1502,8 +1746,10 @@ {"id":{"name":"sh_camera","path":"shaders/sh_camera/sh_camera.yy",},}, {"id":{"name":"s_node_vfx_spawn","path":"sprites/s_node_vfx_spawn/s_node_vfx_spawn.yy",},}, {"id":{"name":"texture_set_repeat","path":"scripts/texture_set_repeat/texture_set_repeat.yy",},}, + {"id":{"name":"BBMOD_DefaultSpriteShader","path":"scripts/BBMOD_DefaultSpriteShader/BBMOD_DefaultSpriteShader.yy",},}, {"id":{"name":"fd_rectangle_update_velocity","path":"scripts/fd_rectangle_update_velocity/fd_rectangle_update_velocity.yy",},}, {"id":{"name":"sh_draw_mapping","path":"shaders/sh_draw_mapping/sh_draw_mapping.yy",},}, + {"id":{"name":"BBMOD_Cubemap","path":"scripts/BBMOD_Cubemap/BBMOD_Cubemap.yy",},}, {"id":{"name":"__bbox","path":"scripts/__bbox/__bbox.yy",},}, {"id":{"name":"node_shadow","path":"scripts/node_shadow/node_shadow.yy",},}, {"id":{"name":"s_node_color_data","path":"sprites/s_node_color_data/s_node_color_data.yy",},}, @@ -1511,13 +1757,16 @@ {"id":{"name":"sh_fd_calculate_velocity_divergence_glsl","path":"shaders/sh_fd_calculate_velocity_divergence_glsl/sh_fd_calculate_velocity_divergence_glsl.yy",},}, {"id":{"name":"node_simple_shape","path":"scripts/node_simple_shape/node_simple_shape.yy",},}, {"id":{"name":"fd_rectangle_get_acceleration_a","path":"scripts/fd_rectangle_get_acceleration_a/fd_rectangle_get_acceleration_a.yy",},}, + {"id":{"name":"BBMOD_ShParticleDepth","path":"shaders/BBMOD_ShParticleDepth/BBMOD_ShParticleDepth.yy",},}, {"id":{"name":"node_random","path":"scripts/node_random/node_random.yy",},}, {"id":{"name":"sh_channel_A","path":"shaders/sh_channel_A/sh_channel_A.yy",},}, {"id":{"name":"node_atlas","path":"scripts/node_atlas/node_atlas.yy",},}, {"id":{"name":"sh_blend_sat","path":"shaders/sh_blend_sat/sh_blend_sat.yy",},}, {"id":{"name":"s_node_feedback","path":"sprites/s_node_feedback/s_node_feedback.yy",},}, + {"id":{"name":"BBMOD_RenderQueue","path":"scripts/BBMOD_RenderQueue/BBMOD_RenderQueue.yy",},}, {"id":{"name":"getGradientData","path":"scripts/getGradientData/getGradientData.yy",},}, {"id":{"name":"s_node_character","path":"sprites/s_node_character/s_node_character.yy",},}, + {"id":{"name":"BBMOD_Terrain","path":"scripts/BBMOD_Terrain/BBMOD_Terrain.yy",},}, {"id":{"name":"node_glow","path":"scripts/node_glow/node_glow.yy",},}, {"id":{"name":"sh_blend_min","path":"shaders/sh_blend_min/sh_blend_min.yy",},}, {"id":{"name":"node_polar","path":"scripts/node_polar/node_polar.yy",},}, @@ -1528,7 +1777,9 @@ {"id":{"name":"s_node_solid","path":"sprites/s_node_solid/s_node_solid.yy",},}, {"id":{"name":"s_node_fluidSim_vortex","path":"sprites/s_node_fluidSim_vortex/s_node_fluidSim_vortex.yy",},}, {"id":{"name":"string_hexadecimal","path":"scripts/string_hexadecimal/string_hexadecimal.yy",},}, + {"id":{"name":"BBMOD_SprDefaultNormalW","path":"sprites/BBMOD_SprDefaultNormalW/BBMOD_SprDefaultNormalW.yy",},}, {"id":{"name":"s_node_camera","path":"sprites/s_node_camera/s_node_camera.yy",},}, + {"id":{"name":"BBMOD_AddRealOnCollisionModule","path":"scripts/BBMOD_AddRealOnCollisionModule/BBMOD_AddRealOnCollisionModule.yy",},}, {"id":{"name":"sh_find_boundary","path":"shaders/sh_find_boundary/sh_find_boundary.yy",},}, {"id":{"name":"_node_strand_affector","path":"scripts/_node_strand_affector/_node_strand_affector.yy",},}, {"id":{"name":"Steamworks","path":"extensions/Steamworks/Steamworks.yy",},}, @@ -1540,9 +1791,11 @@ {"id":{"name":"s_node_repeat","path":"sprites/s_node_repeat/s_node_repeat.yy",},}, {"id":{"name":"s_node_array_length","path":"sprites/s_node_array_length/s_node_array_length.yy",},}, {"id":{"name":"string_splice","path":"scripts/string_splice/string_splice.yy",},}, + {"id":{"name":"__bbmod_string","path":"scripts/__bbmod_string/__bbmod_string.yy",},}, {"id":{"name":"s_node_vfx_input","path":"sprites/s_node_vfx_input/s_node_vfx_input.yy",},}, {"id":{"name":"node_strand_force_apply","path":"scripts/node_strand_force_apply/node_strand_force_apply.yy",},}, {"id":{"name":"s_node_rigidSim","path":"sprites/s_node_rigidSim/s_node_rigidSim.yy",},}, + {"id":{"name":"BBMOD_ShGizmo","path":"shaders/BBMOD_ShGizmo/BBMOD_ShGizmo.yy",},}, {"id":{"name":"sh_seperate_shape_ite","path":"shaders/sh_seperate_shape_ite/sh_seperate_shape_ite.yy",},}, {"id":{"name":"o_main","path":"objects/o_main/o_main.yy",},}, {"id":{"name":"area_function","path":"scripts/area_function/area_function.yy",},}, @@ -1552,6 +1805,7 @@ {"id":{"name":"number_function","path":"scripts/number_function/number_function.yy",},}, {"id":{"name":"s_node_array_shift","path":"sprites/s_node_array_shift/s_node_array_shift.yy",},}, {"id":{"name":"s_node_statistic","path":"sprites/s_node_statistic/s_node_statistic.yy",},}, + {"id":{"name":"BBMOD_ParticleSystem","path":"scripts/BBMOD_ParticleSystem/BBMOD_ParticleSystem.yy",},}, {"id":{"name":"s_node_cache_array","path":"sprites/s_node_cache_array/s_node_cache_array.yy",},}, {"id":{"name":"sh_blend_alpha_addition","path":"shaders/sh_blend_alpha_addition/sh_blend_alpha_addition.yy",},}, {"id":{"name":"widget","path":"scripts/widget/widget.yy",},}, @@ -1563,6 +1817,7 @@ {"id":{"name":"sh_erode","path":"shaders/sh_erode/sh_erode.yy",},}, {"id":{"name":"mac_window_init","path":"scripts/mac_window_init/mac_window_init.yy",},}, {"id":{"name":"fd_rectangle_get_velocity_dissipation_value","path":"scripts/fd_rectangle_get_velocity_dissipation_value/fd_rectangle_get_velocity_dissipation_value.yy",},}, + {"id":{"name":"BBMOD_MeshBuilder","path":"scripts/BBMOD_MeshBuilder/BBMOD_MeshBuilder.yy",},}, {"id":{"name":"node_pixel_cloud","path":"scripts/node_pixel_cloud/node_pixel_cloud.yy",},}, {"id":{"name":"s_node_crop_content","path":"sprites/s_node_crop_content/s_node_crop_content.yy",},}, {"id":{"name":"draw_line_zigzag","path":"scripts/draw_line_zigzag/draw_line_zigzag.yy",},}, @@ -1573,11 +1828,13 @@ {"id":{"name":"s_node_3d_plane","path":"sprites/s_node_3d_plane/s_node_3d_plane.yy",},}, {"id":{"name":"Regex","path":"extensions/Regex/Regex.yy",},}, {"id":{"name":"s_node_path_shift","path":"sprites/s_node_path_shift/s_node_path_shift.yy",},}, + {"id":{"name":"BBMOD_MixRealFromHealthModule","path":"scripts/BBMOD_MixRealFromHealthModule/BBMOD_MixRealFromHealthModule.yy",},}, {"id":{"name":"node_rigid_group","path":"scripts/node_rigid_group/node_rigid_group.yy",},}, {"id":{"name":"s_node_grid_tri","path":"sprites/s_node_grid_tri/s_node_grid_tri.yy",},}, {"id":{"name":"s_node_local_analyze","path":"sprites/s_node_local_analyze/s_node_local_analyze.yy",},}, {"id":{"name":"_f_p0b","path":"fonts/_f_p0b/_f_p0b.yy",},}, {"id":{"name":"o_dialog_keyframe_curve","path":"objects/o_dialog_keyframe_curve/o_dialog_keyframe_curve.yy",},}, + {"id":{"name":"BBMOD_AnimationStateMachine","path":"scripts/BBMOD_AnimationStateMachine/BBMOD_AnimationStateMachine.yy",},}, {"id":{"name":"sh_fd_visualize_pressure_glsl","path":"shaders/sh_fd_visualize_pressure_glsl/sh_fd_visualize_pressure_glsl.yy",},}, {"id":{"name":"s_node_rigidSim_force","path":"sprites/s_node_rigidSim_force/s_node_rigidSim_force.yy",},}, {"id":{"name":"fd_rectangle_set_acceleration","path":"scripts/fd_rectangle_set_acceleration/fd_rectangle_set_acceleration.yy",},}, @@ -1592,18 +1849,23 @@ {"id":{"name":"s_node_padding","path":"sprites/s_node_padding/s_node_padding.yy",},}, {"id":{"name":"sh_blend_add_alpha_adj","path":"shaders/sh_blend_add_alpha_adj/sh_blend_add_alpha_adj.yy",},}, {"id":{"name":"preview_overlay_area","path":"scripts/preview_overlay_area/preview_overlay_area.yy",},}, + {"id":{"name":"BBMOD_AttractorModule","path":"scripts/BBMOD_AttractorModule/BBMOD_AttractorModule.yy",},}, {"id":{"name":"node_text","path":"scripts/node_text/node_text.yy",},}, + {"id":{"name":"BBMOD_MixRealOverTimeModule","path":"scripts/BBMOD_MixRealOverTimeModule/BBMOD_MixRealOverTimeModule.yy",},}, {"id":{"name":"panel_collection","path":"scripts/panel_collection/panel_collection.yy",},}, {"id":{"name":"node_string_trim","path":"scripts/node_string_trim/node_string_trim.yy",},}, + {"id":{"name":"BBMOD_Animation","path":"scripts/BBMOD_Animation/BBMOD_Animation.yy",},}, {"id":{"name":"lerp_float","path":"scripts/lerp_float/lerp_float.yy",},}, {"id":{"name":"vectorRangeBox","path":"scripts/vectorRangeBox/vectorRangeBox.yy",},}, {"id":{"name":"sh_draw_vertex_aa","path":"shaders/sh_draw_vertex_aa/sh_draw_vertex_aa.yy",},}, {"id":{"name":"fd_rectangle_get_velocity_width","path":"scripts/fd_rectangle_get_velocity_width/fd_rectangle_get_velocity_width.yy",},}, {"id":{"name":"node_convolution","path":"scripts/node_convolution/node_convolution.yy",},}, {"id":{"name":"sh_outline_only","path":"shaders/sh_outline_only/sh_outline_only.yy",},}, + {"id":{"name":"BBMOD_GravityModule","path":"scripts/BBMOD_GravityModule/BBMOD_GravityModule.yy",},}, {"id":{"name":"lcd_function","path":"scripts/lcd_function/lcd_function.yy",},}, {"id":{"name":"s_node_destray","path":"sprites/s_node_destray/s_node_destray.yy",},}, {"id":{"name":"node_color_sampler","path":"scripts/node_color_sampler/node_color_sampler.yy",},}, + {"id":{"name":"node_iterator_sort_output","path":"scripts/node_iterator_sort_output/node_iterator_sort_output.yy",},}, {"id":{"name":"s_node_fluidSim_update","path":"sprites/s_node_fluidSim_update/s_node_fluidSim_update.yy",},}, {"id":{"name":"ase_reader","path":"scripts/ase_reader/ase_reader.yy",},}, {"id":{"name":"fd_rectangle_inherit_velocity","path":"scripts/fd_rectangle_inherit_velocity/fd_rectangle_inherit_velocity.yy",},}, @@ -1623,6 +1885,7 @@ {"id":{"name":"fd_rectangle_set_velocity_maccormack_weight","path":"scripts/fd_rectangle_set_velocity_maccormack_weight/fd_rectangle_set_velocity_maccormack_weight.yy",},}, {"id":{"name":"s_node_array_sort","path":"sprites/s_node_array_sort/s_node_array_sort.yy",},}, {"id":{"name":"node_trail","path":"scripts/node_trail/node_trail.yy",},}, + {"id":{"name":"BBMOD_AddVec3OnCollisionModule","path":"scripts/BBMOD_AddVec3OnCollisionModule/BBMOD_AddVec3OnCollisionModule.yy",},}, {"id":{"name":"byte_reader","path":"scripts/byte_reader/byte_reader.yy",},}, {"id":{"name":"json_prettify","path":"scripts/json_prettify/json_prettify.yy",},}, {"id":{"name":"s_node_loop_output","path":"sprites/s_node_loop_output/s_node_loop_output.yy",},}, @@ -1633,9 +1896,12 @@ {"id":{"name":"fd_rectangle_get_velocity_height","path":"scripts/fd_rectangle_get_velocity_height/fd_rectangle_get_velocity_height.yy",},}, {"id":{"name":"logger","path":"scripts/logger/logger.yy",},}, {"id":{"name":"addon_key_displayer","path":"objects/addon_key_displayer/addon_key_displayer.yy",},}, + {"id":{"name":"BBMOD_ShDefaultDepth","path":"shaders/BBMOD_ShDefaultDepth/BBMOD_ShDefaultDepth.yy",},}, + {"id":{"name":"BBMOD_ShTerrain","path":"shaders/BBMOD_ShTerrain/BBMOD_ShTerrain.yy",},}, {"id":{"name":"s_node_blur_directional","path":"sprites/s_node_blur_directional/s_node_blur_directional.yy",},}, {"id":{"name":"distance_to_line","path":"scripts/distance_to_line/distance_to_line.yy",},}, {"id":{"name":"globalvar_drawer","path":"scripts/globalvar_drawer/globalvar_drawer.yy",},}, + {"id":{"name":"BBMOD_Camera","path":"scripts/BBMOD_Camera/BBMOD_Camera.yy",},}, {"id":{"name":"s_node_average","path":"sprites/s_node_average/s_node_average.yy",},}, {"id":{"name":"s_node_sprite_sheet","path":"sprites/s_node_sprite_sheet/s_node_sprite_sheet.yy",},}, {"id":{"name":"s_node_text_length","path":"sprites/s_node_text_length/s_node_text_length.yy",},}, @@ -1648,13 +1914,16 @@ {"id":{"name":"node_animate_curve","path":"scripts/node_animate_curve/node_animate_curve.yy",},}, {"id":{"name":"window_functions","path":"scripts/window_functions/window_functions.yy",},}, {"id":{"name":"node_mesh_create_path","path":"scripts/node_mesh_create_path/node_mesh_create_path.yy",},}, + {"id":{"name":"__bbmod_async","path":"scripts/__bbmod_async/__bbmod_async.yy",},}, {"id":{"name":"sh_fd_calculate_pressure_srj_glsl","path":"shaders/sh_fd_calculate_pressure_srj_glsl/sh_fd_calculate_pressure_srj_glsl.yy",},}, {"id":{"name":"curve_bounce_function","path":"scripts/curve_bounce_function/curve_bounce_function.yy",},}, {"id":{"name":"fd_rectangle_replace_material_surface","path":"scripts/fd_rectangle_replace_material_surface/fd_rectangle_replace_material_surface.yy",},}, {"id":{"name":"pack_skyline","path":"scripts/pack_skyline/pack_skyline.yy",},}, + {"id":{"name":"BBMOD_ShDefaultBatched","path":"shaders/BBMOD_ShDefaultBatched/BBMOD_ShDefaultBatched.yy",},}, {"id":{"name":"fd_x","path":"scripts/fd_x/fd_x.yy",},}, {"id":{"name":"s_node_number","path":"sprites/s_node_number/s_node_number.yy",},}, {"id":{"name":"gif_reader","path":"scripts/gif_reader/gif_reader.yy",},}, + {"id":{"name":"BBMOD_ShDefaultAnimated","path":"shaders/BBMOD_ShDefaultAnimated/BBMOD_ShDefaultAnimated.yy",},}, {"id":{"name":"_f_p3","path":"fonts/_f_p3/_f_p3.yy",},}, {"id":{"name":"node_group_input","path":"scripts/node_group_input/node_group_input.yy",},}, {"id":{"name":"fd_rectangle_set_material_dissipation_type","path":"scripts/fd_rectangle_set_material_dissipation_type/fd_rectangle_set_material_dissipation_type.yy",},}, @@ -1664,6 +1933,7 @@ {"id":{"name":"draw_rect_border","path":"scripts/draw_rect_border/draw_rect_border.yy",},}, {"id":{"name":"node_composite","path":"scripts/node_composite/node_composite.yy",},}, {"id":{"name":"s_node_path_map","path":"sprites/s_node_path_map/s_node_path_map.yy",},}, + {"id":{"name":"BBMOD_MixRealFromSpeedModule","path":"scripts/BBMOD_MixRealFromSpeedModule/BBMOD_MixRealFromSpeedModule.yy",},}, {"id":{"name":"s_node_greyscale","path":"sprites/s_node_greyscale/s_node_greyscale.yy",},}, {"id":{"name":"sh_dilate","path":"shaders/sh_dilate/sh_dilate.yy",},}, {"id":{"name":"node_image_sequence","path":"scripts/node_image_sequence/node_image_sequence.yy",},}, @@ -1677,6 +1947,7 @@ {"id":{"name":"node_iterator_input","path":"scripts/node_iterator_input/node_iterator_input.yy",},}, {"id":{"name":"s_node_atlas","path":"sprites/s_node_atlas/s_node_atlas.yy",},}, {"id":{"name":"node_fluid_add","path":"scripts/node_fluid_add/node_fluid_add.yy",},}, + {"id":{"name":"BBMOD_Quaternion","path":"scripts/BBMOD_Quaternion/BBMOD_Quaternion.yy",},}, {"id":{"name":"o_dialog_output_visibility","path":"objects/o_dialog_output_visibility/o_dialog_output_visibility.yy",},}, {"id":{"name":"node_normal","path":"scripts/node_normal/node_normal.yy",},}, {"id":{"name":"s_node_glow","path":"sprites/s_node_glow/s_node_glow.yy",},}, @@ -1684,16 +1955,21 @@ {"id":{"name":"s_node_condition","path":"sprites/s_node_condition/s_node_condition.yy",},}, {"id":{"name":"s_node_vfx_repel","path":"sprites/s_node_vfx_repel/s_node_vfx_repel.yy",},}, {"id":{"name":"fd_rectangle_set_collision_mask_surface","path":"scripts/fd_rectangle_set_collision_mask_surface/fd_rectangle_set_collision_mask_surface.yy",},}, + {"id":{"name":"BBMOD_OBJImporter","path":"scripts/BBMOD_OBJImporter/BBMOD_OBJImporter.yy",},}, {"id":{"name":"node_array_length","path":"scripts/node_array_length/node_array_length.yy",},}, {"id":{"name":"node_erode","path":"scripts/node_erode/node_erode.yy",},}, {"id":{"name":"draw_circle_angle","path":"scripts/draw_circle_angle/draw_circle_angle.yy",},}, {"id":{"name":"node_wrap_mesh","path":"scripts/node_wrap_mesh/node_wrap_mesh.yy",},}, {"id":{"name":"node_curve","path":"scripts/node_curve/node_curve.yy",},}, {"id":{"name":"sh_alpha_hash","path":"shaders/sh_alpha_hash/sh_alpha_hash.yy",},}, + {"id":{"name":"BBMOD_MixVec3Module","path":"scripts/BBMOD_MixVec3Module/BBMOD_MixVec3Module.yy",},}, {"id":{"name":"sh_blur_alpha","path":"shaders/sh_blur_alpha/sh_blur_alpha.yy",},}, {"id":{"name":"node_render_sprite_sheet","path":"scripts/node_render_sprite_sheet/node_render_sprite_sheet.yy",},}, + {"id":{"name":"BBMOD_Importer","path":"scripts/BBMOD_Importer/BBMOD_Importer.yy",},}, {"id":{"name":"textArrayBox","path":"scripts/textArrayBox/textArrayBox.yy",},}, + {"id":{"name":"BBMOD_ERenderCommand","path":"scripts/BBMOD_ERenderCommand/BBMOD_ERenderCommand.yy",},}, {"id":{"name":"value_snap","path":"scripts/value_snap/value_snap.yy",},}, + {"id":{"name":"BBMOD_DualQuaternion","path":"scripts/BBMOD_DualQuaternion/BBMOD_DualQuaternion.yy",},}, {"id":{"name":"node_sdf","path":"scripts/node_sdf/node_sdf.yy",},}, {"id":{"name":"file_dropper","path":"extensions/file_dropper/file_dropper.yy",},}, {"id":{"name":"compat_path_array","path":"scripts/compat_path_array/compat_path_array.yy",},}, @@ -1701,15 +1977,21 @@ {"id":{"name":"node_export","path":"scripts/node_export/node_export.yy",},}, {"id":{"name":"controlPointBox","path":"scripts/controlPointBox/controlPointBox.yy",},}, {"id":{"name":"sh_vertex_pt","path":"shaders/sh_vertex_pt/sh_vertex_pt.yy",},}, + {"id":{"name":"BBMOD_TerrainCollisionModule","path":"scripts/BBMOD_TerrainCollisionModule/BBMOD_TerrainCollisionModule.yy",},}, {"id":{"name":"fd_rectangle_set_velocity_dissipation_type","path":"scripts/fd_rectangle_set_velocity_dissipation_type/fd_rectangle_set_velocity_dissipation_type.yy",},}, {"id":{"name":"node_color_replacement","path":"scripts/node_color_replacement/node_color_replacement.yy",},}, {"id":{"name":"path_reader","path":"scripts/path_reader/path_reader.yy",},}, + {"id":{"name":"BBMOD_SetVec2Module","path":"scripts/BBMOD_SetVec2Module/BBMOD_SetVec2Module.yy",},}, {"id":{"name":"node_3d_repeat","path":"scripts/node_3d_repeat/node_3d_repeat.yy",},}, {"id":{"name":"point_direction_positive","path":"scripts/point_direction_positive/point_direction_positive.yy",},}, {"id":{"name":"s_node_fluidSim_add_fluid","path":"sprites/s_node_fluidSim_add_fluid/s_node_fluidSim_add_fluid.yy",},}, {"id":{"name":"node_blur_contrast","path":"scripts/node_blur_contrast/node_blur_contrast.yy",},}, {"id":{"name":"sh_trail_filler","path":"shaders/sh_trail_filler/sh_trail_filler.yy",},}, + {"id":{"name":"BBMOD_MixSpeedModule","path":"scripts/BBMOD_MixSpeedModule/BBMOD_MixSpeedModule.yy",},}, + {"id":{"name":"__bbmod_light_ambient","path":"scripts/__bbmod_light_ambient/__bbmod_light_ambient.yy",},}, {"id":{"name":"s_fx_pixel","path":"sprites/s_fx_pixel/s_fx_pixel.yy",},}, + {"id":{"name":"BBMOD_CollisionEventModule","path":"scripts/BBMOD_CollisionEventModule/BBMOD_CollisionEventModule.yy",},}, + {"id":{"name":"BBMOD_Resouce","path":"scripts/BBMOD_Resouce/BBMOD_Resouce.yy",},}, {"id":{"name":"gradients_function","path":"scripts/gradients_function/gradients_function.yy",},}, {"id":{"name":"s_node_border","path":"sprites/s_node_border/s_node_border.yy",},}, {"id":{"name":"d3_vector","path":"scripts/d3_vector/d3_vector.yy",},}, @@ -1718,6 +2000,7 @@ {"id":{"name":"node_local_analyze","path":"scripts/node_local_analyze/node_local_analyze.yy",},}, {"id":{"name":"s_node_blur_contrast","path":"sprites/s_node_blur_contrast/s_node_blur_contrast.yy",},}, {"id":{"name":"fd_y","path":"scripts/fd_y/fd_y.yy",},}, + {"id":{"name":"bbmod_gpu_get_default_state","path":"scripts/bbmod_gpu_get_default_state/bbmod_gpu_get_default_state.yy",},}, {"id":{"name":"node_string_regex_search","path":"scripts/node_string_regex_search/node_string_regex_search.yy",},}, {"id":{"name":"s_node_text_render","path":"sprites/s_node_text_render/s_node_text_render.yy",},}, {"id":{"name":"__init_global","path":"scripts/__init_global/__init_global.yy",},}, @@ -1732,10 +2015,13 @@ {"id":{"name":"string_cut","path":"scripts/string_cut/string_cut.yy",},}, {"id":{"name":"node_3d_prim_cylinder","path":"scripts/node_3d_prim_cylinder/node_3d_prim_cylinder.yy",},}, {"id":{"name":"panel_nodes","path":"scripts/panel_nodes/panel_nodes.yy",},}, + {"id":{"name":"__bbmod_init","path":"scripts/__bbmod_init/__bbmod_init.yy",},}, {"id":{"name":"sh_gradient_points","path":"shaders/sh_gradient_points/sh_gradient_points.yy",},}, {"id":{"name":"s_node_vfx_turb","path":"sprites/s_node_vfx_turb/s_node_vfx_turb.yy",},}, + {"id":{"name":"BBMOD_ShDefaultUnlit","path":"shaders/BBMOD_ShDefaultUnlit/BBMOD_ShDefaultUnlit.yy",},}, {"id":{"name":"o_dialog_l_system","path":"objects/o_dialog_l_system/o_dialog_l_system.yy",},}, {"id":{"name":"GmlLua","path":"scripts/GmlLua/GmlLua.yy",},}, + {"id":{"name":"__bbmod_defines","path":"scripts/__bbmod_defines/__bbmod_defines.yy",},}, {"id":{"name":"sh_shadow_cast_light_sep","path":"shaders/sh_shadow_cast_light_sep/sh_shadow_cast_light_sep.yy",},}, {"id":{"name":"node_grid","path":"scripts/node_grid/node_grid.yy",},}, {"id":{"name":"node_edge_detect","path":"scripts/node_edge_detect/node_edge_detect.yy",},}, @@ -1751,8 +2037,10 @@ {"id":{"name":"node_dilate","path":"scripts/node_dilate/node_dilate.yy",},}, {"id":{"name":"fd_rectangle_get_acceleration_b","path":"scripts/fd_rectangle_get_acceleration_b/fd_rectangle_get_acceleration_b.yy",},}, {"id":{"name":"surface_draw_functions","path":"scripts/surface_draw_functions/surface_draw_functions.yy",},}, + {"id":{"name":"BBMOD_Matrix","path":"scripts/BBMOD_Matrix/BBMOD_Matrix.yy",},}, {"id":{"name":"pack_shelf","path":"scripts/pack_shelf/pack_shelf.yy",},}, {"id":{"name":"s_node_path_trim","path":"sprites/s_node_path_trim/s_node_path_trim.yy",},}, + {"id":{"name":"__bbmod_ssao","path":"scripts/__bbmod_ssao/__bbmod_ssao.yy",},}, {"id":{"name":"libfilesystem","path":"extensions/libfilesystem/libfilesystem.yy",},}, {"id":{"name":"node_channels_hsv","path":"scripts/node_channels_hsv/node_channels_hsv.yy",},}, {"id":{"name":"sh_stripe","path":"shaders/sh_stripe/sh_stripe.yy",},}, @@ -1761,6 +2049,7 @@ {"id":{"name":"sh_blend_contrast","path":"shaders/sh_blend_contrast/sh_blend_contrast.yy",},}, {"id":{"name":"node_3d_prim_cube","path":"scripts/node_3d_prim_cube/node_3d_prim_cube.yy",},}, {"id":{"name":"s_node_fluidSim_add_collider","path":"sprites/s_node_fluidSim_add_collider/s_node_fluidSim_add_collider.yy",},}, + {"id":{"name":"BBMOD_ShDefaultUnlitAnimated","path":"shaders/BBMOD_ShDefaultUnlitAnimated/BBMOD_ShDefaultUnlitAnimated.yy",},}, {"id":{"name":"node_3d_plane","path":"scripts/node_3d_plane/node_3d_plane.yy",},}, {"id":{"name":"node_display_text","path":"scripts/node_display_text/node_display_text.yy",},}, {"id":{"name":"s_kenney","path":"sprites/s_kenney/s_kenney.yy",},}, @@ -1774,20 +2063,24 @@ {"id":{"name":"panel_color","path":"scripts/panel_color/panel_color.yy",},}, {"id":{"name":"s_node_line","path":"sprites/s_node_line/s_node_line.yy",},}, {"id":{"name":"fd_rectangle_get_material_dissipation_value","path":"scripts/fd_rectangle_get_material_dissipation_value/fd_rectangle_get_material_dissipation_value.yy",},}, + {"id":{"name":"BBMOD_MixVec2FromSpeedModule","path":"scripts/BBMOD_MixVec2FromSpeedModule/BBMOD_MixVec2FromSpeedModule.yy",},}, {"id":{"name":"panel_graph","path":"scripts/panel_graph/panel_graph.yy",},}, {"id":{"name":"node_counter","path":"scripts/node_counter/node_counter.yy",},}, {"id":{"name":"delau_helper","path":"scripts/delau_helper/delau_helper.yy",},}, {"id":{"name":"s_node_shape_polygon","path":"sprites/s_node_shape_polygon/s_node_shape_polygon.yy",},}, {"id":{"name":"sh_color_picker_hue","path":"shaders/sh_color_picker_hue/sh_color_picker_hue.yy",},}, {"id":{"name":"node_path_eval","path":"scripts/node_path_eval/node_path_eval.yy",},}, + {"id":{"name":"BBMOD_StaticBatch","path":"scripts/BBMOD_StaticBatch/BBMOD_StaticBatch.yy",},}, {"id":{"name":"node_vector_dot","path":"scripts/node_vector_dot/node_vector_dot.yy",},}, {"id":{"name":"o_dialog_arrayBox","path":"objects/o_dialog_arrayBox/o_dialog_arrayBox.yy",},}, + {"id":{"name":"BBMOD_SprColorGradingLUT","path":"sprites/BBMOD_SprColorGradingLUT/BBMOD_SprColorGradingLUT.yy",},}, {"id":{"name":"node_path_transform","path":"scripts/node_path_transform/node_path_transform.yy",},}, {"id":{"name":"node_path_map_area","path":"scripts/node_path_map_area/node_path_map_area.yy",},}, {"id":{"name":"s_node_timeline_preview","path":"sprites/s_node_timeline_preview/s_node_timeline_preview.yy",},}, {"id":{"name":"node_csv_file_read","path":"scripts/node_csv_file_read/node_csv_file_read.yy",},}, {"id":{"name":"scrollPane","path":"scripts/scrollPane/scrollPane.yy",},}, {"id":{"name":"node_gradient_extract","path":"scripts/node_gradient_extract/node_gradient_extract.yy",},}, + {"id":{"name":"BBMOD_Exception","path":"scripts/BBMOD_Exception/BBMOD_Exception.yy",},}, {"id":{"name":"node_base_convert","path":"scripts/node_base_convert/node_base_convert.yy",},}, {"id":{"name":"s_node_normal_light","path":"sprites/s_node_normal_light/s_node_normal_light.yy",},}, {"id":{"name":"node_transform_single","path":"scripts/node_transform_single/node_transform_single.yy",},}, @@ -1796,8 +2089,10 @@ {"id":{"name":"s_node_path_transform","path":"sprites/s_node_path_transform/s_node_path_transform.yy",},}, {"id":{"name":"s_node_sdf","path":"sprites/s_node_sdf/s_node_sdf.yy",},}, {"id":{"name":"s_node_path_blend","path":"sprites/s_node_path_blend/s_node_path_blend.yy",},}, + {"id":{"name":"BBMOD_ShDefaultDepthLightmap","path":"shaders/BBMOD_ShDefaultDepthLightmap/BBMOD_ShDefaultDepthLightmap.yy",},}, {"id":{"name":"preview_overlay_scalar","path":"scripts/preview_overlay_scalar/preview_overlay_scalar.yy",},}, {"id":{"name":"s_node_3d_extrude","path":"sprites/s_node_3d_extrude/s_node_3d_extrude.yy",},}, + {"id":{"name":"BBMOD_EParticle","path":"scripts/BBMOD_EParticle/BBMOD_EParticle.yy",},}, {"id":{"name":"buttonGradient","path":"scripts/buttonGradient/buttonGradient.yy",},}, {"id":{"name":"sh_draw_downsample","path":"shaders/sh_draw_downsample/sh_draw_downsample.yy",},}, {"id":{"name":"__view_get","path":"scripts/__view_get/__view_get.yy",},}, @@ -1806,29 +2101,42 @@ {"id":{"name":"fd_rectangle_create","path":"scripts/fd_rectangle_create/fd_rectangle_create.yy",},}, {"id":{"name":"s_node_level_selector","path":"sprites/s_node_level_selector/s_node_level_selector.yy",},}, {"id":{"name":"random_function","path":"scripts/random_function/random_function.yy",},}, + {"id":{"name":"BBMOD_MixVec3FromSpeedModule","path":"scripts/BBMOD_MixVec3FromSpeedModule/BBMOD_MixVec3FromSpeedModule.yy",},}, {"id":{"name":"s_node_input","path":"sprites/s_node_input/s_node_input.yy",},}, {"id":{"name":"addon","path":"objects/addon/addon.yy",},}, + {"id":{"name":"BBMOD_EmissionOverTimeModule","path":"scripts/BBMOD_EmissionOverTimeModule/BBMOD_EmissionOverTimeModule.yy",},}, {"id":{"name":"string_decimal","path":"scripts/string_decimal/string_decimal.yy",},}, + {"id":{"name":"__bbmod_camera","path":"scripts/__bbmod_camera/__bbmod_camera.yy",},}, {"id":{"name":"_f_h3","path":"fonts/_f_h3/_f_h3.yy",},}, {"id":{"name":"s_node_math","path":"sprites/s_node_math/s_node_math.yy",},}, {"id":{"name":"node_iterator_index","path":"scripts/node_iterator_index/node_iterator_index.yy",},}, + {"id":{"name":"BBMOD_AddVec3OverTimeModule","path":"scripts/BBMOD_AddVec3OverTimeModule/BBMOD_AddVec3OverTimeModule.yy",},}, {"id":{"name":"s_node_palette_sort","path":"sprites/s_node_palette_sort/s_node_palette_sort.yy",},}, + {"id":{"name":"BBMOD_ShDefaultDepthAnimated","path":"shaders/BBMOD_ShDefaultDepthAnimated/BBMOD_ShDefaultDepthAnimated.yy",},}, + {"id":{"name":"BBMOD_BaseRenderer","path":"scripts/BBMOD_BaseRenderer/BBMOD_BaseRenderer.yy",},}, {"id":{"name":"node_greyscale","path":"scripts/node_greyscale/node_greyscale.yy",},}, {"id":{"name":"s_node_cross_product_3d","path":"sprites/s_node_cross_product_3d/s_node_cross_product_3d.yy",},}, {"id":{"name":"node_color_adjustment","path":"scripts/node_color_adjustment/node_color_adjustment.yy",},}, {"id":{"name":"s_node_strandSim_force","path":"sprites/s_node_strandSim_force/s_node_strandSim_force.yy",},}, + {"id":{"name":"BBMOD_ParticleEmitter","path":"scripts/BBMOD_ParticleEmitter/BBMOD_ParticleEmitter.yy",},}, {"id":{"name":"s_node_array_reverse","path":"sprites/s_node_array_reverse/s_node_array_reverse.yy",},}, + {"id":{"name":"BBMOD_Shader","path":"scripts/BBMOD_Shader/BBMOD_Shader.yy",},}, {"id":{"name":"sh_ani_noise","path":"shaders/sh_ani_noise/sh_ani_noise.yy",},}, {"id":{"name":"rectangle_collision","path":"scripts/rectangle_collision/rectangle_collision.yy",},}, {"id":{"name":"sh_level","path":"shaders/sh_level/sh_level.yy",},}, {"id":{"name":"sh_grid_tri","path":"shaders/sh_grid_tri/sh_grid_tri.yy",},}, {"id":{"name":"s_node_text","path":"sprites/s_node_text/s_node_text.yy",},}, {"id":{"name":"panel_menu","path":"scripts/panel_menu/panel_menu.yy",},}, + {"id":{"name":"BBMOD_AddVec2OnCollisionModule","path":"scripts/BBMOD_AddVec2OnCollisionModule/BBMOD_AddVec2OnCollisionModule.yy",},}, + {"id":{"name":"__bbmod_logging","path":"scripts/__bbmod_logging/__bbmod_logging.yy",},}, {"id":{"name":"s_node_ase_file","path":"sprites/s_node_ase_file/s_node_ase_file.yy",},}, {"id":{"name":"draw_line_round","path":"scripts/draw_line_round/draw_line_round.yy",},}, + {"id":{"name":"BBMOD_EAntialiasing","path":"scripts/BBMOD_EAntialiasing/BBMOD_EAntialiasing.yy",},}, {"id":{"name":"vectorBox","path":"scripts/vectorBox/vectorBox.yy",},}, {"id":{"name":"sh_blend_normal_dim","path":"shaders/sh_blend_normal_dim/sh_blend_normal_dim.yy",},}, + {"id":{"name":"BBMOD_AddVec4OnCollisionModule","path":"scripts/BBMOD_AddVec4OnCollisionModule/BBMOD_AddVec4OnCollisionModule.yy",},}, {"id":{"name":"node_skew","path":"scripts/node_skew/node_skew.yy",},}, + {"id":{"name":"bbmod_surface_check","path":"scripts/bbmod_surface_check/bbmod_surface_check.yy",},}, {"id":{"name":"s_node_dot_product","path":"sprites/s_node_dot_product/s_node_dot_product.yy",},}, {"id":{"name":"s_node_canvas","path":"sprites/s_node_canvas/s_node_canvas.yy",},}, {"id":{"name":"sh_downsample","path":"shaders/sh_downsample/sh_downsample.yy",},}, @@ -1845,27 +2153,36 @@ {"id":{"name":"s_node_vfx_vortex","path":"sprites/s_node_vfx_vortex/s_node_vfx_vortex.yy",},}, {"id":{"name":"s_node_array_add","path":"sprites/s_node_array_add/s_node_array_add.yy",},}, {"id":{"name":"s_node_counter","path":"sprites/s_node_counter/s_node_counter.yy",},}, + {"id":{"name":"BBMOD_AnimationState","path":"scripts/BBMOD_AnimationState/BBMOD_AnimationState.yy",},}, {"id":{"name":"s_node_image_sequence_to_anim","path":"sprites/s_node_image_sequence_to_anim/s_node_image_sequence_to_anim.yy",},}, {"id":{"name":"_node_VFX_spawner","path":"scripts/_node_VFX_spawner/_node_VFX_spawner.yy",},}, + {"id":{"name":"BBMOD_ShDefaultLightmap","path":"shaders/BBMOD_ShDefaultLightmap/BBMOD_ShDefaultLightmap.yy",},}, {"id":{"name":"sh_channel_B_grey","path":"shaders/sh_channel_B_grey/sh_channel_B_grey.yy",},}, {"id":{"name":"draw_UI_scale","path":"scripts/draw_UI_scale/draw_UI_scale.yy",},}, + {"id":{"name":"BBMOD_Material","path":"scripts/BBMOD_Material/BBMOD_Material.yy",},}, {"id":{"name":"s_node_strandSim_update","path":"sprites/s_node_strandSim_update/s_node_strandSim_update.yy",},}, {"id":{"name":"s_node_RGB_combine","path":"sprites/s_node_RGB_combine/s_node_RGB_combine.yy",},}, {"id":{"name":"s_node_shadow_cast","path":"sprites/s_node_shadow_cast/s_node_shadow_cast.yy",},}, {"id":{"name":"path_function","path":"scripts/path_function/path_function.yy",},}, {"id":{"name":"fd_rectangle_set_velocity_dissipation_value","path":"scripts/fd_rectangle_set_velocity_dissipation_value/fd_rectangle_set_velocity_dissipation_value.yy",},}, + {"id":{"name":"BBMOD_ShDefaultDepthBatched","path":"shaders/BBMOD_ShDefaultDepthBatched/BBMOD_ShDefaultDepthBatched.yy",},}, {"id":{"name":"panel_history","path":"scripts/panel_history/panel_history.yy",},}, {"id":{"name":"sh_channel_H","path":"shaders/sh_channel_H/sh_channel_H.yy",},}, {"id":{"name":"s_node_RGB","path":"sprites/s_node_RGB/s_node_RGB.yy",},}, {"id":{"name":"fd_rectangle_draw","path":"scripts/fd_rectangle_draw/fd_rectangle_draw.yy",},}, + {"id":{"name":"BBMOD_SetColorModule","path":"scripts/BBMOD_SetColorModule/BBMOD_SetColorModule.yy",},}, + {"id":{"name":"BBMOD_MixRealModule","path":"scripts/BBMOD_MixRealModule/BBMOD_MixRealModule.yy",},}, {"id":{"name":"s_node_export","path":"sprites/s_node_export/s_node_export.yy",},}, {"id":{"name":"texture_set_interpolation","path":"scripts/texture_set_interpolation/texture_set_interpolation.yy",},}, + {"id":{"name":"BBMOD_ShInstanceIDAnimated","path":"shaders/BBMOD_ShInstanceIDAnimated/BBMOD_ShInstanceIDAnimated.yy",},}, {"id":{"name":"oRigidbody","path":"objects/oRigidbody/oRigidbody.yy",},}, + {"id":{"name":"BBMOD_DynamicBatch","path":"scripts/BBMOD_DynamicBatch/BBMOD_DynamicBatch.yy",},}, {"id":{"name":"node_grey_to_alpha","path":"scripts/node_grey_to_alpha/node_grey_to_alpha.yy",},}, {"id":{"name":"sh_blend_add","path":"shaders/sh_blend_add/sh_blend_add.yy",},}, {"id":{"name":"node_de_stray","path":"scripts/node_de_stray/node_de_stray.yy",},}, {"id":{"name":"sh_channel_G","path":"shaders/sh_channel_G/sh_channel_G.yy",},}, {"id":{"name":"s_node_dilate","path":"sprites/s_node_dilate/s_node_dilate.yy",},}, + {"id":{"name":"BBMOD_State","path":"scripts/BBMOD_State/BBMOD_State.yy",},}, {"id":{"name":"node_collection","path":"scripts/node_collection/node_collection.yy",},}, {"id":{"name":"node_value","path":"scripts/node_value/node_value.yy",},}, {"id":{"name":"draw_line_curve","path":"scripts/draw_line_curve/draw_line_curve.yy",},}, @@ -1880,11 +2197,13 @@ {"id":{"name":"sh_warp_4points","path":"shaders/sh_warp_4points/sh_warp_4points.yy",},}, {"id":{"name":"_f_p2","path":"fonts/_f_p2/_f_p2.yy",},}, {"id":{"name":"fd_rectangle_get_pressure_iteration_type","path":"scripts/fd_rectangle_get_pressure_iteration_type/fd_rectangle_get_pressure_iteration_type.yy",},}, + {"id":{"name":"BBMOD_PostProcessor","path":"scripts/BBMOD_PostProcessor/BBMOD_PostProcessor.yy",},}, {"id":{"name":"node_text_file_write","path":"scripts/node_text_file_write/node_text_file_write.yy",},}, {"id":{"name":"sh_lum2alpha","path":"shaders/sh_lum2alpha/sh_lum2alpha.yy",},}, {"id":{"name":"button","path":"scripts/button/button.yy",},}, {"id":{"name":"s_node_3d_sphere","path":"sprites/s_node_3d_sphere/s_node_3d_sphere.yy",},}, {"id":{"name":"fd_rectangle_replace_velocity","path":"scripts/fd_rectangle_replace_velocity/fd_rectangle_replace_velocity.yy",},}, + {"id":{"name":"BBMOD_ShInstanceID","path":"shaders/BBMOD_ShInstanceID/BBMOD_ShInstanceID.yy",},}, {"id":{"name":"node_VFX_effector","path":"scripts/node_VFX_effector/node_VFX_effector.yy",},}, {"id":{"name":"sh_blur_gaussian","path":"shaders/sh_blur_gaussian/sh_blur_gaussian.yy",},}, {"id":{"name":"node_path_shift","path":"scripts/node_path_shift/node_path_shift.yy",},}, @@ -1895,6 +2214,7 @@ {"id":{"name":"paddingBox","path":"scripts/paddingBox/paddingBox.yy",},}, {"id":{"name":"fd_rectangle_set_visualization_shader","path":"scripts/fd_rectangle_set_visualization_shader/fd_rectangle_set_visualization_shader.yy",},}, {"id":{"name":"addonPanel","path":"scripts/addonPanel/addonPanel.yy",},}, + {"id":{"name":"BBMOD_ParticleShader","path":"scripts/BBMOD_ParticleShader/BBMOD_ParticleShader.yy",},}, {"id":{"name":"s_node_ase_layer","path":"sprites/s_node_ase_layer/s_node_ase_layer.yy",},}, {"id":{"name":"_f_p1","path":"fonts/_f_p1/_f_p1.yy",},}, {"id":{"name":"tuple_functions","path":"scripts/tuple_functions/tuple_functions.yy",},}, @@ -1908,10 +2228,12 @@ {"id":{"name":"s_node_feedback_output","path":"sprites/s_node_feedback_output/s_node_feedback_output.yy",},}, {"id":{"name":"sh_fd_repulse","path":"shaders/sh_fd_repulse/sh_fd_repulse.yy",},}, {"id":{"name":"sh_surface_replace_fast_find","path":"shaders/sh_surface_replace_fast_find/sh_surface_replace_fast_find.yy",},}, + {"id":{"name":"BBMOD_BaseMaterial","path":"scripts/BBMOD_BaseMaterial/BBMOD_BaseMaterial.yy",},}, {"id":{"name":"node_3d_combine","path":"scripts/node_3d_combine/node_3d_combine.yy",},}, {"id":{"name":"fd_rectangle_add_material","path":"scripts/fd_rectangle_add_material/fd_rectangle_add_material.yy",},}, {"id":{"name":"fd_rectangle_set_target","path":"scripts/fd_rectangle_set_target/fd_rectangle_set_target.yy",},}, {"id":{"name":"draw_line_dashed","path":"scripts/draw_line_dashed/draw_line_dashed.yy",},}, + {"id":{"name":"BBMOD_SpotLight","path":"scripts/BBMOD_SpotLight/BBMOD_SpotLight.yy",},}, {"id":{"name":"pseudo_regex","path":"scripts/pseudo_regex/pseudo_regex.yy",},}, {"id":{"name":"node_invert","path":"scripts/node_invert/node_invert.yy",},}, {"id":{"name":"o_dialog_history","path":"objects/o_dialog_history/o_dialog_history.yy",},}, @@ -1930,12 +2252,16 @@ {"id":{"name":"node_logic_operate","path":"scripts/node_logic_operate/node_logic_operate.yy",},}, {"id":{"name":"node_repeat","path":"scripts/node_repeat/node_repeat.yy",},}, {"id":{"name":"sh_fd_advect_velocity_1_glsl","path":"shaders/sh_fd_advect_velocity_1_glsl/sh_fd_advect_velocity_1_glsl.yy",},}, + {"id":{"name":"BBMOD_SphereCollider","path":"scripts/BBMOD_SphereCollider/BBMOD_SphereCollider.yy",},}, {"id":{"name":"mac_window_step","path":"scripts/mac_window_step/mac_window_step.yy",},}, {"id":{"name":"s_node_image","path":"sprites/s_node_image/s_node_image.yy",},}, {"id":{"name":"__node_value_processor","path":"scripts/__node_value_processor/__node_value_processor.yy",},}, + {"id":{"name":"BBMOD_BaseCamera","path":"scripts/BBMOD_BaseCamera/BBMOD_BaseCamera.yy",},}, + {"id":{"name":"BBMOD_ShParticleUnlit","path":"shaders/BBMOD_ShParticleUnlit/BBMOD_ShParticleUnlit.yy",},}, {"id":{"name":"s_node_group_input","path":"sprites/s_node_group_input/s_node_group_input.yy",},}, {"id":{"name":"sample_projects","path":"scripts/sample_projects/sample_projects.yy",},}, {"id":{"name":"load_function","path":"scripts/load_function/load_function.yy",},}, + {"id":{"name":"BBMOD_VertexFormat","path":"scripts/BBMOD_VertexFormat/BBMOD_VertexFormat.yy",},}, {"id":{"name":"draw_fit","path":"scripts/draw_fit/draw_fit.yy",},}, {"id":{"name":"ds_map","path":"scripts/ds_map/ds_map.yy",},}, {"id":{"name":"s_node_morph_surface","path":"sprites/s_node_morph_surface/s_node_morph_surface.yy",},}, @@ -1945,10 +2271,12 @@ {"id":{"name":"sh_atlas","path":"shaders/sh_atlas/sh_atlas.yy",},}, {"id":{"name":"node_3d_extrude","path":"scripts/node_3d_extrude/node_3d_extrude.yy",},}, {"id":{"name":"node_path_array","path":"scripts/node_path_array/node_path_array.yy",},}, + {"id":{"name":"BBMOD_SprDefaultBaseOpacity","path":"sprites/BBMOD_SprDefaultBaseOpacity/BBMOD_SprDefaultBaseOpacity.yy",},}, {"id":{"name":"node_scale","path":"scripts/node_scale/node_scale.yy",},}, {"id":{"name":"node_array_insert","path":"scripts/node_array_insert/node_array_insert.yy",},}, {"id":{"name":"sh_displace","path":"shaders/sh_displace/sh_displace.yy",},}, {"id":{"name":"node_group","path":"scripts/node_group/node_group.yy",},}, + {"id":{"name":"BBMOD_DefaultShader","path":"scripts/BBMOD_DefaultShader/BBMOD_DefaultShader.yy",},}, {"id":{"name":"fd_rectangle_get_material_height","path":"scripts/fd_rectangle_get_material_height/fd_rectangle_get_material_height.yy",},}, {"id":{"name":"rangeBox","path":"scripts/rangeBox/rangeBox.yy",},}, {"id":{"name":"node_VFX_effect_accelerate","path":"scripts/node_VFX_effect_accelerate/node_VFX_effect_accelerate.yy",},}, @@ -1959,6 +2287,7 @@ {"id":{"name":"node_path_plot","path":"scripts/node_path_plot/node_path_plot.yy",},}, {"id":{"name":"node_bw","path":"scripts/node_bw/node_bw.yy",},}, {"id":{"name":"sh_de_corner","path":"shaders/sh_de_corner/sh_de_corner.yy",},}, + {"id":{"name":"BBMOD_Vec3","path":"scripts/BBMOD_Vec3/BBMOD_Vec3.yy",},}, {"id":{"name":"node_rigid_object_spawner","path":"scripts/node_rigid_object_spawner/node_rigid_object_spawner.yy",},}, {"id":{"name":"o_dialog_graph_connection","path":"objects/o_dialog_graph_connection/o_dialog_graph_connection.yy",},}, {"id":{"name":"sh_fd_advect_velocity_0_glsl","path":"shaders/sh_fd_advect_velocity_0_glsl/sh_fd_advect_velocity_0_glsl.yy",},}, @@ -1967,6 +2296,7 @@ {"id":{"name":"fd_rectangle_get_material_surface","path":"scripts/fd_rectangle_get_material_surface/fd_rectangle_get_material_surface.yy",},}, {"id":{"name":"fd_rectangle_clear","path":"scripts/fd_rectangle_clear/fd_rectangle_clear.yy",},}, {"id":{"name":"instance_create","path":"scripts/instance_create/instance_create.yy",},}, + {"id":{"name":"bbmod_get_calling_function_name","path":"scripts/bbmod_get_calling_function_name/bbmod_get_calling_function_name.yy",},}, {"id":{"name":"__rectangle","path":"scripts/__rectangle/__rectangle.yy",},}, {"id":{"name":"s_node_iterator_index","path":"sprites/s_node_iterator_index/s_node_iterator_index.yy",},}, {"id":{"name":"node_de_corner","path":"scripts/node_de_corner/node_de_corner.yy",},}, @@ -1977,6 +2307,7 @@ {"id":{"name":"timer_function","path":"scripts/timer_function/timer_function.yy",},}, {"id":{"name":"fd_rectangle_get_material_time_step","path":"scripts/fd_rectangle_get_material_time_step/fd_rectangle_get_material_time_step.yy",},}, {"id":{"name":"node_pack_sprites","path":"scripts/node_pack_sprites/node_pack_sprites.yy",},}, + {"id":{"name":"BBMOD_AddVec2OverTimeModule","path":"scripts/BBMOD_AddVec2OverTimeModule/BBMOD_AddVec2OverTimeModule.yy",},}, {"id":{"name":"font_data","path":"scripts/font_data/font_data.yy",},}, {"id":{"name":"animation_curve","path":"scripts/animation_curve/animation_curve.yy",},}, {"id":{"name":"node_camera","path":"scripts/node_camera/node_camera.yy",},}, @@ -1986,6 +2317,7 @@ {"id":{"name":"node_rigid_variable","path":"scripts/node_rigid_variable/node_rigid_variable.yy",},}, {"id":{"name":"s_node_zoom","path":"sprites/s_node_zoom/s_node_zoom.yy",},}, {"id":{"name":"migration_function","path":"scripts/migration_function/migration_function.yy",},}, + {"id":{"name":"BBMOD_Property","path":"scripts/BBMOD_Property/BBMOD_Property.yy",},}, {"id":{"name":"node_noise_fbm","path":"scripts/node_noise_fbm/node_noise_fbm.yy",},}, {"id":{"name":"sh_channel_V","path":"shaders/sh_channel_V/sh_channel_V.yy",},}, {"id":{"name":"__shapes","path":"scripts/__shapes/__shapes.yy",},}, @@ -1993,13 +2325,17 @@ {"id":{"name":"shell_helper","path":"scripts/shell_helper/shell_helper.yy",},}, {"id":{"name":"node_strand_collision","path":"scripts/node_strand_collision/node_strand_collision.yy",},}, {"id":{"name":"s_node_atlas_get","path":"sprites/s_node_atlas_get/s_node_atlas_get.yy",},}, + {"id":{"name":"BBMOD_ShInstanceIDLightmap","path":"shaders/BBMOD_ShInstanceIDLightmap/BBMOD_ShInstanceIDLightmap.yy",},}, {"id":{"name":"sh_flood_fill_it","path":"shaders/sh_flood_fill_it/sh_flood_fill_it.yy",},}, + {"id":{"name":"node_iterator_sort_input","path":"scripts/node_iterator_sort_input/node_iterator_sort_input.yy",},}, {"id":{"name":"draw_corner","path":"scripts/draw_corner/draw_corner.yy",},}, {"id":{"name":"o_dialog_preference","path":"objects/o_dialog_preference/o_dialog_preference.yy",},}, + {"id":{"name":"BBMOD_ShInstanceIDBatched","path":"shaders/BBMOD_ShInstanceIDBatched/BBMOD_ShInstanceIDBatched.yy",},}, {"id":{"name":"node_wrap","path":"scripts/node_wrap/node_wrap.yy",},}, {"id":{"name":"node_3d_prim_sphere","path":"scripts/node_3d_prim_sphere/node_3d_prim_sphere.yy",},}, {"id":{"name":"o_dialog_about","path":"objects/o_dialog_about/o_dialog_about.yy",},}, {"id":{"name":"dialog_management","path":"scripts/dialog_management/dialog_management.yy",},}, + {"id":{"name":"BBMOD_CollisionKillModule","path":"scripts/BBMOD_CollisionKillModule/BBMOD_CollisionKillModule.yy",},}, {"id":{"name":"draw_shapes","path":"scripts/draw_shapes/draw_shapes.yy",},}, {"id":{"name":"node_channels","path":"scripts/node_channels/node_channels.yy",},}, {"id":{"name":"string_eval_tree","path":"scripts/string_eval_tree/string_eval_tree.yy",},}, @@ -2021,9 +2357,11 @@ {"id":{"name":"sh_fd_visualize_pixel_art_fiery_smoke_glsl","path":"shaders/sh_fd_visualize_pixel_art_fiery_smoke_glsl/sh_fd_visualize_pixel_art_fiery_smoke_glsl.yy",},}, {"id":{"name":"node_ase_layer","path":"scripts/node_ase_layer/node_ase_layer.yy",},}, {"id":{"name":"sh_local_analyze","path":"shaders/sh_local_analyze/sh_local_analyze.yy",},}, + {"id":{"name":"bbmod_json_load","path":"scripts/bbmod_json_load/bbmod_json_load.yy",},}, {"id":{"name":"s_node_array_zip","path":"sprites/s_node_array_zip/s_node_array_zip.yy",},}, {"id":{"name":"fd_rectangle_get_material_width","path":"scripts/fd_rectangle_get_material_width/fd_rectangle_get_material_width.yy",},}, {"id":{"name":"font_sprite_loader","path":"scripts/font_sprite_loader/font_sprite_loader.yy",},}, + {"id":{"name":"BBMOD_FrustumCollider","path":"scripts/BBMOD_FrustumCollider/BBMOD_FrustumCollider.yy",},}, {"id":{"name":"s_node_draw_atlas","path":"sprites/s_node_draw_atlas/s_node_draw_atlas.yy",},}, {"id":{"name":"s_node_noise_aniso","path":"sprites/s_node_noise_aniso/s_node_noise_aniso.yy",},}, {"id":{"name":"steam_ugc_functions","path":"scripts/steam_ugc_functions/steam_ugc_functions.yy",},}, diff --git a/datafiles/data/BBMOD/BBMOD b/datafiles/data/BBMOD/BBMOD new file mode 100644 index 000000000..044385bd3 Binary files /dev/null and b/datafiles/data/BBMOD/BBMOD differ diff --git a/datafiles/data/BBMOD/BBMOD.dll b/datafiles/data/BBMOD/BBMOD.dll new file mode 100644 index 000000000..b2250147a Binary files /dev/null and b/datafiles/data/BBMOD/BBMOD.dll differ diff --git a/datafiles/data/BBMOD/BBMOD.exe b/datafiles/data/BBMOD/BBMOD.exe new file mode 100644 index 000000000..8eb32fbb9 Binary files /dev/null and b/datafiles/data/BBMOD/BBMOD.exe differ diff --git a/datafiles/data/BBMOD/LICENSE-Assimp b/datafiles/data/BBMOD/LICENSE-Assimp new file mode 100644 index 000000000..7d2194deb --- /dev/null +++ b/datafiles/data/BBMOD/LICENSE-Assimp @@ -0,0 +1,78 @@ +Open Asset Import Library (assimp) + +Copyright (c) 2006-2021, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +****************************************************************************** + +AN EXCEPTION applies to all files in the ./test/models-nonbsd folder. +These are 3d models for testing purposes, from various free sources +on the internet. They are - unless otherwise stated - copyright of +their respective creators, which may impose additional requirements +on the use of their work. For any of these models, see +.source.txt for more legal information. Contact us if you +are a copyright holder and believe that we credited you inproperly or +if you don't want your files to appear in the repository. + + +****************************************************************************** + +Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors +http://code.google.com/p/poly2tri/ + +All rights reserved. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of Poly2Tri nor the names of its contributors may be + used to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/datafiles/data/BBMOD/Models/GizmoMove.bbmod b/datafiles/data/BBMOD/Models/GizmoMove.bbmod new file mode 100644 index 000000000..bea708563 Binary files /dev/null and b/datafiles/data/BBMOD/Models/GizmoMove.bbmod differ diff --git a/datafiles/data/BBMOD/Models/GizmoRotate.bbmod b/datafiles/data/BBMOD/Models/GizmoRotate.bbmod new file mode 100644 index 000000000..0d6cd8911 Binary files /dev/null and b/datafiles/data/BBMOD/Models/GizmoRotate.bbmod differ diff --git a/datafiles/data/BBMOD/Models/GizmoScale.bbmod b/datafiles/data/BBMOD/Models/GizmoScale.bbmod new file mode 100644 index 000000000..34819c97d Binary files /dev/null and b/datafiles/data/BBMOD/Models/GizmoScale.bbmod differ diff --git a/datafiles/data/BBMOD/Models/Sphere.bbmod b/datafiles/data/BBMOD/Models/Sphere.bbmod new file mode 100644 index 000000000..b5c23a9bd Binary files /dev/null and b/datafiles/data/BBMOD/Models/Sphere.bbmod differ diff --git a/datafiles/data/BBMOD/Models/gizmo.blend b/datafiles/data/BBMOD/Models/gizmo.blend new file mode 100644 index 000000000..eebc8c2ac Binary files /dev/null and b/datafiles/data/BBMOD/Models/gizmo.blend differ diff --git a/datafiles/data/BBMOD/Models/rotateGizmo.fbx b/datafiles/data/BBMOD/Models/rotateGizmo.fbx new file mode 100644 index 000000000..8668d3c6b Binary files /dev/null and b/datafiles/data/BBMOD/Models/rotateGizmo.fbx differ diff --git a/datafiles/data/BBMOD/Skies/IBL+0.png b/datafiles/data/BBMOD/Skies/IBL+0.png new file mode 100644 index 000000000..744d26c48 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+0.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL+10.png b/datafiles/data/BBMOD/Skies/IBL+10.png new file mode 100644 index 000000000..cd19b6b26 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+10.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL+15.png b/datafiles/data/BBMOD/Skies/IBL+15.png new file mode 100644 index 000000000..aa341b236 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+15.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL+20.png b/datafiles/data/BBMOD/Skies/IBL+20.png new file mode 100644 index 000000000..4811930af Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+20.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL+30.png b/datafiles/data/BBMOD/Skies/IBL+30.png new file mode 100644 index 000000000..8135b46e5 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+30.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL+40.png b/datafiles/data/BBMOD/Skies/IBL+40.png new file mode 100644 index 000000000..3c8624347 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+40.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL+5.png b/datafiles/data/BBMOD/Skies/IBL+5.png new file mode 100644 index 000000000..2da797e99 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+5.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL+50.png b/datafiles/data/BBMOD/Skies/IBL+50.png new file mode 100644 index 000000000..121b01933 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+50.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL+60.png b/datafiles/data/BBMOD/Skies/IBL+60.png new file mode 100644 index 000000000..db7df61e6 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+60.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL+70.png b/datafiles/data/BBMOD/Skies/IBL+70.png new file mode 100644 index 000000000..086c196ca Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+70.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL+80.png b/datafiles/data/BBMOD/Skies/IBL+80.png new file mode 100644 index 000000000..de58a84dd Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+80.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL+90.png b/datafiles/data/BBMOD/Skies/IBL+90.png new file mode 100644 index 000000000..65b29ff10 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL+90.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL-10.png b/datafiles/data/BBMOD/Skies/IBL-10.png new file mode 100644 index 000000000..b0c864318 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL-10.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL-15.png b/datafiles/data/BBMOD/Skies/IBL-15.png new file mode 100644 index 000000000..395db0301 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL-15.png differ diff --git a/datafiles/data/BBMOD/Skies/IBL-5.png b/datafiles/data/BBMOD/Skies/IBL-5.png new file mode 100644 index 000000000..89249c901 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/IBL-5.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+0.png b/datafiles/data/BBMOD/Skies/Sky+0.png new file mode 100644 index 000000000..a6e8ff6bb Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+0.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+10.png b/datafiles/data/BBMOD/Skies/Sky+10.png new file mode 100644 index 000000000..347c8cce8 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+10.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+15.png b/datafiles/data/BBMOD/Skies/Sky+15.png new file mode 100644 index 000000000..65cfe441c Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+15.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+20.png b/datafiles/data/BBMOD/Skies/Sky+20.png new file mode 100644 index 000000000..6550d7e0c Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+20.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+30.png b/datafiles/data/BBMOD/Skies/Sky+30.png new file mode 100644 index 000000000..80e69827f Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+30.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+40.png b/datafiles/data/BBMOD/Skies/Sky+40.png new file mode 100644 index 000000000..5bf91b50b Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+40.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+5.png b/datafiles/data/BBMOD/Skies/Sky+5.png new file mode 100644 index 000000000..ea8b5ef43 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+5.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+50.png b/datafiles/data/BBMOD/Skies/Sky+50.png new file mode 100644 index 000000000..3fef896d3 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+50.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+60.png b/datafiles/data/BBMOD/Skies/Sky+60.png new file mode 100644 index 000000000..d15d481fd Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+60.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+70.png b/datafiles/data/BBMOD/Skies/Sky+70.png new file mode 100644 index 000000000..6e5637b8f Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+70.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+80.png b/datafiles/data/BBMOD/Skies/Sky+80.png new file mode 100644 index 000000000..a8ffdee2e Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+80.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky+90.png b/datafiles/data/BBMOD/Skies/Sky+90.png new file mode 100644 index 000000000..6053ecbad Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky+90.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky-10.png b/datafiles/data/BBMOD/Skies/Sky-10.png new file mode 100644 index 000000000..2a798d51c Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky-10.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky-15.png b/datafiles/data/BBMOD/Skies/Sky-15.png new file mode 100644 index 000000000..b3c51d975 Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky-15.png differ diff --git a/datafiles/data/BBMOD/Skies/Sky-5.png b/datafiles/data/BBMOD/Skies/Sky-5.png new file mode 100644 index 000000000..5d79b054b Binary files /dev/null and b/datafiles/data/BBMOD/Skies/Sky-5.png differ diff --git a/datafiles/data/BBMOD/assimp-vc143-mt.dll b/datafiles/data/BBMOD/assimp-vc143-mt.dll new file mode 100644 index 000000000..e47fdda53 Binary files /dev/null and b/datafiles/data/BBMOD/assimp-vc143-mt.dll differ diff --git a/datafiles/data/BBMOD/libBBMOD.dylib b/datafiles/data/BBMOD/libBBMOD.dylib new file mode 100644 index 000000000..76f2426cb Binary files /dev/null and b/datafiles/data/BBMOD/libBBMOD.dylib differ diff --git a/datafiles/data/BBMOD/libassimp.5.dylib b/datafiles/data/BBMOD/libassimp.5.dylib new file mode 100644 index 000000000..cb98ce307 Binary files /dev/null and b/datafiles/data/BBMOD/libassimp.5.dylib differ diff --git a/objects/_p_dialog/Create_0.gml b/objects/_p_dialog/Create_0.gml index e07bfd881..079ed6d48 100644 --- a/objects/_p_dialog/Create_0.gml +++ b/objects/_p_dialog/Create_0.gml @@ -172,9 +172,12 @@ if(destroy_on_click_out && mouse_press(mb_any) && !point_in_rectangle(mouse_mx, mouse_my, x0, y0, x1, y1)) { instance_destroy(self); + onDestroy(); DIALOG_CLICK = false; } } + + function onDestroy() {} #endregion #region children diff --git a/objects/o_dialog_panel/Create_0.gml b/objects/o_dialog_panel/Create_0.gml index abe81ae3a..654d393fd 100644 --- a/objects/o_dialog_panel/Create_0.gml +++ b/objects/o_dialog_panel/Create_0.gml @@ -56,4 +56,9 @@ event_inherited(); content.onResize(); } } + + function onDestroy() { + if(!content) return; + content.onClose(); + } #endregion \ No newline at end of file diff --git a/objects/o_dialog_panel/Draw_64.gml b/objects/o_dialog_panel/Draw_64.gml index 5543d7ba8..c903e33a4 100644 --- a/objects/o_dialog_panel/Draw_64.gml +++ b/objects/o_dialog_panel/Draw_64.gml @@ -47,8 +47,10 @@ if !ready exit; draw_text_cut(dialog_x + ui(32), dialog_y + ui(8), content.title, dialog_w - ui(32 + 32)); if(instanceof(content) != "Panel_Menu") - if(buttonInstant(THEME.button_hide, dialog_x + dialog_w - ui(28), dialog_y + ui(8), ui(20), ui(20), mouse_ui, sFOCUS, sHOVER, "", THEME.window_exit) == 2) + if(buttonInstant(THEME.button_hide, dialog_x + dialog_w - ui(28), dialog_y + ui(8), ui(20), ui(20), mouse_ui, sFOCUS, sHOVER, "", THEME.window_exit) == 2) { + onDestroy(); instance_destroy(); + } } var bx = content.showHeader? dialog_x + ui(8) : dialog_x + ui(24); diff --git a/objects/o_main/Create_0.gml b/objects/o_main/Create_0.gml index 88d8fe657..ecb421684 100644 --- a/objects/o_main/Create_0.gml +++ b/objects/o_main/Create_0.gml @@ -84,10 +84,13 @@ HOTKEY_MOD = 0; #endregion -#region gif reader - globalvar GIF_READER; +#region Loader + globalvar GIF_READER, _BBMOD_DLL; + GIF_READER = ds_list_create(); gif_complete_st = ds_stack_create(); + + _BBMOD_DLL = new BBMOD_DLL(); #endregion #region tunnel @@ -97,6 +100,23 @@ TUNNELS_OUT = ds_map_create(); #endregion +#region add on callback + globalvar ANIMATION_PRE, ANIMATION_POST; + + ANIMATION_PRE = []; + ANIMATION_POST = []; + + function __addon_preAnim() { + for( var i = 0; i < array_length(ANIMATION_PRE); i++ ) + ANIMATION_PRE[i](); + } + + function __addon_postAnim() { + for( var i = 0; i < array_length(ANIMATION_POST); i++ ) + ANIMATION_POST[i](); + } +#endregion + #region file drop if(OS == os_windows) { file_dropper_init(); diff --git a/objects/o_main/Step_1.gml b/objects/o_main/Step_1.gml index 7c24b2e8f..3c788c4ec 100644 --- a/objects/o_main/Step_1.gml +++ b/objects/o_main/Step_1.gml @@ -81,8 +81,11 @@ } if(ANIMATOR.is_playing || ANIMATOR.rendering) { - if(ANIMATOR.frame_progress) + if(ANIMATOR.frame_progress) { + __addon_preAnim(); Render(); + __addon_postAnim(); + } ANIMATOR.frame_progress = false; } else { if(UPDATE & RENDER_TYPE.full) diff --git a/scripts/BBMOD_AABBCollider/BBMOD_AABBCollider.gml b/scripts/BBMOD_AABBCollider/BBMOD_AABBCollider.gml new file mode 100644 index 000000000..6c2583833 --- /dev/null +++ b/scripts/BBMOD_AABBCollider/BBMOD_AABBCollider.gml @@ -0,0 +1,296 @@ +/// @func BBMOD_AABBCollider([_position[, _size]]) +/// +/// @extends BBMOD_Collider +/// +/// @desc An axis-aligned bounding box (AABB) collider. +/// +/// @param {Struct.BBMOD_Vec3} [_position] The position (center) of the AABB. +/// Defaults to `(0, 0, 0)`. +/// @param {Struct.BBMOD_Vec3} [_size] The size of the AABB on each +/// axis in both directions (e.g. `new BBMOD_Vec3(2)` would make a 4x4x4 box). +/// Defaults to `(0.5, 0.5, 0.5)`. +/// +/// @see BBMOD_FrustumCollider +/// @see BBMOD_PlaneCollider +/// @see BBMOD_SphereCollider +function BBMOD_AABBCollider( + _position=new BBMOD_Vec3(), + _size=new BBMOD_Vec3(0.5) +) : BBMOD_Collider() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_Vec3} The center position of the AABB. + Position = _position; + + /// @var {Struct.BBMOD_Vec3} The size of the AABB on each axis in both + /// directions (e.g. `new BBMOD_Vec3(2)` would be a 4x4x4 box). + Size = _size; + + /// @func FromMinMax(_min, _max) + /// + /// @desc Initializes the AABB using its minimum and maximum coordinates. + /// + /// @param {Struct.BBMOD_Vec3} _min The minimum coordinate of the AABB. + /// @param {Struct.BBMOD_Vec3} _max The maximum coordinate of the AABB. + /// + /// @return {Struct.BBMOD_AABBCollider} Returns `self`. + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L37 + static FromMinMax = function (_min, _max) { + gml_pragma("forceinline"); + Position = _min.Add(_max).Scale(0.5); + Size = _max.Sub(_min).Scale(0.5); + return self; + }; + + /// @func GetMin() + /// + /// @desc Retrieves the minimum coordinate of the AABB. + /// + /// @return {Struct.BBMOD_Vec3} The minimum coordinate. + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L24 + static GetMin = function () { + gml_pragma("forceinline"); + var _p1 = Position.Add(Size); + var _p2 = Position.Sub(Size); + return _p1.Minimize(_p2); + }; + + /// @func GetMax() + /// + /// @desc Retrieves the maximum coordinate of the AABB. + /// + /// @return {Struct.BBMOD_Vec3} The maximum coordinate. + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L30 + static GetMax = function () { + gml_pragma("forceinline"); + var _p1 = Position.Add(Size); + var _p2 = Position.Sub(Size); + return _p1.Maximize(_p2); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L149 + static GetClosestPoint = function (_point) { + gml_pragma("forceinline"); + return _point.Clamp(GetMin(), GetMax()); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L340 + static TestAABB = function (_aabb) { + gml_pragma("forceinline"); + var _aMin = GetMin(); + var _aMax = GetMax(); + var _bMin = _aabb.GetMin(); + var _bMax = _aabb.GetMax(); + return ((_aMin.X <= _bMax.X && _aMax.X >= _bMin.X) + && (_aMin.Y <= _bMax.Y && _aMax.Y >= _bMin.Y) + && (_aMin.Z <= _bMax.Z && _aMax.Z >= _bMin.Z)); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L482 + static TestPlane = function (_plane) { + gml_pragma("forceinline"); + var _pLen = (Size.X * abs(_plane.Normal.X) + + Size.Y * abs(_plane.Normal.Y) + + Size.Z * abs(_plane.Normal.Z)); + var _dist = _plane.Normal.Dot(Position) - _plane.Distance; + return (abs(_dist) <= _pLen); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L108 + static TestPoint = function (_point) { + gml_pragma("forceinline"); + var _min = GetMin(); + var _max = GetMax(); + if (_point.X < _min.X || _point.Y < _min.Y || _point.Z < _min.Z) + { + return false; + } + if (_point.X > _max.X || _point.Y > _max.Y || _point.Z > _max.Z) + { + return false; + } + return true; + }; + + static TestSphere = function (_sphere) { + gml_pragma("forceinline"); + return _sphere.TestAABB(self); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L707 + static Raycast = function (_ray, _result=undefined) { + if (_result != undefined) + { + _result.Reset(); + } + + var _min = GetMin(); + var _max = GetMax(); + + var _t1 = (_min.X - _ray.Origin.X) + / (bbmod_cmp(_ray.Direction.X, 0.0) ? 0.00001 : _ray.Direction.X); + var _t2 = (_max.X - _ray.Origin.X) + / (bbmod_cmp(_ray.Direction.X, 0.0) ? 0.00001 : _ray.Direction.X); + var _t3 = (_min.Y - _ray.Origin.Y) + / (bbmod_cmp(_ray.Direction.Y, 0.0) ? 0.00001 : _ray.Direction.Y); + var _t4 = (_max.Y - _ray.Origin.Y) + / (bbmod_cmp(_ray.Direction.Y, 0.0) ? 0.00001 : _ray.Direction.Y); + var _t5 = (_min.Z - _ray.Origin.Z) + / (bbmod_cmp(_ray.Direction.Z, 0.0) ? 0.00001 : _ray.Direction.Z); + var _t6 = (_max.Z - _ray.Origin.Z) + / (bbmod_cmp(_ray.Direction.Z, 0.0) ? 0.00001 : _ray.Direction.Z); + + var _tmin = max(max(min(_t1, _t2), min(_t3, _t4)), min(_t5, _t6)); + var _tmax = min(min(max(_t1, _t2), max(_t3, _t4)), max(_t5, _t6)); + + if (_tmax < 0.0) + { + return false; + } + + if (_tmin > _tmax) + { + return false; + } + + if (_result != undefined) + { + var _tResult = (_tmin < 0.0) ? _tmax : _tmin; + + _result.Distance = _tResult; + _result.Point = _ray.Origin.Add(_ray.Direction.Scale(_tResult)); + + for (var i = 0; i < 6; ++i) + { + var _ti; + + switch (i) + { + case 0: + _ti = _t1; + break; + + case 1: + _ti = _t2; + break; + + case 2: + _ti = _t3; + break; + + case 3: + _ti = _t4; + break; + + case 4: + _ti = _t5; + break; + + case 5: + _ti = _t6; + break; + } + + if (bbmod_cmp(_tResult, _ti)) + { + switch (i) + { + case 0: + _result.Normal = new BBMOD_Vec3(-1.0, 0.0, 0.0); + break; + + case 1: + _result.Normal = new BBMOD_Vec3(1.0, 0.0, 0.0); + break; + + case 2: + _result.Normal = new BBMOD_Vec3(0.0, -1.0, 0.0); + break; + + case 3: + _result.Normal = new BBMOD_Vec3(0.0, 1.0, 0.0); + break; + + case 4: + _result.Normal = new BBMOD_Vec3(0.0, 0.0, -1.0); + break; + + case 5: + _result.Normal = new BBMOD_Vec3(0.0, 0.0, 1.0); + break; + } + } + } + } + + return true; + }; + + static DrawDebug = function (_color=c_white, _alpha=1.0) { + var _vbuffer = global.__bbmodVBufferDebug; + + var _x1 = Position.X - Size.X; + var _x2 = Position.X + Size.X; + var _y1 = Position.Y - Size.Y; + var _y2 = Position.Y + Size.Y; + var _z1 = Position.Z - Size.Z; + var _z2 = Position.Z + Size.Z; + + vertex_begin(_vbuffer, BBMOD_VFORMAT_DEBUG.Raw); + + // Bottom + // 1--2 + // | | + // 4--3 + vertex_position_3d(_vbuffer, _x1, _y1, _z1); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x2, _y1, _z1); vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x2, _y1, _z1); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x2, _y2, _z1); vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x2, _y2, _z1); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x1, _y2, _z1); vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x1, _y2, _z1); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x1, _y1, _z1); vertex_color(_vbuffer, _color, _alpha); + + // Top + // 1--2 + // | | + // 4--3 + vertex_position_3d(_vbuffer, _x1, _y1, _z2); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x2, _y1, _z2); vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x2, _y1, _z2); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x2, _y2, _z2); vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x2, _y2, _z2); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x1, _y2, _z2); vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x1, _y2, _z2); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x1, _y1, _z2); vertex_color(_vbuffer, _color, _alpha); + + // Sides + // 1--2 + // | | + // 4--3 + vertex_position_3d(_vbuffer, _x1, _y1, _z1); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x1, _y1, _z2); vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x2, _y1, _z1); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x2, _y1, _z2); vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x2, _y2, _z1); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x2, _y2, _z2); vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x1, _y2, _z1); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _x1, _y2, _z2); vertex_color(_vbuffer, _color, _alpha); + + vertex_end(_vbuffer); + + vertex_submit(_vbuffer, pr_linelist, -1); + + return self; + }; +} diff --git a/scripts/BBMOD_AABBCollider/BBMOD_AABBCollider.yy b/scripts/BBMOD_AABBCollider/BBMOD_AABBCollider.yy new file mode 100644 index 000000000..eab5c27e7 --- /dev/null +++ b/scripts/BBMOD_AABBCollider/BBMOD_AABBCollider.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AABBCollider", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Raycasting", + "path": "folders/_Extensions/BBMOD/Raycasting.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AABBEmissionModule/BBMOD_AABBEmissionModule.gml b/scripts/BBMOD_AABBEmissionModule/BBMOD_AABBEmissionModule.gml new file mode 100644 index 000000000..bb9727962 --- /dev/null +++ b/scripts/BBMOD_AABBEmissionModule/BBMOD_AABBEmissionModule.gml @@ -0,0 +1,46 @@ +/// @func BBMOD_AABBEmissionModule([_min[, _max[, _inside]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that positions spawned particles into an AABB shape. +/// +/// @param {Struct.BBMOD_Vec3} [_min] The minimum coordinate of the AABB. +/// Defaults to `(-0.5, -0.5, -0.5)`. +/// @param {Struct.BBMOD_Vec3} [_max] The maximum coordinate of the AABB. +/// Defaults to `(0.5, 0.5, 0.5)`. +/// @param {Bool} [_inside] If `true` then the particles can be spawned inside +/// of the AABB. Defaults to `true`. +/// +/// @see BBMOD_EParticle.PositionX +/// @see BBMOD_EParticle.PositionY +/// @see BBMOD_EParticle.PositionZ +function BBMOD_AABBEmissionModule( + _min=new BBMOD_Vec3(-0.5), + _max=new BBMOD_Vec3(0.5), + _inside=true +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The minimum coordinate of the AABB. Default value is + /// to `(-0.5, -0.5, -0.5)`. + Min = _min; + + /// @var {Real} The maximum coordinate of the AABB. Default value is + /// to `(0.5, 0.5, 0.5)`. + Max = _max; + + /// @var {Bool} If `true` then the particles can be spawned inside of + /// the AABB. Default value is `true`. + Inside = _inside; + + static on_particle_start = function (_emitter, _particleIndex) { + var _side = choose(0, 1, 2); + _emitter.Particles[# BBMOD_EParticle.PositionX, _particleIndex] += + (Inside || _side != 0) ? random_range(Min.X, Max.X) : choose(Min.X, Max.X); + _emitter.Particles[# BBMOD_EParticle.PositionY, _particleIndex] += + (Inside || _side != 1) ? random_range(Min.Y, Max.Y) : choose(Min.Y, Max.Y); + _emitter.Particles[# BBMOD_EParticle.PositionZ, _particleIndex] += + (Inside || _side != 2) ? random_range(Min.Z, Max.Z) : choose(Min.Z, Max.Z); + }; +} diff --git a/scripts/BBMOD_AABBEmissionModule/BBMOD_AABBEmissionModule.yy b/scripts/BBMOD_AABBEmissionModule/BBMOD_AABBEmissionModule.yy new file mode 100644 index 000000000..9aee66a32 --- /dev/null +++ b/scripts/BBMOD_AABBEmissionModule/BBMOD_AABBEmissionModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AABBEmissionModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Shape", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Emission/Shape.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AddRealOnCollisionModule/BBMOD_AddRealOnCollisionModule.yy b/scripts/BBMOD_AddRealOnCollisionModule/BBMOD_AddRealOnCollisionModule.yy new file mode 100644 index 000000000..e6ab37914 --- /dev/null +++ b/scripts/BBMOD_AddRealOnCollisionModule/BBMOD_AddRealOnCollisionModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AddRealOnCollisionModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "AddPropertyOnCollision", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOnCollision.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AddRealOnCollisionModule/bbmod_addrealoncollisionmodule.gml b/scripts/BBMOD_AddRealOnCollisionModule/bbmod_addrealoncollisionmodule.gml new file mode 100644 index 000000000..43cf567ce --- /dev/null +++ b/scripts/BBMOD_AddRealOnCollisionModule/bbmod_addrealoncollisionmodule.gml @@ -0,0 +1,57 @@ +/// @func BBMOD_AddRealOnCollisionModule([_property[, _change]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that adds a value to particles' property +/// when they have a collision. +/// +/// @param {Real} [_property] The property to add the value to. Use values from +/// {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Real} [_change] The value to add to particles' health. Defaults +/// to 1.0. +/// +/// @see BBMOD_EParticle.HasCollided +function BBMOD_AddRealOnCollisionModule(_property=undefined, _change=1.0) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The property to add the value to. Use values from + /// {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Real} The value to add on collision. Default value is 1.0. + Change = _change; + + static on_update = function (_emitter, _deltaTime) { + if (Property != undefined) + { + var _y2 = _emitter.ParticlesAlive - 1; + if (_y2 >= 0) + { + var _particles = _emitter.Particles; + var _gridCompute = _emitter.GridCompute; + + ds_grid_set_region( + _gridCompute, + 0, 0, + 0, _y2, + Change); + + ds_grid_multiply_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, _y2, + 0, 0); + + ds_grid_add_grid_region( + _particles, + _gridCompute, + 0, 0, + 0, _y2, + Property, 0); + } + } + }; +} diff --git a/scripts/BBMOD_AddRealOverTimeModule/BBMOD_AddRealOverTimeModule.yy b/scripts/BBMOD_AddRealOverTimeModule/BBMOD_AddRealOverTimeModule.yy new file mode 100644 index 000000000..5d022d9dc --- /dev/null +++ b/scripts/BBMOD_AddRealOverTimeModule/BBMOD_AddRealOverTimeModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AddRealOverTimeModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "AddPropertyOverTime", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOverTime.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AddRealOverTimeModule/bbmod_addrealovertimemodule.gml b/scripts/BBMOD_AddRealOverTimeModule/bbmod_addrealovertimemodule.gml new file mode 100644 index 000000000..f415d919a --- /dev/null +++ b/scripts/BBMOD_AddRealOverTimeModule/bbmod_addrealovertimemodule.gml @@ -0,0 +1,51 @@ +/// @func BBMOD_AddRealOverTimeModule([_property[, _change[, _period]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that adds a value to particles' property +/// over time. +/// +/// @param {Real} [_property] The property to add the value to. Use values from +/// {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Real} [_change] The value added over specified period. Defaults to +/// 1.0. +/// @param {Real} [_period] How long in seconds it takes to add the value to the +/// property. Defaults to 1.0. +/// +/// @see BBMOD_EParticle.HealthLeft +function BBMOD_AddRealOverTimeModule( + _property=undefined, + _change=1.0, + _period=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The property to add the value to. Use values from + /// {@link BBMOD_EParticle} Default value is `undefined`. + Property = _property; + + /// @var {Real} The value added over {@link BBMOD_AddRealOverTimeModule.Period}. + /// Default value is 1.0. + Change = _change; + + /// @var {Real} How long in seconds it takes to add the value to the + /// property. Defaults to 1.0. + Period = _period; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _y2 = _emitter.ParticlesAlive - 1; + if (_y2 >= 0) + { + ds_grid_add_region( + _emitter.Particles, + _property, 0, + _property, _y2, + Change * ((_deltaTime * 0.000001) / Period)); + } + } + }; +} diff --git a/scripts/BBMOD_AddVec2OnCollisionModule/BBMOD_AddVec2OnCollisionModule.yy b/scripts/BBMOD_AddVec2OnCollisionModule/BBMOD_AddVec2OnCollisionModule.yy new file mode 100644 index 000000000..e037b1e84 --- /dev/null +++ b/scripts/BBMOD_AddVec2OnCollisionModule/BBMOD_AddVec2OnCollisionModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AddVec2OnCollisionModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "AddPropertyOnCollision", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOnCollision.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AddVec2OnCollisionModule/bbmod_addvec2oncollisionmodule.gml b/scripts/BBMOD_AddVec2OnCollisionModule/bbmod_addvec2oncollisionmodule.gml new file mode 100644 index 000000000..3f5f966be --- /dev/null +++ b/scripts/BBMOD_AddVec2OnCollisionModule/bbmod_addvec2oncollisionmodule.gml @@ -0,0 +1,74 @@ +/// @func BBMOD_AddVec2OnCollisionModule([_property[, _change]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that adds a value to two consecutive +/// particle properties it has a collision. +/// +/// @param {Real} [_property] The first of the two consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec2} [_change] The value to add to particles' health. +/// Defaults to `(1.0, 1.0)`. +/// +/// @see BBMOD_EParticle.HasCollided +function BBMOD_AddVec2OnCollisionModule( + _property=undefined, + _change=new BBMOD_Vec2(1.0) +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the two consecutive properties. Use values from + /// {@link BBMOD_EParticle}.Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec2} The value to add on collision. Default value is + /// `(1.0, 1.0)`. + Change = _change; + + static on_update = function (_emitter, _deltaTime) { + if (Property != undefined) + { + var _y2 = _emitter.ParticlesAlive - 1; + if (_y2 >= 0) + { + var _particles = _emitter.Particles; + var _gridCompute = _emitter.GridCompute; + var _change = Change; + + ds_grid_set_region( + _gridCompute, + 0, 0, + 0, _y2, + _change.X); + + ds_grid_set_region( + _gridCompute, + 1, 0, + 1, _y2, + _change.Y); + + ds_grid_multiply_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, _y2, + 0, 0); + + ds_grid_multiply_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, _y2, + 1, 0); + + ds_grid_add_grid_region( + _particles, + _gridCompute, + 0, 0, + 1, _y2, + Property, 0); + } + } + }; +} diff --git a/scripts/BBMOD_AddVec2OverTimeModule/BBMOD_AddVec2OverTimeModule.yy b/scripts/BBMOD_AddVec2OverTimeModule/BBMOD_AddVec2OverTimeModule.yy new file mode 100644 index 000000000..94a284b03 --- /dev/null +++ b/scripts/BBMOD_AddVec2OverTimeModule/BBMOD_AddVec2OverTimeModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AddVec2OverTimeModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "AddPropertyOverTime", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOverTime.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AddVec2OverTimeModule/bbmod_addvec2overtimemodule.gml b/scripts/BBMOD_AddVec2OverTimeModule/bbmod_addvec2overtimemodule.gml new file mode 100644 index 000000000..12bfbdeb3 --- /dev/null +++ b/scripts/BBMOD_AddVec2OverTimeModule/bbmod_addvec2overtimemodule.gml @@ -0,0 +1,53 @@ +/// @func BBMOD_AddVec2OverTimeModule([_property[, _change[, _period]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that adds a value to two consecutive +/// particle properties over time. +/// +/// @param {Real} [_property] The first of the two consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec2} [_change] The value added over specified period. +/// Defaults to `(1.0, 1.0)`. +/// @param {Real} [_period] How long in seconds it takes to add the value to the +/// properties. Defaults to 1.0. +/// +/// @see BBMOD_EParticle.HealthLeft +function BBMOD_AddVec2OverTimeModule( + _property=undefined, + _change=new BBMOD_Vec2(1.0), + _period=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the two consecutive properties. Use values from + /// {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec2} The value added over + /// {@link BBMOD_AddVec2OverTimeModule.Period}. Default value is + /// `(1.0, 1.0)`. + Change = _change; + + /// @var {Real} How long in seconds it takes to add the value to the + /// properties. Defaults to 1.0. + Period = _period; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _y2 = _emitter.ParticlesAlive - 1; + if (_y2 >= 0) + { + var _factor = ((_deltaTime * 0.000001) / Period); + var _change = Change; + var _changeX = _change.X * _factor; + var _changeY = _change.Y * _factor; + ds_grid_add_region(_emitter.Particles, _property, 0, _property, _y2, _changeX); + ds_grid_add_region(_emitter.Particles, _property + 1, 0, _property + 1, _y2, _changeY); + } + } + }; +} diff --git a/scripts/BBMOD_AddVec3OnCollisionModule/BBMOD_AddVec3OnCollisionModule.yy b/scripts/BBMOD_AddVec3OnCollisionModule/BBMOD_AddVec3OnCollisionModule.yy new file mode 100644 index 000000000..871889d44 --- /dev/null +++ b/scripts/BBMOD_AddVec3OnCollisionModule/BBMOD_AddVec3OnCollisionModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AddVec3OnCollisionModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "AddPropertyOnCollision", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOnCollision.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AddVec3OnCollisionModule/bbmod_addvec3oncollisionmodule.gml b/scripts/BBMOD_AddVec3OnCollisionModule/bbmod_addvec3oncollisionmodule.gml new file mode 100644 index 000000000..dfec2b82c --- /dev/null +++ b/scripts/BBMOD_AddVec3OnCollisionModule/bbmod_addvec3oncollisionmodule.gml @@ -0,0 +1,87 @@ +/// @func BBMOD_AddVec3OnCollisionModule([_property[, _change]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that adds a value to three consecutive +/// particle properties it has a collision. +/// +/// @param {Real} [_property] The first of the three consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec3} [_change] The value to add to particles' health. Defaults +/// to `(1.0, 1.0, 1.0)`. +/// +/// @see BBMOD_EParticle.HasCollided +function BBMOD_AddVec3OnCollisionModule( + _property=undefined, + _change=new BBMOD_Vec3(1.0) +): BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the three consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec3} The value to add on collision. Default value is + /// `(1.0, 1.0, 1.0)`. + Change = _change; + + static on_update = function (_emitter, _deltaTime) { + if (Property != undefined) + { + var _y2 = _emitter.ParticlesAlive - 1; + if (_y2 >= 0) + { + var _particles = _emitter.Particles; + var _gridCompute = _emitter.GridCompute; + var _change = Change; + + ds_grid_set_region( + _gridCompute, + 0, 0, + 0, _y2, + _change.X); + + ds_grid_set_region( + _gridCompute, + 1, 0, + 1, _y2, + _change.Y); + + ds_grid_set_region( + _gridCompute, + 2, 0, + 2, _y2, + _change.Z); + + ds_grid_multiply_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, _y2, + 0, 0); + + ds_grid_multiply_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, _y2, + 1, 0); + + ds_grid_multiply_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, _y2, + 2, 0); + + ds_grid_add_grid_region( + _particles, + _gridCompute, + 0, 0, + 2, _y2, + Property, 0); + } + } + }; +} diff --git a/scripts/BBMOD_AddVec3OverTimeModule/BBMOD_AddVec3OverTimeModule.yy b/scripts/BBMOD_AddVec3OverTimeModule/BBMOD_AddVec3OverTimeModule.yy new file mode 100644 index 000000000..f4f56478e --- /dev/null +++ b/scripts/BBMOD_AddVec3OverTimeModule/BBMOD_AddVec3OverTimeModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AddVec3OverTimeModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "AddPropertyOverTime", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOverTime.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AddVec3OverTimeModule/bbmod_addvec3overtimemodule.gml b/scripts/BBMOD_AddVec3OverTimeModule/bbmod_addvec3overtimemodule.gml new file mode 100644 index 000000000..f8366d2f3 --- /dev/null +++ b/scripts/BBMOD_AddVec3OverTimeModule/bbmod_addvec3overtimemodule.gml @@ -0,0 +1,55 @@ +/// @func BBMOD_AddVec3OverTimeModule([_property[, _change[, _period]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that adds a value to three consecutive +/// particle properties over time. +/// +/// @param {Real} [_property] The first of the three consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec3} [_change] The value added over specified period. +/// Defaults to `(1.0, 1.0, 1.0)`. +/// @param {Real} [_period] How long in seconds it takes to add the value to the +/// properties. Defaults to 1.0. +/// +/// @see BBMOD_EParticle.HealthLeft +function BBMOD_AddVec3OverTimeModule( + _property=undefined, + _change=new BBMOD_Vec3(1.0), + _period=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the three consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec3} The value added over + /// {@link BBMOD_AddVec3OverTimeModule.Period}. Default value is + /// `(1.0, 1.0, 1.0)`. + Change = _change; + + /// @var {Real} How long in seconds it takes to add the value to the + /// properties. Defaults to 1.0. + Period = _period; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _y2 = _emitter.ParticlesAlive - 1; + if (_y2 >= 0) + { + var _factor = ((_deltaTime * 0.000001) / Period); + var _change = Change; + var _changeX = _change.X * _factor; + var _changeY = _change.Y * _factor; + var _changeZ = _change.Z * _factor; + ds_grid_add_region(_emitter.Particles, _property, 0, _property, _y2, _changeX); + ds_grid_add_region(_emitter.Particles, _property + 1, 0, _property + 1, _y2, _changeY); + ds_grid_add_region(_emitter.Particles, _property + 2, 0, _property + 2, _y2, _changeZ); + } + } + }; +} diff --git a/scripts/BBMOD_AddVec4OnCollisionModule/BBMOD_AddVec4OnCollisionModule.yy b/scripts/BBMOD_AddVec4OnCollisionModule/BBMOD_AddVec4OnCollisionModule.yy new file mode 100644 index 000000000..159737263 --- /dev/null +++ b/scripts/BBMOD_AddVec4OnCollisionModule/BBMOD_AddVec4OnCollisionModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AddVec4OnCollisionModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "AddPropertyOnCollision", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOnCollision.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AddVec4OnCollisionModule/bbmod_addvec4oncollisionmodule.gml b/scripts/BBMOD_AddVec4OnCollisionModule/bbmod_addvec4oncollisionmodule.gml new file mode 100644 index 000000000..a1fc0169b --- /dev/null +++ b/scripts/BBMOD_AddVec4OnCollisionModule/bbmod_addvec4oncollisionmodule.gml @@ -0,0 +1,100 @@ +/// @func BBMOD_AddVec4OnCollisionModule([_property[, _change]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that adds a value to four consecutive +/// particle properties it has a collision. +/// +/// @param {Real} [_property] The first of the four consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec4} [_change] The value to add to particles' health. Defaults +/// to `(1.0, 1.0, 1.0, 1.0)`. +/// +/// @see BBMOD_EParticle.HasCollided +function BBMOD_AddVec4OnCollisionModule( + _property=undefined, + _change=new BBMOD_Vec4(1.0) +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec4} The value to add on collision. Default value is + /// `(1.0, 1.0, 1.0, 1.0)`. + Change = _change; + + static on_update = function (_emitter, _deltaTime) { + if (Property != undefined) + { + var _y2 = _emitter.ParticlesAlive - 1; + if (_y2 >= 0) + { + var _particles = _emitter.Particles; + var _gridCompute = _emitter.GridCompute; + var _change = Change; + + ds_grid_set_region( + _gridCompute, + 0, 0, + 0, _y2, + _change.X); + + ds_grid_set_region( + _gridCompute, + 1, 0, + 1, _y2, + _change.Y); + + ds_grid_set_region( + _gridCompute, + 2, 0, + 2, _y2, + _change.Z); + + ds_grid_set_region( + _gridCompute, + 3, 0, + 3, _y2, + _change.W); + + ds_grid_multiply_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, _y2, + 0, 0); + + ds_grid_multiply_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, _y2, + 1, 0); + + ds_grid_multiply_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, _y2, + 2, 0); + + ds_grid_multiply_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, _y2, + 3, 0); + + ds_grid_add_grid_region( + _particles, + _gridCompute, + 0, 0, + 3, _y2, + Property, 0); + } + } + }; +} diff --git a/scripts/BBMOD_AddVec4OverTimeModule/BBMOD_AddVec4OverTimeModule.yy b/scripts/BBMOD_AddVec4OverTimeModule/BBMOD_AddVec4OverTimeModule.yy new file mode 100644 index 000000000..365dcbd9d --- /dev/null +++ b/scripts/BBMOD_AddVec4OverTimeModule/BBMOD_AddVec4OverTimeModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AddVec4OverTimeModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "AddPropertyOverTime", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/AddPropertyOverTime.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AddVec4OverTimeModule/bbmod_addvec4overtimemodule.gml b/scripts/BBMOD_AddVec4OverTimeModule/bbmod_addvec4overtimemodule.gml new file mode 100644 index 000000000..7d0966085 --- /dev/null +++ b/scripts/BBMOD_AddVec4OverTimeModule/bbmod_addvec4overtimemodule.gml @@ -0,0 +1,57 @@ +/// @func BBMOD_AddVec4OverTimeModule([_property[, _change[, _period]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that adds a value to four consecutive +/// particle properties over time. +/// +/// @param {Real} [_property] The first of the four consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec4} [_change] The value added over specified period. +/// Defaults to `(1.0, 1.0, 1.0, 1.0)`. +/// @param {Real} [_period] How long in seconds it takes to add the value to the +/// properties. Defaults to 1.0. +/// +/// @see BBMOD_EParticle.HealthLeft +function BBMOD_AddVec4OverTimeModule( + _property=undefined, + _change=new BBMOD_Vec4(1.0), + _period=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec4} The value added over + /// {@link BBMOD_AddVec4OverTimeModule.Period}. Default value is + /// `(1.0, 1.0, 1.0, 1.0)`. + Change = _change; + + /// @var {Real} How long in seconds it takes to add the value to the + /// properties. Defaults to 1.0. + Period = _period; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _y2 = _emitter.ParticlesAlive - 1; + if (_y2 >= 0) + { + var _factor = ((_deltaTime * 0.000001) / Period); + var _change = Change; + var _changeX = _change.X * _factor; + var _changeY = _change.Y * _factor; + var _changeZ = _change.Z * _factor; + var _changeW = _change.W * _factor; + ds_grid_add_region(_emitter.Particles, _property, 0, _property, _y2, _changeX); + ds_grid_add_region(_emitter.Particles, _property + 1, 0, _property + 1, _y2, _changeY); + ds_grid_add_region(_emitter.Particles, _property + 2, 0, _property + 2, _y2, _changeZ); + ds_grid_add_region(_emitter.Particles, _property + 3, 0, _property + 3, _y2, _changeW); + } + } + }; +} diff --git a/scripts/BBMOD_Animation/BBMOD_Animation.gml b/scripts/BBMOD_Animation/BBMOD_Animation.gml new file mode 100644 index 000000000..479d20d4a --- /dev/null +++ b/scripts/BBMOD_Animation/BBMOD_Animation.gml @@ -0,0 +1,508 @@ +#macro BBMOD_BONE_SPACE_PARENT (1 << 0) +#macro BBMOD_BONE_SPACE_WORLD (1 << 1) +#macro BBMOD_BONE_SPACE_BONE (1 << 2) + +/// @func BBMOD_Animation([_file[, _sha1]]) +/// +/// @extends BBMOD_Resource +/// +/// @desc An animation which can be played using {@link BBMOD_AnimationPlayer}. +/// +/// @param {String} [_file] A "*.bbanim" animation file to load. If not +/// specified, then an empty animation is created. +/// @param {String} [_sha1] Expected SHA1 of the file. If the actual one does +/// not match with this, then the model will not be loaded. +/// +/// @example +/// Following code loads an animation from a file `Walk.bbanim`: +/// +/// ```gml +/// try +/// { +/// animWalk = new BBMOD_Animation("Walk.bbanim"); +/// } +/// catch (_exception) +/// { +/// // The animation failed to load! +/// } +/// ``` +/// +/// You can also load animations from buffers like so: +/// +/// ```gml +/// var _buffer = buffer_load("Walk.anim"); +/// try +/// { +/// animWalk = new BBMOD_Animation().from_buffer(_buffer); +/// } +/// catch (_exception) +/// { +/// // Failed to load an animation from the buffer! +/// } +/// buffer_delete(_buffer); +/// ``` +/// +/// @throws {BBMOD_Exception} When the animation fails to load. +function BBMOD_Animation(_file=undefined, _sha1=undefined) + : BBMOD_Resource() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Bool} If `false` then the animation has not been loaded yet. + /// @readonly + IsLoaded = false; + + /// @var {Real} The major version of the animation file. + VersionMajor = BBMOD_VERSION_MAJOR; + + /// @var {Real} The minor version of the animation file. + VersionMinor = BBMOD_VERSION_MINOR; + + /// @var {Real} The transformation spaces included in the animation file. + /// @private + __spaces = 0; + + /// @var {Real} The duration of the animation (in tics). + /// @readonly + Duration = 0; + + /// @var {Real} Number of animation tics per second. + /// @readonly + TicsPerSecond = 0; + + /// @var {Real} + /// @private + __modelNodeCount = 0; + + /// @var {Real} + /// @private + __modelBoneCount = 0; + + /// @var {Array>} + /// @private + __framesParent = []; + + /// @var {Array>} + /// @private + __framesWorld = []; + + /// @var {Array>} + /// @private + __framesBone = []; + + /// @var {Bool} + /// @private + __isTransition = false; + + /// @var {Real} Duration of transition into this animation (in seconds). + /// Must be a value greater or equal to 0! + TransitionIn = 0.1; + + /// @var {Real} Duration of transition out of this animation (in seconds). + /// Must be a value greater or equal to 0! + TransitionOut = 0; + + /// @var {Array} Custom animation events in form of `[frame, name, ...]`. + /// @private + __events = []; + + /// @func add_event(_frame, _name) + /// + /// @desc Adds a custom animation event. + /// + /// @param {Real} _frame The frame at which should be the event triggered. + /// @param {String} _name The name of the event. + /// + /// @return {Struct.BBMOD_Animation} Returns `self`. + /// + /// @example + /// ```gml + /// animWalk = new BBMOD_Animation("Data/Character_Walk.bbanim"); + /// animWalk.add_event(0, "Footstep") + /// .add_event(16, "Footstep"); + /// animationPlayer.on_event("Footstep", method(self, function () { + /// // Play footstep sound... + /// })); + /// ``` + static add_event = function (_frame, _name) { + gml_pragma("forceinline"); + array_push(__events, _frame, _name); + return self; + }; + + /// @func supports_attachments() + /// + /// @desc Checks whether the animation supports bone attachments. + /// + /// @return {Bool} Returns true if the animation supports bone attachments. + static supports_attachments = function () { + gml_pragma("forceinline"); + return ((__spaces & (BBMOD_BONE_SPACE_PARENT | BBMOD_BONE_SPACE_WORLD)) != 0); + }; + + /// @func supports_bone_transform() + /// + /// @desc Checks whether the animation supports bone transformation through + /// code. + /// + /// @return {Bool} Returns true if the animation supports bone + /// transformation through code. + static supports_bone_transform = function () { + gml_pragma("forceinline"); + return ((__spaces & BBMOD_BONE_SPACE_PARENT) != 0); + }; + + /// @func supports_transitions() + /// + /// @desc Checks whether the animation supports transitions. + /// + /// @return {Bool} Returns true if the animation supports transitions. + static supports_transitions = function () { + gml_pragma("forceinline"); + return ((__spaces & (BBMOD_BONE_SPACE_PARENT | BBMOD_BONE_SPACE_WORLD)) != 0); + }; + + /// @func get_animation_time(_timeInSeconds) + /// + /// @desc Calculates animation time from current time in seconds. + /// + /// @param {Real} _timeInSeconds The current time in seconds. + /// + /// @return {Real} The animation time. + static get_animation_time = function (_timeInSeconds) { + gml_pragma("forceinline"); + return round(_timeInSeconds * TicsPerSecond); + }; + + /// @func from_buffer(_buffer) + /// + /// @desc Loads animation data from a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to load the data from. + /// + /// @return {Struct.BBMOD_Animation} Returns `self`. + /// + /// @throws {BBMOD_Exception} If loading fails. + static from_buffer = function (_buffer) { + var _hasMinorVersion = false; + + var _type = buffer_read(_buffer, buffer_string); + if (_type == "bbanim") + { + } + else if (_type == "BBANIM") + { + _hasMinorVersion = true; + } + else + { + throw new BBMOD_Exception("Buffer does not contain a BBANIM!"); + } + + VersionMajor = buffer_read(_buffer, buffer_u8); + if (VersionMajor != BBMOD_VERSION_MAJOR) + { + throw new BBMOD_Exception( + "Invalid BBANIM major version " + string(VersionMajor) + "!"); + } + + if (_hasMinorVersion) + { + VersionMinor = buffer_read(_buffer, buffer_u8); + if (VersionMinor > BBMOD_VERSION_MINOR) + { + throw new BBMOD_Exception( + "Invalid BBANIM minor version " + string(VersionMinor) + "!"); + } + } + else + { + VersionMinor = 0; + } + + __spaces = buffer_read(_buffer, buffer_u8); + Duration = buffer_read(_buffer, buffer_f64); + TicsPerSecond = buffer_read(_buffer, buffer_f64); + + __modelNodeCount = buffer_read(_buffer, buffer_u32); + var _modelNodeSize = __modelNodeCount * 8; + __modelBoneCount = buffer_read(_buffer, buffer_u32); + var _modelBoneSize = __modelBoneCount * 8; + + __framesParent = (__spaces & BBMOD_BONE_SPACE_PARENT) ? [] : undefined; + __framesWorld = (__spaces & BBMOD_BONE_SPACE_WORLD) ? [] : undefined; + __framesBone = (__spaces & BBMOD_BONE_SPACE_BONE) ? [] : undefined; + + repeat (Duration) + { + if (__spaces & BBMOD_BONE_SPACE_PARENT) + { + array_push(__framesParent, + bbmod_array_from_buffer(_buffer, buffer_f32, _modelNodeSize)); + } + + if (__spaces & BBMOD_BONE_SPACE_WORLD) + { + array_push(__framesWorld, + bbmod_array_from_buffer(_buffer, buffer_f32, _modelNodeSize)); + } + + if (__spaces & BBMOD_BONE_SPACE_BONE) + { + array_push(__framesBone, + bbmod_array_from_buffer(_buffer, buffer_f32, _modelBoneSize)); + } + } + + if (VersionMinor >= 4) + { + var _eventCount = buffer_read(_buffer, buffer_u32); + repeat (_eventCount) + { + array_push(__events, buffer_read(_buffer, buffer_f64)); // Frame + array_push(__events, buffer_read(_buffer, buffer_string)); // Event name + } + } + + IsLoaded = true; + + return self; + }; + + /// @func to_buffer(_buffer) + /// + /// @desc Writes animation data to a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to write the data to. + /// + /// @return {Struct.BBMOD_Animation} Returns `self`. + static to_buffer = function (_buffer) { + buffer_write(_buffer, buffer_string, "BBANIM"); + buffer_write(_buffer, buffer_u8, VersionMajor); + buffer_write(_buffer, buffer_u8, VersionMinor); + + buffer_write(_buffer, buffer_u8, __spaces); + buffer_write(_buffer, buffer_f64, Duration); + buffer_write(_buffer, buffer_f64, TicsPerSecond); + + buffer_write(_buffer, buffer_u32, __modelNodeCount); + buffer_write(_buffer, buffer_u32, __modelBoneCount); + + var d = 0; + repeat (Duration) + { + if (__spaces & BBMOD_BONE_SPACE_PARENT) + { + bbmod_array_to_buffer(__framesParent[d], _buffer, buffer_f32); + } + + if (__spaces & BBMOD_BONE_SPACE_WORLD) + { + bbmod_array_to_buffer(__framesWorld[d], _buffer, buffer_f32); + } + + if (__spaces & BBMOD_BONE_SPACE_BONE) + { + bbmod_array_to_buffer(__framesBone[d], _buffer, buffer_f32); + } + + ++d; + } + + var _eventCount = array_length(__events) / 2; + buffer_write(_buffer, buffer_u32, _eventCount); + + var i = 0; + repeat (_eventCount) + { + buffer_write(_buffer, buffer_f64, __events[i]); + buffer_write(_buffer, buffer_string, __events[i + 1]); + i += 2; + } + + return self; + }; + + if (_file != undefined) + { + from_file(_file, _sha1); + } + + /// @func create_transition(_timeFrom, _animTo, _timeTo) + /// + /// @desc Creates a new animation transition. + /// + /// @param {Real} _timeFrom Animation time of this animation that we are + /// transitioning from. + /// @param {Struct.BBMOD_Animation} _animTo The animation to transition to. + /// @param {Real} _timeTo Animation time of the target animation. + /// + /// @return {Struct.BBMOD_Animation} The created transition or `undefined` + /// if the animations have different optimization levels or if they do not + /// support transitions. + static create_transition = function (_timeFrom, _animTo, _timeTo) { + if ((__spaces & (BBMOD_BONE_SPACE_PARENT | BBMOD_BONE_SPACE_WORLD)) == 0 + || __spaces != _animTo.__spaces) + { + return undefined; + } + + var _transition = new BBMOD_Animation(); + _transition.IsLoaded = true; + _transition.VersionMajor = VersionMajor; + _transition.VersionMinor = VersionMinor; + _transition.__spaces = (__spaces & BBMOD_BONE_SPACE_PARENT) + ? BBMOD_BONE_SPACE_PARENT + : BBMOD_BONE_SPACE_WORLD; + _transition.Duration = round((TransitionOut + _animTo.TransitionIn) + * TicsPerSecond); + _transition.TicsPerSecond = TicsPerSecond; + _transition.__isTransition = true; + + var _frameFrom, _frameTo, _framesDest; + + if (__spaces & BBMOD_BONE_SPACE_PARENT) + { + _frameFrom = __framesParent[_timeFrom]; + _frameTo = _animTo.__framesParent[_timeTo]; + _framesDest = _transition.__framesParent; + } + else + { + _frameFrom = __framesWorld[_timeFrom]; + _frameTo = _animTo.__framesWorld[_timeTo]; + _framesDest = _transition.__framesWorld; + } + + var _time = 0; + repeat (_transition.Duration) + { + var _frameSize = array_length(_frameFrom); + var _frame = array_create(_frameSize); + + var i = 0; + repeat (_frameSize / 8) + { + var _factor = _time / _transition.Duration; + + // First dual quaternion + var _dq10 = _frameFrom[i]; + var _dq11 = _frameFrom[i + 1]; + var _dq12 = _frameFrom[i + 2]; + var _dq13 = _frameFrom[i + 3]; + // (* 2 since we use this only in the translation reconstruction) + var _dq14 = _frameFrom[i + 4] * 2; + var _dq15 = _frameFrom[i + 5] * 2; + var _dq16 = _frameFrom[i + 6] * 2; + var _dq17 = _frameFrom[i + 7] * 2; + + // Second dual quaternion + var _dq20 = _frameTo[i]; + var _dq21 = _frameTo[i + 1]; + var _dq22 = _frameTo[i + 2]; + var _dq23 = _frameTo[i + 3]; + // (* 2 since we use this only in the translation reconstruction) + var _dq24 = _frameTo[i + 4] * 2; + var _dq25 = _frameTo[i + 5] * 2; + var _dq26 = _frameTo[i + 6] * 2; + var _dq27 = _frameTo[i + 7] * 2; + + // Lerp between reconstructed translations + var _pos0 = lerp( + _dq17 * (-_dq10) + _dq14 * _dq13 + _dq15 * (-_dq12) - _dq16 * (-_dq11), + _dq27 * (-_dq20) + _dq24 * _dq23 + _dq25 * (-_dq22) - _dq26 * (-_dq21), + _factor + ); + + var _pos1 = lerp( + _dq17 * (-_dq11) + _dq15 * _dq13 + _dq16 * (-_dq10) - _dq14 * (-_dq12), + _dq27 * (-_dq21) + _dq25 * _dq23 + _dq26 * (-_dq20) - _dq24 * (-_dq22), + _factor + ); + + var _pos2 = lerp( + _dq17 * (-_dq12) + _dq16 * _dq13 + _dq14 * (-_dq11) - _dq15 * (-_dq10), + _dq27 * (-_dq22) + _dq26 * _dq23 + _dq24 * (-_dq21) - _dq25 * (-_dq20), + _factor + ); + + // Slerp rotations and store result into _dq1 + var _norm; + + _norm = 1 / sqrt(_dq10 * _dq10 + + _dq11 * _dq11 + + _dq12 * _dq12 + + _dq13 * _dq13); + + _dq10 *= _norm; + _dq11 *= _norm; + _dq12 *= _norm; + _dq13 *= _norm; + + _norm = sqrt(_dq20 * _dq20 + + _dq21 * _dq21 + + _dq22 * _dq22 + + _dq23 * _dq23); + + _dq20 *= _norm; + _dq21 *= _norm; + _dq22 *= _norm; + _dq23 *= _norm; + + var _dot = _dq10 * _dq20 + + _dq11 * _dq21 + + _dq12 * _dq22 + + _dq13 * _dq23; + + if (_dot < 0) + { + _dot = -_dot; + _dq20 *= -1; + _dq21 *= -1; + _dq22 *= -1; + _dq23 *= -1; + } + + if (_dot > 0.9995) + { + _dq10 = lerp(_dq10, _dq20, _factor); + _dq11 = lerp(_dq11, _dq21, _factor); + _dq12 = lerp(_dq12, _dq22, _factor); + _dq13 = lerp(_dq13, _dq23, _factor); + } + else + { + var _theta0 = arccos(_dot); + var _theta = _theta0 * _factor; + var _sinTheta = sin(_theta); + var _sinTheta0 = sin(_theta0); + var _s2 = _sinTheta / _sinTheta0; + var _s1 = cos(_theta) - (_dot * _s2); + + _dq10 = (_dq10 * _s1) + (_dq20 * _s2); + _dq11 = (_dq11 * _s1) + (_dq21 * _s2); + _dq12 = (_dq12 * _s1) + (_dq22 * _s2); + _dq13 = (_dq13 * _s1) + (_dq23 * _s2); + } + + // Create new dual quaternion from translation and rotation and + // write it into the frame + _frame[@ i] = _dq10; + _frame[@ i + 1] = _dq11; + _frame[@ i + 2] = _dq12; + _frame[@ i + 3] = _dq13; + _frame[@ i + 4] = (+_pos0 * _dq13 + _pos1 * _dq12 - _pos2 * _dq11) * 0.5; + _frame[@ i + 5] = (+_pos1 * _dq13 + _pos2 * _dq10 - _pos0 * _dq12) * 0.5; + _frame[@ i + 6] = (+_pos2 * _dq13 + _pos0 * _dq11 - _pos1 * _dq10) * 0.5; + _frame[@ i + 7] = (-_pos0 * _dq10 - _pos1 * _dq11 - _pos2 * _dq12) * 0.5; + + i += 8; + } + + array_push(_framesDest, _frame); + ++_time; + } + + return _transition; + }; +} diff --git a/scripts/BBMOD_Animation/BBMOD_Animation.yy b/scripts/BBMOD_Animation/BBMOD_Animation.yy new file mode 100644 index 000000000..d2100eb67 --- /dev/null +++ b/scripts/BBMOD_Animation/BBMOD_Animation.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Animation", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Animation", + "path": "folders/_Extensions/BBMOD/Core/Animation.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AnimationInstance/BBMOD_AnimationInstance.gml b/scripts/BBMOD_AnimationInstance/BBMOD_AnimationInstance.gml new file mode 100644 index 000000000..09a49058e --- /dev/null +++ b/scripts/BBMOD_AnimationInstance/BBMOD_AnimationInstance.gml @@ -0,0 +1,27 @@ +/// @func BBMOD_AnimationInstance(_animation) +/// +/// @desc An instance of an animation. Used for animation playback. +/// +/// @param {Struct.BBMOD_Animation} _animation An animation to create an +/// instance of. +/// +/// @see BBMOD_Animation +function BBMOD_AnimationInstance(_animation) constructor +{ + /// @var {Struct.BBMOD_Animation} The animation to be played. + /// @see BBMOD_Animation + /// @readonly + Animation = _animation; + + /// @var {Bool} If `true` then the animation should be looped. + /// @readonly + Loop = false; + + /// @var {Real} The last frame when animation events were executed. + /// @private + __eventExecuted = -1; + + /// @var {Real} + /// @private + __animationTime = 0; +} diff --git a/scripts/BBMOD_AnimationInstance/BBMOD_AnimationInstance.yy b/scripts/BBMOD_AnimationInstance/BBMOD_AnimationInstance.yy new file mode 100644 index 000000000..51e93675d --- /dev/null +++ b/scripts/BBMOD_AnimationInstance/BBMOD_AnimationInstance.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AnimationInstance", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Animation", + "path": "folders/_Extensions/BBMOD/Core/Animation.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AnimationPlayer/BBMOD_AnimationPlayer.gml b/scripts/BBMOD_AnimationPlayer/BBMOD_AnimationPlayer.gml new file mode 100644 index 000000000..185d0c6b4 --- /dev/null +++ b/scripts/BBMOD_AnimationPlayer/BBMOD_AnimationPlayer.gml @@ -0,0 +1,600 @@ +/// @macro {Real} Maximum number of bones that a single model can have. +/// Equals to 128. +#macro BBMOD_MAX_BONES 128 + +/// @macro {String} An event triggered when an animation player changes to a +/// different animation. The event data will contain the previous animation. +/// You can retrieve the new animation using +/// {@link BBMOD_AnimationPlayer.Animation}. +/// @see BBMOD_AnimationPlayer.on_event +#macro BBMOD_EV_ANIMATION_CHANGE "bbmod_ev_animation_change" + +/// @macro {String} An event triggered when an animation finishes playing. The +/// event data will contain the animation that ended. +/// @see BBMOD_AnimationPlayer.on_event +#macro BBMOD_EV_ANIMATION_END "bbmod_ev_animation_end" + +/// @macro {String} An event triggered when an animation loops and continues +/// playing from the start. The event data will contain the animation that +/// looped. +/// @see BBMOD_AnimationPlayer.on_event +#macro BBMOD_EV_ANIMATION_LOOP "bbmod_ev_animation_loop" + +#macro __BBMOD_EV_ALL "__bbmod_ev_all" + +/// @func BBMOD_AnimationPlayer(_model[, _paused]) +/// +/// @extends BBMOD_Class +/// +/// @implements {BBMOD_IEventListener} +/// @implements {BBMOD_IRenderable} +/// +/// @desc An animation player. Each instance of an animated model should have +/// its own animation player. +/// +/// @param {Struct.BBMOD_Model} _model A model that the animation player +/// animates. +/// @param {Bool} [_paused] If `true` then the animation player is created +/// as paused. Defaults to `false`. +/// +/// @example +/// Following code shows how to load models and animations in a resource manager +/// object and then play animations in multiple instances of another object. +/// +/// ```gml +/// /// @desc Create event of OResourceManager +/// modCharacter = new BBMOD_Model("character.bbmod"); +/// animIdle = new BBMOD_Animation("idle.bbanim"); +/// +/// /// @desc Create event of OCharacter +/// model = OResourceManager.modCharacter; +/// animationPlayer = new BBMOD_AnimationPlayer(model); +/// animationPlayer.play(OResourceManager.animIdle, true); +/// +/// /// @desc Step event of OCharacter +/// animationPlayer.update(delta_time); +/// +/// /// @desc Draw event of OCharacter +/// bbmod_material_reset(); +/// animationPlayer.render(); +/// bbmod_material_reset(); +/// ``` +/// +/// @see BBMOD_Animation +/// @see BBMOD_IEventListener +/// @see BBMOD_Model +function BBMOD_AnimationPlayer(_model, _paused=false) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + implement(BBMOD_IEventListener); + + static Class_destroy = destroy; + + /// @var {Struct.BBMOD_Model} A model that the animation player animates. + /// @readonly + Model = _model; + + /// @var {Id.DsList} List of animations to play. + /// @private + __animations = ds_list_create(); + + /// @var {Struct.BBMOD_Animation} The currently playing animation or + /// `undefined`. + /// @readonly + Animation = undefined; + + /// @var {Bool} If true then {@link BBMOD_AnimationPlayer.Animation} loops. + /// @readonly + AnimationLoops = false; + + /// @var {Struct.BBMOD_Animation} + /// @private + __animationLast = undefined; + + /// @var {Struct.BBMOD_AnimationInstance} + /// @private + __animationInstanceLast = undefined; + + /// @var {Array} Array of node position overrides. + /// @private + __nodePositionOverride = array_create(BBMOD_MAX_BONES, undefined); + + /// @var {Array} Array of node rotation + /// overrides. + /// @private + __nodeRotationOverride = array_create(BBMOD_MAX_BONES, undefined); + + /// @var {Array} Array of node post-rotations. + /// @private + __nodeRotationPost = array_create(BBMOD_MAX_BONES, undefined); + + /// @var {Bool} If `true`, then the animation playback is paused. + Paused = _paused; + + /// @var {Real} The current animation playback time. + /// @readonly + Time = 0; + + /// @var {Array} + /// @private + __frame = undefined; + + /// @var {Real} Number of frames (calls to {@link BBMOD_AnimationPlayer.update}) + /// to skip. Defaults to 0 (frame skipping is disabled). Increasing the + /// value increases performance. Use `infinity` to disable computing + /// animation frames entirely. + /// @note This does not affect animation events. These are still triggered + /// even if the frame is skipped. + Frameskip = 0; + + /// @var {Real} + /// @private + __frameskipCurrent = 0; + + /// @var {Real} Controls animation playback speed. Must be a positive + /// number! + PlaybackSpeed = 1; + + /// @var {Array} An array of node transforms in world space. + /// Useful for attachments. + /// @see BBMOD_AnimationPlayer.get_node_transform + /// @private + __nodeTransform = array_create(BBMOD_MAX_BONES * 8, 0.0); + + /// @var {Array} An array containing transforms of all bones. + /// Used to pass current model pose as a uniform to a vertex shader. + /// @see BBMOD_AnimationPlayer.get_transform + /// @private + __transformArray = array_create(BBMOD_MAX_BONES * 8, 0.0); + + static animate = function (_animationInstance, _animationTime) { + var _model = Model; + var _animation = _animationInstance.Animation; + var _frame = _animation.__framesParent[_animationTime]; + __frame = _frame; + var _transformArray = __transformArray; + var _offsetArray = _model.__offsetArray; + var _nodeTransform = __nodeTransform; + var _positionOverrides = __nodePositionOverride; + var _rotationOverrides = __nodeRotationOverride; + var _rotationPost = __nodeRotationPost; + + static _animStack = []; + if (array_length(_animStack) < _model.NodeCount) + { + array_resize(_animStack, _model.NodeCount); + } + + _animStack[@ 0] = _model.RootNode; + var _stackNext = 1; + + repeat (_model.NodeCount) + { + if (_stackNext == 0) + { + break; + } + + var _node = _animStack[--_stackNext]; + + // TODO: Separate skeleton from the rest of the nodes to save on + // iterations here. + + var _nodeIndex = _node.Index; + var _nodeOffset = _nodeIndex * 8; + var _nodePositionOverride = _positionOverrides[_nodeIndex]; + var _nodeRotationOverride = _rotationOverrides[_nodeIndex]; + var _nodeRotationPost = _rotationPost[_nodeIndex]; + var _nodeParent = _node.Parent; + var _parentIndex = (_nodeParent != undefined) ? _nodeParent.Index : -1; + + if (_nodePositionOverride != undefined + || _nodeRotationOverride != undefined + || _nodeRotationPost != undefined) + { + var _dq = new BBMOD_DualQuaternion().FromArray(_frame, _nodeOffset); + var _position = (_nodePositionOverride != undefined) + ? _nodePositionOverride + : _dq.GetTranslation(); + var _rotation = (_nodeRotationOverride != undefined) + ? _nodeRotationOverride + : _dq.GetRotation(); + if (_nodeRotationPost != undefined) + { + _rotation = _nodeRotationPost.Mul(_rotation); + } + _dq.FromTranslationRotation(_position, _rotation); + if (_parentIndex != -1) + { + _dq = _dq.Mul(new BBMOD_DualQuaternion() + .FromArray(_nodeTransform, _parentIndex * 8)); + } + _dq.ToArray(_nodeTransform, _nodeOffset); + } + else + { + if (_parentIndex == -1) + { + // No parent transform -> just copy the node transform + array_copy(_nodeTransform, _nodeOffset, _frame, _nodeOffset, 8); + } + else + { + // Multiply node transform with parent's transform + __bbmod_dual_quaternion_array_multiply( + _frame, _nodeOffset, _nodeTransform, _parentIndex * 8, + _nodeTransform, _nodeOffset); + } + } + + if (_node.IsBone) + { + __bbmod_dual_quaternion_array_multiply( + _offsetArray, _nodeOffset, + _nodeTransform, _nodeOffset, + _transformArray, _nodeOffset); + } + + var _children = _node.Children; + var i = 0; + repeat (array_length(_children)) + { + _animStack[_stackNext++] = _children[i++]; + } + } + } + + /// @func update(_deltaTime) + /// + /// @desc Updates the animation player. This should be called every frame in + /// the step event. + /// + /// @param {Real} _deltaTime How much time has passed since the last frame + /// (in microseconds). + /// + /// @return {Struct.BBMOD_AnimationPlayer} Returns `self`. + static update = function (_deltaTime) { + if (!Model.IsLoaded) + { + return self; + } + + if (Paused) + { + return self; + } + + Time += _deltaTime * 0.000001 * PlaybackSpeed; + + repeat (ds_list_size(__animations)) + { + var _animInst = __animations[| 0]; + var _animation = _animInst.Animation; + + if (!_animation.IsLoaded) + { + break; + } + + var _animationTime = _animation.get_animation_time(Time); + + if (_animationTime >= _animation.Duration) + { + if (_animInst.Loop) + { + Time %= (_animation.Duration / _animation.TicsPerSecond); + _animationTime %= _animation.Duration; + _animInst.__eventExecuted = -1; + trigger_event(BBMOD_EV_ANIMATION_LOOP, _animation); + } + else + { + Time = 0.0; + ds_list_delete(__animations, 0); + if (!_animation.__isTransition) + { + Animation = undefined; + trigger_event(BBMOD_EV_ANIMATION_END, _animation); + } + continue; + } + } + + _animInst.__animationTime = _animationTime; + + var _nodeSize = Model.NodeCount * 8; + if (array_length(__nodeTransform) < _nodeSize) + { + array_resize(__nodeTransform, _nodeSize); + } + + var _boneSize = Model.BoneCount * 8; + if (array_length(__transformArray) != _boneSize) + { + array_resize(__transformArray, _boneSize); + } + + var _animEvents = _animation.__events; + var _eventIndex = 0; + var _eventExecuted = _animInst.__eventExecuted; + + repeat (array_length(_animEvents) / 2) + { + var _eventFrame = _animEvents[_eventIndex]; + if (_eventFrame <= _animationTime && _eventExecuted < _eventFrame) + { + trigger_event(_animEvents[_eventIndex + 1], _animation); + } + _eventIndex += 2; + } + + _animInst.__eventExecuted = _animationTime; + + //static _iters = 0; + //static _sum = 0; + //var _t = get_timer(); + + if (__frameskipCurrent == 0) + { + if (_animation.__spaces & BBMOD_BONE_SPACE_BONE) + { + if (_animation.__spaces & BBMOD_BONE_SPACE_WORLD) + { + array_copy(__nodeTransform, 0, + _animation.__framesWorld[_animationTime], 0, _nodeSize); + } + + // TODO: Just use the animation's array right away? + array_copy(__transformArray, 0, + _animation.__framesBone[_animationTime], 0, _boneSize); + } + else if (_animation.__spaces & BBMOD_BONE_SPACE_WORLD) + { + var _frame = _animation.__framesWorld[_animationTime]; + var _transformArray = __transformArray; + var _offsetArray = Model.__offsetArray; + + array_copy(__nodeTransform, 0, _frame, 0, _nodeSize); + array_copy(_transformArray, 0, _frame, 0, _boneSize); + + var _index = 0; + repeat (Model.BoneCount) + { + __bbmod_dual_quaternion_array_multiply( + _offsetArray, _index, + _frame, _index, + _transformArray, _index); + _index += 8; + } + } + else if (_animation.__spaces & BBMOD_BONE_SPACE_PARENT) + { + animate(_animInst, _animationTime); + } + + array_copy(__transformArray, _boneSize, __nodeTransform, _boneSize, + _nodeSize - _boneSize); + } + + if (Frameskip == infinity) + { + __frameskipCurrent = -1; + } + else if (++__frameskipCurrent > Frameskip) + { + __frameskipCurrent = 0; + } + + //var _current = get_timer() - _t; + //_sum += _current; + //++_iters; + //show_debug_message("Current: " + string(_current) + "μs"); + //show_debug_message("Average: " + string(_sum / _iters) + "μs"); + + __animationInstanceLast = _animInst; + } + + return self; + }; + + /// @func play(_animation[, _loop]) + /// + /// @desc Starts playing an animation from its start. + /// + /// @param {Struct.BBMOD_Animation} _animation An animation to play. + /// @param {Bool} [_loop] If `true` then the animation will be looped. + /// Defaults to `false`. + /// + /// @return {Struct.BBMOD_AnimationPlayer} Returns `self`. + static play = function (_animation, _loop=false) { + Animation = _animation; + AnimationLoops = _loop; + + if (__animationLast != _animation) + { + trigger_event(BBMOD_EV_ANIMATION_CHANGE, Animation); + __animationLast = _animation; + } + + Time = 0; + + var _animationList = __animations; + var _animationLast = __animationInstanceLast; + + ds_list_clear(_animationList); + + if (_animationLast != undefined + && _animationLast.Animation.TransitionOut + _animation.TransitionIn > 0) + { + var _transition = _animationLast.Animation.create_transition( + _animationLast.__animationTime, + _animation, + 0); + + if (_transition != undefined) + { + ds_list_add(_animationList, new BBMOD_AnimationInstance(_transition)); + } + } + + var _animationInstance = new BBMOD_AnimationInstance(_animation); + _animationInstance.Loop = AnimationLoops; + ds_list_add(_animationList, _animationInstance); + + return self; + }; + + /// @func change(_animation[, _loop]) + /// + /// @desc Starts playing an animation from its start, only if it is a + /// different one that the last played animation. + /// + /// @param {Struct.BBMOD_Animation} _animation The animation to change to, + /// @param {Bool} [_loop] If `true` then the animation will be looped. + /// Defaults to `false`. + /// + /// @return {Struct.BBMOD_AnimationPlayer} Returns `self`. + /// + /// @see BBMOD_AnimationPlayer.Animation + static change = function (_animation, _loop=false) { + gml_pragma("forceinline"); + if (Animation != _animation) + { + play(_animation, _loop); + } + return self; + }; + + /// @func get_transform() + /// + /// @desc Returns an array of current transformations of all bones. This + /// should be passed to a vertex shader. + /// + /// @return {Array} The transformation array. + static get_transform = function () { + gml_pragma("forceinline"); + return __transformArray; + }; + + /// @func get_node_transform(_nodeIndex) + /// + /// @desc Returns a transformation (dual quaternion) of a node, which can be + /// used for example for attachments. + /// + /// @param {Real} _nodeIndex An index of a node. + /// + /// @return {Struct.BBMOD_DualQuaternion} The transformation. + /// + /// @see BBMOD_Model.find_node_id + static get_node_transform = function (_nodeIndex) { + gml_pragma("forceinline"); + return new BBMOD_DualQuaternion().FromArray(__nodeTransform, _nodeIndex * 8); + }; + + /// @func get_node_transform_from_frame(_nodeIndex) + /// + /// @desc Returns a transformation (dual quaternion) of a node from the last + /// animation frame. This is useful if you want to add additional + /// transformations onto an animated bone, instead of competely replacing it. + /// + /// @param {Real} _nodeIndex An index of a node. + /// + /// @return {Struct.BBMOD_DualQuaternion} The transformation. + /// + /// @see BBMOD_Model.find_node_id + /// @see BBMOD_AnimationPlayer.get_node_transform + static get_node_transform_from_frame = function (_nodeIndex) { + gml_pragma("forceinline"); + if (__frame == undefined) + { + return new BBMOD_DualQuaternion(); + } + return new BBMOD_DualQuaternion().FromArray(__frame, _nodeIndex * 8); + }; + + /// @func set_node_position(_nodeIndex, _position) + /// + /// @desc Overrides a position of a node. + /// + /// @param {Real} _nodeIndex An index of a node. + /// @param {Struct.BBMOD_Vec3} _position A new position of a node. Use + /// `undefined` to unset the position override. + /// + /// @return {Struct.BBMOD_AnimationPlayer} Returns `self`. + static set_node_position = function (_nodeIndex, _position) { + gml_pragma("forceinline"); + __nodePositionOverride[@ _nodeIndex] = _position; + return self; + }; + + /// @func set_node_rotation(_nodeIndex, _rotation) + /// + /// @desc Overrides a rotation of a node. + /// + /// @param {Real} _nodeIndex An index of a node. + /// @param {Struct.BBMOD_Quaternion} _rotation A new rotation of a node. + /// Use `undefined` to unset the rotation override. + /// + /// @return {Struct.BBMOD_AnimationPlayer} Returns `self`. + static set_node_rotation = function (_nodeIndex, _rotation) { + gml_pragma("forceinline"); + __nodeRotationOverride[@ _nodeIndex] = _rotation; + return self; + }; + + /// @func set_node_rotation_post(_nodeIndex, _rotation) + /// + /// @desc Sets a post-rotation of a node. + /// + /// @param {Real} _nodeIndex An index of a node. + /// @param {Struct.BBMOD_Quaternion} _rotation A rotation applied after the + /// node is rotated using frame data. Use `undefined` to unset the post-rotation. + /// + /// @return {Struct.BBMOD_AnimationPlayer} Returns `self`. + static set_node_rotation_post = function (_nodeIndex, _rotation) { + gml_pragma("forceinline"); + __nodeRotationPost[@ _nodeIndex] = _rotation; + return self; + }; + + /// @func submit([_materials]) + /// + /// @desc Immediately submits the animated model for rendering. + /// + /// @param {Array} [_materials] An array of materials, + /// one for each material slot of the model. If not specified, then + /// {@link BBMOD_Model.Materials} is used. Defaults to `undefined`. + /// + /// @return {Struct.BBMOD_AnimationPlayer} Returns `self`. + static submit = function (_materials=undefined) { + gml_pragma("forceinline"); + Model.submit(_materials, get_transform()); + return self; + }; + + /// @func render([_materials]) + /// + /// @desc Enqueues the animated model for rendering. + /// + /// @param {Array} [_materials] An array of materials, + /// one for each material slot of the model. If not specified, then + /// {@link BBMOD_Model.Materials} is used. Defaults to `undefined`. + /// + /// @return {Struct.BBMOD_AnimationPlayer} Returns `self`. + static render = function (_materials=undefined) { + gml_pragma("forceinline"); + Model.render(_materials, get_transform()); + return self; + }; + + static destroy = function () { + Class_destroy(); + ds_list_destroy(__animations); + __nodePositionOverride = undefined; + __nodeRotationOverride = undefined; + __nodeRotationPost = undefined; + return undefined; + }; +} diff --git a/scripts/BBMOD_AnimationPlayer/BBMOD_AnimationPlayer.yy b/scripts/BBMOD_AnimationPlayer/BBMOD_AnimationPlayer.yy new file mode 100644 index 000000000..9e2961750 --- /dev/null +++ b/scripts/BBMOD_AnimationPlayer/BBMOD_AnimationPlayer.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AnimationPlayer", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Animation", + "path": "folders/_Extensions/BBMOD/Core/Animation.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AnimationState/BBMOD_AnimationState.gml b/scripts/BBMOD_AnimationState/BBMOD_AnimationState.gml new file mode 100644 index 000000000..ffde7a9e0 --- /dev/null +++ b/scripts/BBMOD_AnimationState/BBMOD_AnimationState.gml @@ -0,0 +1,81 @@ +/// @func BBMOD_AnimationState(_name, _animation[, _loop]) +/// +/// @extends BBMOD_State +/// @implements {BBMOD_IEventListener} +/// +/// @desc A state of an animation state machine. +/// +/// @param {String} _name The name of the state. +/// @param {Struct.BBMOD_Animation} _animation The animation played while the +/// is active. +/// @param {Bool} [_loop] If `true` then the animation will be looped. +/// Defaults to `false`. +/// +/// @example +/// The following code shows examples of animation states which together make +/// a simple locomotion state machine. +/// +/// ```gml +/// stateIdle = new BBMOD_AnimationState("Idle", animIdle, true); +/// stateIdle.OnUpdate = method(self, function () { +/// if (in_air()) +/// { +/// animationStateMachine.change_state(stateJump); +/// return; +/// } +/// if (speed > 0) +/// { +/// animationStateMachine.change_state(stateWalk); +/// return; +/// } +/// }); +/// animationStateMachine.add_state(stateIdle); +/// +/// stateWalk = new BBMOD_AnimationState("Walk", animWalk, true); +/// stateWalk.OnUpdate = method(self, function () { +/// if (in_air()) +/// { +/// animationStateMachine.change_state(stateJump); +/// return; +/// } +/// if (speed == 0) +/// { +/// animationStateMachine.change_state(stateIdle); +/// return; +/// } +/// }); +/// animationStateMachine.add_state(stateWalk); +/// +/// stateJump = new BBMOD_AnimationState("Jump", animJump, true); +/// stateJump.OnUpdate = method(self, function () { +/// if (!in_air()) +/// { +/// animationStateMachine.change_state(stateLanding); +/// return; +/// } +/// }); +/// animationStateMachine.add_state(stateJump); +/// +/// stateLanding = new BBMOD_AnimationState("Landing", animLanding); +/// stateLanding.on_event(BBMOD_EV_ANIMATION_END, method(self, function () { +/// animationStateMachine.change_state(stateIdle); +/// })); +/// ``` +/// +/// @see BBMOD_AnimationStateMachine +function BBMOD_AnimationState(_name, _animation, _loop=false) + : BBMOD_State(_name) constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + implement(BBMOD_IEventListener); + + /// @var {Struct.BBMOD_Animation} The animation played while the state is + /// active. + /// @readonly + Animation = _animation; + + /// @var {Bool} If `true` then the animation is played in loops. + /// @readonly + Loop = _loop; +} diff --git a/scripts/BBMOD_AnimationState/BBMOD_AnimationState.yy b/scripts/BBMOD_AnimationState/BBMOD_AnimationState.yy new file mode 100644 index 000000000..17ea7b34b --- /dev/null +++ b/scripts/BBMOD_AnimationState/BBMOD_AnimationState.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AnimationState", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "StateMachine", + "path": "folders/_Extensions/BBMOD/StateMachine.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AnimationStateMachine/BBMOD_AnimationStateMachine.gml b/scripts/BBMOD_AnimationStateMachine/BBMOD_AnimationStateMachine.gml new file mode 100644 index 000000000..d979835c2 --- /dev/null +++ b/scripts/BBMOD_AnimationStateMachine/BBMOD_AnimationStateMachine.gml @@ -0,0 +1,87 @@ +/// @func BBMOD_AnimationStateMachine(_animationPlayer) +/// +/// @extends BBMOD_StateMachine +/// +/// @desc A state machine that controls animation playback. +/// +/// @param {Struct.BBMOD_AnimationPlayer} _animationPlayer The animation player +/// to control. +/// +/// @example +/// Following code shows an animation state machine which goes to the "Idle" +/// state on start and independently on the current state switches to "Dead" +/// state when variable `hp` meets 0. After the death animation ends, the state +/// machine enters the final state and the instance is destroyed. +/// +/// ```gml +/// // Create event +/// destroy = false; +/// animationPlayer = new BBMOD_AnimationPlayer(model); +/// +/// animationStateMachine = new BBMOD_AnimationStateMachine(animationPlayer); +/// animationStateMachine.OnEnter = method(self, function () { +/// animationStateMachine.change_state(stateIdle); +/// }); +/// animationStateMachine.OnExit = method(self, function () { +/// destroy = true; +/// }); +/// animationStateMachine.OnPreUpdate = method(self, function () { +/// if (hp <= 0 && animationStateMachine.State != stateDead) +/// { +/// animationStateMachine.change_state(stateDead); +/// } +/// }); +/// +/// stateIdle = new BBMOD_AnimationState("Idle", animIdle); +/// animationStateMachine.add_state(stateIdle); +/// +/// stateDead = new BBMOD_AnimationState("Dead", animDead); +/// stateDead.on_event(BBMOD_EV_ANIMATION_END, method(self, function () { +/// animationStateMachine.finish(); +/// })); +/// animationStateMachine.add_state(stateDead); +/// +/// animationStateMachine.start(); +/// +/// // Step event +/// animationStateMachine.update(); +/// if (destroy) +/// { +/// instance_destroy(); +/// } +/// +/// // Clean Up event +/// animationPlayer = animationPlayer.destroy(); +/// animationStateMachine = animationStateMachine.destroy(); +/// ``` +/// +/// @see BBMOD_AnimationPlayer +/// @see BBMOD_AnimationState +function BBMOD_AnimationStateMachine(_animationPlayer) + : BBMOD_StateMachine() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static StateMachine_update = update; + + /// @var {Struct.BBMOD_AnimationPlayer} The state machine's animation player. + /// @readonly + AnimationPlayer = _animationPlayer; + + AnimationPlayer.on_event(method(self, function (_data, _event) { + if (State != undefined) + { + State.trigger_event(_event, _data); + } + })); + + static update = function (_deltaTime) { + StateMachine_update(_deltaTime); + if (State != undefined) + { + AnimationPlayer.change(State.Animation, State.Loop); + } + AnimationPlayer.update(_deltaTime); + return self; + }; +} diff --git a/scripts/BBMOD_AnimationStateMachine/BBMOD_AnimationStateMachine.yy b/scripts/BBMOD_AnimationStateMachine/BBMOD_AnimationStateMachine.yy new file mode 100644 index 000000000..fc0dc7cfb --- /dev/null +++ b/scripts/BBMOD_AnimationStateMachine/BBMOD_AnimationStateMachine.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AnimationStateMachine", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "StateMachine", + "path": "folders/_Extensions/BBMOD/StateMachine.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_AttractorModule/BBMOD_AttractorModule.gml b/scripts/BBMOD_AttractorModule/BBMOD_AttractorModule.gml new file mode 100644 index 000000000..dbce14490 --- /dev/null +++ b/scripts/BBMOD_AttractorModule/BBMOD_AttractorModule.gml @@ -0,0 +1,80 @@ +/// @func BBMOD_AttractorModule([_position[, _relative[, _radius[, _force]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that attracts/repels particles to/from a given +/// position. +/// +/// @param {Struct.BBMOD_Vec3} [_position] The position to attract/repel +/// particles to/from. Defaults to `(0, 0, 0)`. +/// @param {Bool} [_relative] If `true`, then the position is relative to the +/// emitter. Defaults to `true`. +/// @param {Real} [_radius] The radius of the influence. Defaults to 1.0. +/// @param {Real} [_force] The strength of the force. Use negative to repel the +/// particles. Defaults to 1.0. +function BBMOD_AttractorModule( + _position=new BBMOD_Vec3(), + _relative=true, + _radius=1.0, + _force=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_Vec3} The position to attract/repel particles to/from. + /// Default value is `(0, 0, 0)`. + Position = _position; + + /// @var {Bool} If `true`, then {@link BBMOD_AttractorModule.Position} is + /// relative to the emitter. Default value is `true`. + Relative = _relative; + + /// @var {Struct.BBMOD_Vec3} + /// @private + __positionReal = Position; + + /// @var {Real} The radius of the influence. Defaults to 1.0. + Radius = _radius; + + /// @var {Real} The strength of the force. Use negative to repel the + /// particles. Defaults value is 1.0. + Force = _force; + + static on_update = function (_emitter, _deltaTime) { + __positionReal = Relative ? _emitter.Position.Add(Position) : Position; + + var _particles = _emitter.Particles; + var _positionRealX = __positionReal.X; + var _positionRealY = __positionReal.Y; + var _positionRealZ = __positionReal.Z; + var _radius = Radius; + var _force = Force; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _mass = _particles[# BBMOD_EParticle.Mass, _particleIndex]; + if (_mass != 0.0) + { + var _vecX = _positionRealX + - _particles[# BBMOD_EParticle.PositionX, _particleIndex]; + var _vecY = _positionRealY + - _particles[# BBMOD_EParticle.PositionY, _particleIndex]; + var _vecZ = _positionRealZ + - _particles[# BBMOD_EParticle.PositionZ, _particleIndex]; + var _distance = sqrt((_vecX * _vecX) + (_vecY * _vecY) + (_vecZ * _vecZ)); + if (_distance <= _radius) + { + var _scale = (_force * (1.0 - (_distance / _radius))) / _mass; + _particles[# BBMOD_EParticle.AccelerationX, _particleIndex] += + (_vecX / _distance) * _scale; + _particles[# BBMOD_EParticle.AccelerationY, _particleIndex] += + (_vecY / _distance) * _scale; + _particles[# BBMOD_EParticle.AccelerationZ, _particleIndex] += + (_vecZ / _distance) * _scale; + } + } + ++_particleIndex; + } + }; +} diff --git a/scripts/BBMOD_AttractorModule/BBMOD_AttractorModule.yy b/scripts/BBMOD_AttractorModule/BBMOD_AttractorModule.yy new file mode 100644 index 000000000..b48aa379f --- /dev/null +++ b/scripts/BBMOD_AttractorModule/BBMOD_AttractorModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_AttractorModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Physics", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Physics.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_BaseCamera/BBMOD_BaseCamera.gml b/scripts/BBMOD_BaseCamera/BBMOD_BaseCamera.gml new file mode 100644 index 000000000..cb8319869 --- /dev/null +++ b/scripts/BBMOD_BaseCamera/BBMOD_BaseCamera.gml @@ -0,0 +1,354 @@ +/// @var {Struct.BBMOD_BaseCamera} The last used camera. Can be `undefined`. +/// @private +global.__bbmodCameraCurrent = undefined; + +/// @func BBMOD_BaseCamera() +/// +/// @extends BBMOD_Class +/// +/// @desc A camera with support for both orthographic and perspective +/// projection. +/// +/// @see BBMOD_Camera +function BBMOD_BaseCamera() + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {camera} An underlying GameMaker camera. + /// @readonly + Raw = camera_create(); + + /// @var {Real} The camera's exposure value. Defaults to `1`. + Exposure = 1.0; + + /// @var {Struct.BBMOD_Vec3} The camera's positon. Defaults to `(0, 0, 0)`. + Position = new BBMOD_Vec3(0.0); + + /// @var {Struct.BBMOD_Vec3} A position where the camera is looking at. + Target = BBMOD_VEC3_FORWARD; + + /// @var {Struct.BBMOD_Vec3} The up vector. + Up = BBMOD_VEC3_UP; + + /// @var {Real} The camera's field of view. Defaults to `60`. + /// @note This does not have any effect when {@link BBMOD_BaseCamera.Orthographic} + /// is enabled. + Fov = 60.0; + + /// @var {Real} The camera's aspect ratio. Defaults to + /// `window_get_width() / window_get_height()`. + AspectRatio = window_get_width() / window_get_height(); + + /// @var {Real} Distance to the near clipping plane. Anything closer to the + /// camera than this will not be visible. Defaults to `0.1`. + /// @note This can be a negative value if {@link BBMOD_BaseCamera.Orthographic} + /// is enabled. + ZNear = 0.1; + + /// @var {Real} Distance to the far clipping plane. Anything farther from + /// the camera than this will not be visible. Defaults to `32768`. + ZFar = 32768.0; + + /// @var {Bool} Use `true` to enable orthographic projection. Defaults to + /// `false` (perspective projection). + Orthographic = false; + + /// @var {Real} The width of the orthographic projection. If `undefined`, + /// then it is computed from {@link BBMOD_BaseCamera.Height} using + /// {@link BBMOD_BaseCamera.AspectRatio}. Defaults to the window's width. + /// @see BBMOD_BaseCamera.Orthographic + Width = window_get_width(); + + /// @var {Real} The height of the orthographic projection. If `undefined`, + /// then it is computed from {@link BBMOD_BaseCamera.Width} using + /// {@link BBMOD_BaseCamera.AspectRatio}. Defaults to `undefined`. + /// @see BBMOD_BaseCamera.Orthographic + Height = undefined; + + /// @var {Bool} If `true` then the camera updates position and orientation + /// of the 3D audio listener in the {@link BBMOD_BaseCamera.update_matrices} + /// method. Defaults to `true`. + AudioListener = true; + + /// @var {Array} The `view * projection` matrix. + /// @note This is updated each time {@link BBMOD_BaseCamera.update_matrices} + /// is called. + /// @readonly + ViewProjectionMatrix = matrix_build_identity(); + + /// @func __build_proj_mat() + /// + /// @desc Builds a projection matrix based on the camera's properties. + /// + /// @return {Array} The projection matrix. + /// + /// @private + static __build_proj_mat = function () { + var _proj; + if (Orthographic) + { + var _width = (Width != undefined) ? Width : (Height * AspectRatio); + var _height = (Height != undefined) ? Height : (Width / AspectRatio); + _proj = matrix_build_projection_ortho(_width, -_height, ZNear, ZFar); + } + else + { + _proj = matrix_build_projection_perspective_fov( + -Fov, -AspectRatio, ZNear, ZFar); + } + return _proj; + }; + + /// @func update_matrices() + /// + /// @desc Recomputes camera's view and projection matrices. + /// + /// @return {Struct.BBMOD_BaseCamera} Returns `self`. + /// + /// @note This is called automatically in the {@link BBMOD_BaseCamera.update} + /// method, so you do not need to call this unless you modify + /// {@link BBMOD_BaseCamera.Position} or {@link BBMOD_BaseCamera.Target} + /// after the `update` method. + /// + /// @example + /// ```gml + /// /// @desc Step event + /// camera.set_mouselook(true); + /// camera.update(delta_time); + /// if (camera.Position.Z < 0.0) + /// { + /// camera.Position.Z = 0.0; + /// } + /// camera.update_matrices(); + /// ``` + static update_matrices = function () { + gml_pragma("forceinline"); + + var _view = matrix_build_lookat( + Position.X, Position.Y, Position.Z, + Target.X, Target.Y, Target.Z, + Up.X, Up.Y, Up.Z); + camera_set_view_mat(Raw, _view); + + var _proj = __build_proj_mat(); + camera_set_proj_mat(Raw, _proj); + + // Note: Using _view and _proj mat straight away leads into a weird result... + ViewProjectionMatrix = matrix_multiply( + get_view_mat(), + get_proj_mat()); + + if (AudioListener) + { + audio_listener_position(Position.X, Position.Y, Position.Z); + audio_listener_orientation( + Target.X, Target.Y, Target.Z, + Up.X, Up.Y, Up.Z); + } + + return self; + } + + /// @func update(_deltaTime) + /// + /// @desc Updates camera's matrices. + /// + /// @param {Real} _deltaTime How much time has passed since the last frame + /// (in microseconds). + /// + /// @return {Struct.BBMOD_BaseCamera} Returns `self`. + static update = function (_deltaTime) { + update_matrices(); + return self; + }; + + /// @func get_view_mat() + /// + /// @desc Retrieves camera's view matrix. + /// + /// @return {Array} The view matrix. + static get_view_mat = function () { + gml_pragma("forceinline"); + + if (os_browser == browser_not_a_browser) + { + // This returns a struct in HTML5 for some reason... + return camera_get_view_mat(Raw); + } + + var _view = matrix_get(matrix_view); + var _proj = matrix_get(matrix_projection); + camera_apply(Raw); + var _retval = matrix_get(matrix_view); + matrix_set(matrix_view, _view); + matrix_set(matrix_projection, _proj); + return _retval; + }; + + /// @func get_proj_mat() + /// + /// @desc Retrieves camera's projection matrix. + /// + /// @return {Array} The projection matrix. + static get_proj_mat = function () { + gml_pragma("forceinline"); + + if (os_browser == browser_not_a_browser) + { + // This returns a struct in HTML5 for some reason... + return camera_get_proj_mat(Raw); + } + + var _view = matrix_get(matrix_view); + var _proj = matrix_get(matrix_projection); + camera_apply(Raw); + var _retval = matrix_get(matrix_projection); + matrix_set(matrix_view, _view); + matrix_set(matrix_projection, _proj); + return _retval; + }; + + /// @func get_right() + /// + /// @desc Retrieves a vector pointing right relative to the camera's + /// direction. + /// + /// @return {Struct.BBMOD_Vec3} The right vector. + static get_right = function () { + gml_pragma("forceinline"); + var _view = get_view_mat(); + return new BBMOD_Vec3( + _view[0], + _view[4], + _view[8] + ); + }; + + /// @func get_up() + /// + /// @desc Retrieves a vector pointing up relative to the camera's + /// direction. + /// + /// @return {Struct.BBMOD_Vec3} The up vector. + static get_up = function () { + gml_pragma("forceinline"); + var _view = get_view_mat(); + return new BBMOD_Vec3( + _view[1], + _view[5], + _view[9] + ); + }; + + /// @func get_forward() + /// + /// @desc Retrieves a vector pointing forward in the camera's direction. + /// + /// @return {Struct.BBMOD_Vec3} The forward vector. + static get_forward = function () { + gml_pragma("forceinline"); + var _view = get_view_mat(); + return new BBMOD_Vec3( + _view[2], + _view[6], + _view[10] + ); + }; + + /// @func world_to_screen(_position[, _screenWidth[, _screenHeight]]) + /// + /// @desc Computes screen-space position of a point in world-space. + /// + /// @param {Struct.BBMOD_Vec3} _position The world-space position. + /// @param {Real} [_screenWidth] The width of the screen. If `undefined`, it + /// is retrieved using `window_get_width`. + /// @param {Real} [_screenHeight] The height of the screen. If `undefined`, + /// it is retrieved using `window_get_height`. + /// + /// @return {Struct.BBMOD_Vec4} The screen-space position or `undefined` if + /// the point is outside of the screen. + /// + /// @note This requires {@link BBMOD_BaseCamera.ViewProjectionMatrix}, so you + /// should use this *after* {@link BBMOD_BaseCamera.update_matrices} (or + /// {@link BBMOD_BaseCamera.update}) is called! + static world_to_screen = function (_position, _screenWidth=undefined, _screenHeight=undefined) { + gml_pragma("forceinline"); + _screenWidth ??= window_get_width(); + _screenHeight ??= window_get_height(); + var _screenPos = new BBMOD_Vec4(_position.X, _position.Y, _position.Z, 1.0) + .Transform(ViewProjectionMatrix); + if (_screenPos.Z < 0.0) + { + return undefined; + } + _screenPos = _screenPos.Scale(1.0 / _screenPos.W); + _screenPos.X = ((_screenPos.X * 0.5) + 0.5) * _screenWidth; + _screenPos.Y = (1.0 - ((_screenPos.Y * 0.5) + 0.5)) * _screenHeight; + return _screenPos; + }; + + /// @func screen_point_to_vec3(_vector[, _renderer]) + /// + /// @desc Unprojects a position on the screen into a direction in world-space. + /// + /// @param {Struct.BBMOD_Vector2} _vector The position on the screen. + /// @param {Struct.BBMOD_Renderer} [_renderer] A renderer or `undefined`. + /// + /// @return {Struct.BBMOD_Vec3} The world-space direction. + static screen_point_to_vec3 = function (_vector, _renderer=undefined) { + var _forward = get_forward(); + var _up = get_up(); + var _right = get_right(); + var _tFov = dtan(Fov * 0.5); + _up = _up.Scale(_tFov); + _right = _right.Scale(_tFov * AspectRatio); + var _screenWidth = _renderer ? _renderer.get_width() : window_get_width(); + var _screenHeight = _renderer ? _renderer.get_height() : window_get_height(); + var _screenX = _vector.X - (_renderer ? _renderer.X : 0); + var _screenY = _vector.Y - (_renderer ? _renderer.Y : 0); + var _ray = _forward.Add(_up.Scale(1.0 - 2.0 * (_screenY / _screenHeight)) + .Add(_right.Scale(2.0 * (_screenX / _screenWidth) - 1.0))); + return _ray.Normalize(); + }; + + /// @func apply() + /// + /// @desc Applies the camera. + /// + /// @return {Struct.BBMOD_BaseCamera} Returns `self`. + /// + /// @example + /// Following code renders a model from the camera's view. + /// ```gml + /// camera.apply(); + /// bbmod_material_reset(); + /// model.submit(); + /// bbmod_material_reset(); + /// ``` + /// + /// @note This also overrides the camera position and exposure passed to + /// shaders using {@link bbmod_camera_set_position} and + /// {@link bbmod_camera_set_exposure} respectively! + static apply = function () { + gml_pragma("forceinline"); + global.__bbmodCameraCurrent = self; + camera_apply(Raw); + bbmod_camera_set_position(Position.Clone()); + bbmod_camera_set_zfar(ZFar); + bbmod_camera_set_exposure(Exposure); + return self; + }; + + static destroy = function () { + Class_destroy(); + camera_destroy(Raw); + if (global.__bbmodCameraCurrent == self) + { + global.__bbmodCameraCurrent = undefined; + } + return undefined; + }; +} diff --git a/scripts/BBMOD_BaseCamera/BBMOD_BaseCamera.yy b/scripts/BBMOD_BaseCamera/BBMOD_BaseCamera.yy new file mode 100644 index 000000000..1e2ef8264 --- /dev/null +++ b/scripts/BBMOD_BaseCamera/BBMOD_BaseCamera.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_BaseCamera", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Camera", + "path": "folders/_Extensions/BBMOD/Core/Camera.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_BaseMaterial/BBMOD_BaseMaterial.gml b/scripts/BBMOD_BaseMaterial/BBMOD_BaseMaterial.gml new file mode 100644 index 000000000..8da483400 --- /dev/null +++ b/scripts/BBMOD_BaseMaterial/BBMOD_BaseMaterial.gml @@ -0,0 +1,121 @@ +/// @func BBMOD_BaseMaterial([_shader]) +/// +/// @extends BBMOD_Material +/// +/// @desc A material that can be used when rendering models. +/// +/// @param {Struct.BBMOD_Shader} [_shader] A shader that the material uses in +/// the {@link BBMOD_ERenderPass.Forward} pass. Leave `undefined` if you would +/// like to use {@link BBMOD_Material.set_shader} to specify shaders used in +/// specific render passes. +/// +/// @see BBMOD_Shader +function BBMOD_BaseMaterial(_shader=undefined) + : BBMOD_Material(_shader) constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Material_copy = copy; + static Material_to_json = to_json; + static Material_from_json = from_json; + + /// @var {Struct.BBMOD_Color} Multiplier for {@link BBMOD_Material.BaseOpacity}. + /// Default value is {@link BBMOD_C_WHITE}. + BaseOpacityMultiplier = BBMOD_C_WHITE; + + /// @var {Struct.BBMOD_Vec2} An offset of texture UV coordinates. Defaults + /// to `(0, 0)`. Using this you can control texture's position within texture + /// page. + TextureOffset = new BBMOD_Vec2(0.0); + + /// @var {Struct.BBMOD_Vec2} A scale of texture UV coordinates. Defaults to + /// `(1, 1)`. + /// Using this you can control texture's size within texture page. + TextureScale = new BBMOD_Vec2(1.0); + + /// @var {Real} Controls range over which the mesh smoothly transitions into + /// shadow. This can be useful for example for billboarded particles, where + /// harsh transition does not look good. Default value is 0, which means no + /// smooth transition. + ShadowmapBias = 0.0; + + static copy = function (_dest) { + Material_copy(_dest); + BaseOpacityMultiplier.Copy(_dest.BaseOpacityMultiplier); + _dest.TextureOffset = TextureOffset.Clone(); + _dest.TextureScale = TextureScale.Clone(); + _dest.ShadowmapBias = ShadowmapBias; + return self; + }; + + static clone = function () { + var _clone = new BBMOD_BaseMaterial(); + copy(_clone); + return _clone; + }; + + static to_json = function (_json) { + Material_to_json(_json); + + _json.BaseOpacityMultiplier = { + Red: BaseOpacityMultiplier.Red, + Green: BaseOpacityMultiplier.Green, + Blue: BaseOpacityMultiplier.Blue, + Alpha: BaseOpacityMultiplier.Alpha, + }; + + _json.TextureOffset = { + X: TextureOffset.X, + Y: TextureOffset.Y, + }; + + _json.TextureScale = { + X: TextureScale.X, + Y: TextureScale.Y, + }; + + _json.ShadowmapBias = ShadowmapBias; + + return self; + }; + + static from_json = function (_json) { + Material_from_json(_json); + + var _baseOpacityMultiplier = _json[$ "BaseOpacityMultiplier"]; + if (_baseOpacityMultiplier != undefined) + { + BaseOpacityMultiplier = new BBMOD_Color( + _baseOpacityMultiplier.Red, + _baseOpacityMultiplier.Green, + _baseOpacityMultiplier.Blue, + _baseOpacityMultiplier.Alpha + ); + } + + var _textureOffset = _json[$ "TextureOffset"]; + if (_textureOffset != undefined) + { + TextureOffset = new BBMOD_Vec2( + _textureOffset.X, + _textureOffset.Y + ); + } + + var _textureScale = _json[$ "TextureScale"]; + if (_textureScale != undefined) + { + TextureScale = new BBMOD_Vec2( + _textureScale.X, + _textureScale.Y + ); + } + + if (variable_struct_exists(_json, "ShadowmapBias")) + { + ShadowmapBias = _json.ShadowmapBias; + } + + return self; + }; +} diff --git a/scripts/BBMOD_BaseMaterial/BBMOD_BaseMaterial.yy b/scripts/BBMOD_BaseMaterial/BBMOD_BaseMaterial.yy new file mode 100644 index 000000000..0c0d0e797 --- /dev/null +++ b/scripts/BBMOD_BaseMaterial/BBMOD_BaseMaterial.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_BaseMaterial", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_BaseRenderer/BBMOD_BaseRenderer.gml b/scripts/BBMOD_BaseRenderer/BBMOD_BaseRenderer.gml new file mode 100644 index 000000000..835ec3b59 --- /dev/null +++ b/scripts/BBMOD_BaseRenderer/BBMOD_BaseRenderer.gml @@ -0,0 +1,864 @@ +/// @func BBMOD_BaseRenderer() +/// +/// @extends BBMOD_Class +/// +/// @desc Base struct for renderers, which execute +/// [render commands](./BBMOD_RenderCommand.html) created with method +/// [render](./BBMOD_Model.render.html). +/// +/// @see BBMOD_DefaultRenderer +function BBMOD_BaseRenderer() + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Real} The X position of the renderer on the screen. Default value + /// is 0. + X = 0; + + /// @var {Real} The Y position of the renderer on the screen. Default value + /// is 0. + Y = 0; + + /// @var {Real} The width of the renderer on the screen. If `undefined` then + /// the window width is used. Default value is `undefined`. + Width = undefined; + + /// @var {Real} The height of the renderer on the screen. If `undefined` + /// then the window height is used. Default value is `undefined`. + Height = undefined; + + /// @var {Bool} If `true` then rendering of instance IDs into an off-screen + /// surface is enabled. This must be enabled if you would like to use method + /// {@link BBMOD_BaseRenderer.get_instance_id} for mouse-picking instances. + /// Default value is `false`. + RenderInstanceIDs = false; + + /// @var {Id.Surface} Surface for rendering highlight of selected instances. + /// @private + __surInstanceHighlight = noone; + + /// @var {Struct.BBMOD_Color} Outline color of instances selected by gizmo. + /// Default value is {@link BBMOD_C_ORANGE}. + /// @see BBMOD_BaseRenderer.Gizmo + InstanceHighlightColor = BBMOD_C_ORANGE; + + /// @var {Bool} If `true` then edit mode is enabled. Default value is `false`. + EditMode = false; + + /// @var {Bool} If `true` then mousepicking of gizmo and instances is enabled. + /// Default value is `true`. + /// @note This can be useful for example to disable mousepicking when the + /// mouse cursor is over UI. + EnableMousepick = true; + + /// @var {Constant.MouseButton} The mouse button used to select instances when + /// edit mode is enabled. Default value is `mb_left`. + /// @see BBMOD_BaseRenderer.EditMode + ButtonSelect = mb_left; + + /// @var {Constant.VirtualKey} The keyboard key used to add/remove instances + /// from multiple selection when edit mode is enabled. Default value is + /// `vk_shift`. + /// @see BBMOD_BaseRenderer.EditMode + KeyMultiSelect = vk_shift; + + /// @var {Struct.BBMOD_Gizmo} A gizmo for transforming instances when + /// {@link BBMOD_BaseRenderer.EditMode} is enabled. This is by default `undefined`. + /// @see BBMOD_Gizmo + Gizmo = undefined; + + /// @var {Id.Surface} A surface containing the gizmo. Used to enable + /// z-testing against itself, but ingoring the scene geometry. + /// @private + __surGizmo = noone; + + /// @var {Id.Surface} Surface for mouse-picking the gizmo. + /// @private + __surSelect = noone; + + /// @var {Array} An array of renderable objects + /// and structs. These are automatically rendered in + /// {@link BBMOD_BaseRenderer.render}. + /// @readonly + /// @see BBMOD_BaseRenderer.add + /// @see BBMOD_BaseRenderer.remove + /// @see BBMOD_IRenderable + Renderables = []; + + /// @var {Bool} Set to `true` to enable the `application_surface`. + /// Use method {@link BBMOD_BaseRenderer.present} to draw the + /// `application_surface` to the screen. Defaults to `false`. + UseAppSurface = false; + + /// @var {Real} Resolution multiplier for the `application_surface`. + /// {@link BBMOD_BaseRenderer.UseAppSurface} must be enabled for this to + /// have any effect. Defaults to 1. Use lower values to improve framerate. + RenderScale = 1.0; + + /// @var {Bool} Enables rendering into a shadowmap in the shadows render pass. + /// Defauls to `false`. + /// @see BBMOD_BaseRenderer.ShadowmapArea + /// @see BBMOD_BaseRenderer.ShadowmapResolution + EnableShadows = false; + + /// @var {Id.Surface} The surface used for rendering the scene's depth from the + /// directional light's view. + /// @private + __surShadowmap = noone; + + /// @var {Real} The area captured by the shadowmap. Defaults to 1024. + /// @obsolete This has been replaced with {@link BBMOD_DirectionalLight.ShadowmapArea}. + ShadowmapArea = 1024. + + /// @var {Real} The resolution of the shadowmap surface. Must be power of 2. + /// Defaults to 4096. + /// @obsolete This has been replaced with {@link BBMOD_Light.ShadowmapResolution}. + ShadowmapResolution = 4096; + + /// @var {Real} When rendering shadows, offsets vertex position by its normal + /// scaled by this value. Defaults to 1. Increasing the value can remove some + /// artifacts but using too high value could make the objects appear flying + /// above the ground. + ShadowmapNormalOffset = 1; + + /// @var {Bool} Enables post-processing effects. Defaults to `false`. Enabling + /// this requires the [Post-processing submodule](./PostProcessingSubmodule.html)! + /// + /// @note {@link BBMOD_BaseRenderer.UseAppSurface} must be enabled for this to + /// have any effect! + /// + /// @obsolete Post-processing is now handled through + /// {@link BBMOD_BaseRenderer.PostProcessor}! + EnablePostProcessing = false; + + /// @var {Struct.BBMOD_PostProcessor} Handles post-processing effects if + /// isn't `undefined` and {@link BBMOD_BaseRenderer.UseAppSurface} is enabled. + /// Default value is `undefined`. + /// @see BBMOD_PostProcessor + PostProcessor = undefined; + + /// @var {Id.Surface} + /// @private + __surFinal = noone; + + /// @func get_width() + /// + /// @desc Retrieves the width of the renderer on the screen. + /// + /// @return {Real} The width of the renderer on the screen. + static get_width = function () { + gml_pragma("forceinline"); + return max((Width == undefined) ? window_get_width() : Width, 1); + }; + + /// @func get_height() + /// + /// @desc Retrieves the height of the renderer on the screen. + /// + /// @return {Real} The height of the renderer on the screen. + static get_height = function () { + gml_pragma("forceinline"); + return max((Height == undefined) ? window_get_height() : Height, 1); + }; + + /// @func get_render_width() + /// + /// @desc Retrieves the width of the renderer with + /// + /// {@link BBMOD_BaseRenderer.RenderScale} applied. + /// + /// @return {Real} The width of the renderer after `RenderScale` is applied. + static get_render_width = function () { + gml_pragma("forceinline"); + return max(get_width() * RenderScale, 1); + }; + + /// @func get_render_height() + /// + /// @desc Retrieves the height of the renderer with + /// {@link BBMOD_BaseRenderer.RenderScale} applied. + /// + /// @return {Real} The height of the renderer after `RenderScale` is applied. + static get_render_height = function () { + gml_pragma("forceinline"); + return max(get_height() * RenderScale, 1); + }; + + /// @func set_position(_x, _y) + /// + /// @desc Changes the renderer's position on the screen. + /// + /// @param {Real} _x The new X position on the screen. + /// @param {Real} _y The new Y position on the screen. + /// + /// @return {Struct.BBMOD_BaseRenderer} Returns `self`. + static set_position = function (_x, _y) { + gml_pragma("forceinline"); + X = _x; + Y = _y; + return self; + }; + + /// @func set_size(_width, _height) + /// + /// @desc Changes the renderer's size on the screen. + /// + /// @param {Real} _width The new width on the screen. + /// @param {Real} _height The new height on the screen. + /// + /// @return {Struct.BBMOD_BaseRenderer} Returns `self`. + static set_size = function (_width, _height) { + gml_pragma("forceinline"); + Width = _width; + Height = _height; + return self; + }; + + /// @func set_rectangle(_x, _y, _width, _height) + /// + /// @desc Changes the renderer's position and size on the screen. + /// + /// @param {Real} _x The new X position on the screen. + /// @param {Real} _y The new Y position on the screen. + /// @param {Real} _width The new width on the screen. + /// @param {Real} _height The new height on the screen. + /// + /// @return {Struct.BBMOD_BaseRenderer} Returns `self`. + static set_rectangle = function (_x, _y, _width, _height) { + gml_pragma("forceinline"); + set_position(_x, _y); + set_size(_width, _height); + return self; + }; + + /// @func select_gizmo(_screenX, _screenY) + /// + /// @desc Tries to select a gizmo at given screen coordinates and + /// automatically changes its {@link BBMOD_Gizmo.EditAxis} and + /// {@link BBMOD_Gizmo.EditType} based on which part of the gizmo + /// was selected. + /// + /// @param {Real} _screenX The X position on the screen. + /// @param {Real} _screenY The Y position on the screen. + /// + /// @return {Bool} Returns `true` if the gizmo was selected. + /// + /// @note {@link BBMOD_BaseRenderer.Gizmo} must be defined. + /// + /// @private + static select_gizmo = function (_screenX, _screenY) { + _screenX = clamp(_screenX - X, 0, get_width()) * RenderScale; + _screenY = clamp(_screenY - Y, 0, get_height()) * RenderScale; + + Gizmo.EditAxis = BBMOD_EEditAxis.None; + + var _pixel = surface_getpixel_ext(__surSelect, _screenX, _screenY); + if (_pixel & $FF000000 == 0) + { + return false; + } + + var _blue = (_pixel >> 16) & 255; + var _green = (_pixel >> 8) & 255; + var _red = _pixel & 255; + var _value = max(_red, _green, _blue); + + Gizmo.EditAxis = 0 + | (BBMOD_EEditAxis.X * (_red > 0)) + | (BBMOD_EEditAxis.Y * (_green > 0)) + | (BBMOD_EEditAxis.Z * (_blue > 0)); + + Gizmo.EditType = ((_value == 255) ? BBMOD_EEditType.Position + : ((_value == 128) ? BBMOD_EEditType.Rotation + : BBMOD_EEditType.Scale)); + + return true; + }; + + /// @func get_instance_id(_screenX, _screenY) + /// + /// @desc Retrieves an ID of an instance at given position on the screen. + /// + /// @param {Real} _screenX The X position on the screen. + /// @param {Real} _screenY The Y position on the screen. + /// + /// @return {Id.Instance} The ID of the instance or 0 if no instance was + /// found at the given position. + /// + /// @note {@link BBMOD_BaseRenderer.RenderInstanceIDs} must be enabled. + static get_instance_id = function (_screenX, _screenY) { + gml_pragma("forceinline"); + if (!surface_exists(__surSelect)) + { + return 0; + } + _screenX = clamp(_screenX - X, 0, get_width()) * RenderScale; + _screenY = clamp(_screenY - Y, 0, get_height()) * RenderScale; + return surface_getpixel_ext(__surSelect, _screenX, _screenY); + }; + + /// @func add(_renderable) + /// + /// @desc Adds a renderable object or struct to the renderer. + /// + /// @param {Struct.BBMOD_IRenderable} _renderable The renderable object or + /// struct to add. + /// + /// @return {Struct.BBMOD_BaseRenderer} Returns `self`. + /// + /// @see BBMOD_BaseRenderer.remove + /// @see BBMOD_IRenderable + static add = function (_renderable) { + gml_pragma("forceinline"); + array_push(Renderables, _renderable); + return self; + }; + + /// @func remove(_renderable) + /// + /// @desc Removes a renderable object or a struct from the renderer. + /// + /// @param {Struct.BBMOD_IRenderable} _renderable The renderable object or + /// struct to remove. + /// + /// @return {Struct.BBMOD_BaseRenderer} Returns `self`. + /// + /// @see BBMOD_BaseRenderer.add + /// @see BBMOD_IRenderable + static remove = function (_renderable) { + gml_pragma("forceinline"); + for (var i = array_length(Renderables) - 1; i >= 0; --i) + { + if (Renderables[i] == _renderable) + { + array_delete(Renderables, i, 1); + } + } + return self; + }; + + /// @func update(_deltaTime) + /// + /// @desc Updates the renderer. This should be called in the Step event. + /// + /// @param {Real} _deltaTime How much time has passed since the last frame + /// (in microseconds). + /// + /// @return {Struct.BBMOD_BaseRenderer} Returns `self`. + static update = function (_deltaTime) { + global.__bbmodRendererCurrent = self; + + if (UseAppSurface) + { + application_surface_enable(true); + application_surface_draw_enable(false); + + var _surfaceWidth = get_render_width(); + var _surfaceHeight = get_render_height(); + + if (surface_exists(application_surface) + && (surface_get_width(application_surface) != _surfaceWidth + || surface_get_height(application_surface) != _surfaceHeight)) + { + surface_resize(application_surface, _surfaceWidth, _surfaceHeight); + } + } + + if (Gizmo && EditMode) + { + Gizmo.update(delta_time); + } + + return self; + }; + + /// @func __render_shadowmap() + /// + /// @desc Renders a shadowmap. + /// + /// @note This modifies render pass and view and projection matrices and + /// for optimization reasons it does not reset them back! Make sure to do + /// that yourself in the calling function if needed. + /// + /// @private + static __render_shadowmap = function () { + gml_pragma("forceinline"); + + static _renderQueues = bbmod_render_queues_get(); + + var _shadowCaster = undefined; + var _shadowCasterIndex = -1; + var _shadowmapMatrix; + var _shadowmapZFar; + + if (EnableShadows) + { + var _light = bbmod_light_directional_get(); + if (_light != undefined + && _light.CastShadows + && _light.__getZFar != undefined + && _light.__getShadowmapMatrix != undefined) + { + // Directional light + _shadowCaster = _light; + _shadowmapMatrix = _light.__getShadowmapMatrix(); + _shadowmapZFar = _light.__getZFar(); + } + else + { + // Punctual lights + var i = 0; + repeat (array_length(global.__bbmodPunctualLights)) + { + var _light = global.__bbmodPunctualLights[i]; + if (_light.CastShadows + && _light.__getZFar != undefined + && _light.__getShadowmapMatrix != undefined) + { + _shadowCaster = _light; + _shadowCasterIndex = i; + _shadowmapMatrix = _light.__getShadowmapMatrix(); + _shadowmapZFar = _light.__getZFar(); + break; + } + ++i; + } + } + } + + if (_shadowCaster == undefined) + { + if (surface_exists(__surShadowmap)) + { + surface_free(__surShadowmap); + __surShadowmap = noone; + } + // No shadow caster was found!!! + bbmod_shader_unset_global("bbmod_Shadowmap"); + bbmod_shader_set_global_f("bbmod_ShadowmapEnableVS", 0.0); + bbmod_shader_set_global_f("bbmod_ShadowmapEnablePS", 0.0); + return; + } + + bbmod_render_pass_set(BBMOD_ERenderPass.Shadows); + + __surShadowmap = bbmod_surface_check( + __surShadowmap, _light.ShadowmapResolution, _light.ShadowmapResolution); + + surface_set_target(__surShadowmap); + draw_clear(c_red); + matrix_set(matrix_view, _light.__getViewMatrix()); + matrix_set(matrix_projection, _light.__getProjMatrix()); + bbmod_shader_set_global_f("bbmod_ZFar", _light.__getZFar()); + + var _rqi = 0; + repeat (array_length(_renderQueues)) + { + _renderQueues[_rqi++].submit(); + } + + surface_reset_target(); + + var _shadowmapTexture = surface_get_texture(__surShadowmap); + bbmod_shader_set_global_f("bbmod_ShadowmapEnableVS", 1.0); + bbmod_shader_set_global_f("bbmod_ShadowmapEnablePS", 1.0); + bbmod_shader_set_global_sampler("bbmod_Shadowmap", _shadowmapTexture); + bbmod_shader_set_global_sampler_mip_enable("bbmod_Shadowmap", true); + bbmod_shader_set_global_sampler_filter("bbmod_Shadowmap", true); + bbmod_shader_set_global_sampler_repeat("bbmod_Shadowmap", false); + bbmod_shader_set_global_f2("bbmod_ShadowmapTexel", + texture_get_texel_width(_shadowmapTexture), + texture_get_texel_height(_shadowmapTexture)); + bbmod_shader_set_global_f("bbmod_ShadowmapArea", _shadowmapZFar); + bbmod_shader_set_global_f("bbmod_ShadowmapNormalOffset", ShadowmapNormalOffset); + bbmod_shader_set_global_matrix_array("bbmod_ShadowmapMatrix", _shadowmapMatrix); + bbmod_shader_set_global_f("bbmod_ShadowCasterIndex", _shadowCasterIndex); + }; + + /// @func __render_gizmo_and_instance_ids() + /// + /// @desc Renders gizmo and instance IDs into dedicated surfaces. + /// + /// @private + static __render_gizmo_and_instance_ids = function () { + static _renderQueues = bbmod_render_queues_get(); + + var _view = matrix_get(matrix_view); + var _projection = matrix_get(matrix_projection); + var _renderWidth = get_render_width(); + var _renderHeight = get_render_height(); + + var _editMode = (EditMode && Gizmo); + var _mouseX = window_mouse_get_x(); + var _mouseY = window_mouse_get_y(); + var _mouseOver = (_mouseX >= X && _mouseX < X + get_width() + && _mouseY >= Y && _mouseY < Y + get_height()); + var _continueMousePick = EnableMousepick; + var _gizmoSize; + + if (_editMode) + { + _gizmoSize = Gizmo.Size; + + if (_projection[11] != 0.0) + { + Gizmo.Size = _gizmoSize + * Gizmo.Position.Sub(bbmod_camera_get_position()).Length() / 100.0; + } + } + + //////////////////////////////////////////////////////////////////////// + // Gizmo select + if (_editMode + && _continueMousePick + && _mouseOver + && mouse_check_button_pressed(Gizmo.ButtonDrag)) + { + __surSelect = bbmod_surface_check(__surSelect, _renderWidth, _renderHeight); + surface_set_target(__surSelect); + draw_clear_alpha(0, 0.0); + matrix_set(matrix_view, _view); + matrix_set(matrix_projection, _projection); + Gizmo.submit(Gizmo.MaterialsSelect); + surface_reset_target(); + + if (select_gizmo(_mouseX, _mouseY)) + { + Gizmo.IsEditing = true; + _continueMousePick = false; + } + } + + //////////////////////////////////////////////////////////////////////// + // Instance IDs + var _mousePickInstance = (_editMode && _continueMousePick + && _mouseOver && mouse_check_button_pressed(ButtonSelect)); + + if (_mousePickInstance || RenderInstanceIDs) + { + __surSelect = bbmod_surface_check(__surSelect, _renderWidth, _renderHeight); + + surface_set_target(__surSelect); + draw_clear_alpha(0, 0.0); + matrix_set(matrix_view, _view); + matrix_set(matrix_projection, _projection); + + bbmod_render_pass_set(BBMOD_ERenderPass.Id); + + var _rqi = 0; + repeat (array_length(_renderQueues)) + { + _renderQueues[_rqi++].submit(); + } + + surface_reset_target(); + + // Select instance + if (_mousePickInstance) + { + if (!keyboard_check(KeyMultiSelect)) + { + Gizmo.clear_selection(); + } + + var _id = get_instance_id(_mouseX, _mouseY); + if (_id != 0) + { + Gizmo.toggle_select(_id).update_position(); + Gizmo.Size = _gizmoSize + * Gizmo.Position.Sub(bbmod_camera_get_position()).Length() / 100.0; + } + } + } + + if (_editMode && !ds_list_empty(Gizmo.Selected)) + { + //////////////////////////////////////////////////////////////////// + // Instance highlight + __surInstanceHighlight = bbmod_surface_check( + __surInstanceHighlight, _renderWidth, _renderHeight); + + surface_set_target(__surInstanceHighlight); + draw_clear_alpha(0, 0.0); + + matrix_set(matrix_view, _view); + matrix_set(matrix_projection, _projection); + + bbmod_render_pass_set(BBMOD_ERenderPass.Id); + + var _selectedInstances = Gizmo.Selected; + var _rqi = 0; + repeat (array_length(_renderQueues)) + { + _renderQueues[_rqi++].submit(_selectedInstances); + } + + surface_reset_target(); + + //////////////////////////////////////////////////////////////////// + // Gizmo + bbmod_render_pass_set(BBMOD_ERenderPass.Forward); + + __surGizmo = bbmod_surface_check(__surGizmo, _renderWidth, _renderHeight); + surface_set_target(__surGizmo); + draw_clear_alpha(0, 0.0); + matrix_set(matrix_view, _view); + matrix_set(matrix_projection, _projection); + Gizmo.submit(); + surface_reset_target(); + } + + if (_editMode) + { + Gizmo.Size = _gizmoSize; + } + }; + + /// @func render(_clearQueues=true) + /// + /// @desc Renders all added [renderables](./BBMOD_BaseRenderer.Renderables.html) + /// to the current render target. + /// + /// @param {Bool} [_clearQueues] If true then all render queues are cleared + /// at the end of this method. Default value is `true`. + /// + /// @return {Struct.BBMOD_BaseRenderer} Returns `self`. + static render = function (_clearQueues=true) { + global.__bbmodRendererCurrent = self; + + static _renderQueues = bbmod_render_queues_get(); + + var _world = matrix_get(matrix_world); + var _view = matrix_get(matrix_view); + var _projection = matrix_get(matrix_projection); + + var i = 0; + repeat (array_length(Renderables)) + { + with (Renderables[i++]) + { + render(); + } + } + + bbmod_material_reset(); + + //////////////////////////////////////////////////////////////////////// + // + // Edit mode + // + __render_gizmo_and_instance_ids(); + + //////////////////////////////////////////////////////////////////////// + // + // Shadow map + // + __render_shadowmap(); + + //////////////////////////////////////////////////////////////////////// + // + // Forward pass + // + bbmod_shader_set_global_f("bbmod_ZFar", bbmod_camera_get_zfar()); + + matrix_set(matrix_view, _view); + matrix_set(matrix_projection, _projection); + + bbmod_render_pass_set(BBMOD_ERenderPass.Forward); + + var _rqi = 0; + repeat (array_length(_renderQueues)) + { + var _queue = _renderQueues[_rqi++].submit(); + if (_clearQueues) + { + _queue.clear(); + } + } + + // Unset in case it gets destroyed when the room changes etc. + bbmod_shader_unset_global("bbmod_Shadowmap"); + + bbmod_material_reset(); + + matrix_set(matrix_world, _world); + return self; + }; + + /// @func present() + /// + /// @desc Presents the rendered graphics on the screen, with post-processing + /// applied (if {@link BBMOD_BaseRenderer.PostProcessor} is defined). + /// + /// @return {Struct.BBMOD_BaseRenderer} Returns `self`. + /// + /// @note If {@link BBMOD_BaseRenderer.UseAppSurface} is `false`, then this only + /// draws the gizmo and selected instances. + static present = function () { + global.__bbmodRendererCurrent = self; + + static _gpuState = undefined; + if (_gpuState == undefined) + { + gpu_push_state(); + gpu_set_state(bbmod_gpu_get_default_state()); + gpu_set_tex_filter(true); + gpu_set_tex_repeat(false); + gpu_set_blendenable(true); + _gpuState = gpu_get_state(); + gpu_pop_state(); + } + + var _width = get_width(); + var _height = get_height(); + var _texelWidth = 1.0 / _width; + var _texelHeight = 1.0 / _height; + + if (!UseAppSurface // Can't use post-processing even if it was defined + || (PostProcessor == undefined || !PostProcessor.Enabled)) + { + //////////////////////////////////////////////////////////////////// + // + // Post-processing DISABLED + // + gpu_push_state(); + gpu_set_state(_gpuState); + + if (UseAppSurface) + { + gpu_set_blendenable(false); + draw_surface_stretched(application_surface, X, Y, _width, _height); + gpu_set_blendenable(true); + } + + if (EditMode && Gizmo && !ds_list_empty(Gizmo.Selected)) + { + //////////////////////////////////////////////////////////////// + // Highlighted instances + if (!ds_list_empty(Gizmo.Selected) + && surface_exists(__surInstanceHighlight)) + { + var _shader = BBMOD_ShInstanceHighlight; + shader_set(_shader); + shader_set_uniform_f(shader_get_uniform(_shader, "u_vTexel"), + _texelWidth, _texelHeight); + shader_set_uniform_f(shader_get_uniform(_shader, "u_vColor"), + InstanceHighlightColor.Red / 255.0, + InstanceHighlightColor.Green / 255.0, + InstanceHighlightColor.Blue / 255.0, + InstanceHighlightColor.Alpha); + draw_surface_stretched(__surInstanceHighlight, X, Y, _width, _height); + shader_reset(); + } + + //////////////////////////////////////////////////////////////// + // Gizmo + if (surface_exists(__surGizmo)) + { + draw_surface_stretched(__surGizmo, X, Y, _width, _height); + } + } + + gpu_pop_state(); + } + else + { + //////////////////////////////////////////////////////////////////// + // + // Post-processing ENABLED + // + var _world = matrix_get(matrix_world); + + __surFinal = bbmod_surface_check(__surFinal, _width, _height); + + gpu_push_state(); + gpu_set_state(_gpuState); + + surface_set_target(__surFinal); + matrix_set(matrix_world, matrix_build_identity()); + + draw_surface_stretched(application_surface, 0, 0, _width, _height); + + if (EditMode && Gizmo && !ds_list_empty(Gizmo.Selected)) + { + //////////////////////////////////////////////////////////////// + // Highlighted instances + if (!ds_list_empty(Gizmo.Selected) + && surface_exists(__surInstanceHighlight)) + { + var _shader = BBMOD_ShInstanceHighlight; + shader_set(_shader); + shader_set_uniform_f(shader_get_uniform(_shader, "u_vTexel"), + _texelWidth, _texelHeight); + shader_set_uniform_f(shader_get_uniform(_shader, "u_vColor"), + InstanceHighlightColor.Red / 255.0, + InstanceHighlightColor.Green / 255.0, + InstanceHighlightColor.Blue / 255.0, + InstanceHighlightColor.Alpha); + draw_surface_stretched(__surInstanceHighlight, 0, 0, _width, _height); + shader_reset(); + } + + //////////////////////////////////////////////////////////////// + // Gizmo + if (surface_exists(__surGizmo)) + { + draw_surface_stretched(__surGizmo, 0, 0, _width, _height); + } + } + + surface_reset_target(); + matrix_set(matrix_world, _world); + gpu_pop_state(); + + PostProcessor.draw(__surFinal, X, Y); + } + + return self; + }; + + static destroy = function () { + Class_destroy(); + + if (global.__bbmodRendererCurrent == self) + { + global.__bbmodRendererCurrent = undefined; + } + + if (surface_exists(__surSelect)) + { + surface_free(__surSelect); + } + + if (surface_exists(__surInstanceHighlight)) + { + surface_free(__surInstanceHighlight); + } + + if (surface_exists(__surGizmo)) + { + surface_free(__surGizmo); + } + + if (surface_exists(__surShadowmap)) + { + surface_free(__surShadowmap); + } + + if (surface_exists(__surFinal)) + { + surface_free(__surFinal); + } + + if (UseAppSurface) + { + application_surface_enable(false); + application_surface_draw_enable(true); + } + + return undefined; + }; +} diff --git a/scripts/BBMOD_BaseRenderer/BBMOD_BaseRenderer.yy b/scripts/BBMOD_BaseRenderer/BBMOD_BaseRenderer.yy new file mode 100644 index 000000000..2fe75cea0 --- /dev/null +++ b/scripts/BBMOD_BaseRenderer/BBMOD_BaseRenderer.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_BaseRenderer", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_BaseShader/BBMOD_BaseShader.gml b/scripts/BBMOD_BaseShader/BBMOD_BaseShader.gml new file mode 100644 index 000000000..01539aa6d --- /dev/null +++ b/scripts/BBMOD_BaseShader/BBMOD_BaseShader.gml @@ -0,0 +1,443 @@ +/// @func BBMOD_BaseShader(_shader, _vertexFormat) +/// +/// @extends BBMOD_Shader +/// +/// @desc Base class for BBMOD shaders. +/// +/// @param {Asset.GMShader} _shader The shader resource. +/// @param {Struct.BBMOD_VertexFormat} _vertexFormat The vertex format required +/// by the shader. +/// +/// @see BBMOD_VertexFormat +function BBMOD_BaseShader(_shader, _vertexFormat) + : BBMOD_Shader(_shader, _vertexFormat) constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} Maximum number of point lights in the shader. This must + /// match with value defined in the raw GameMaker shader! + /// @obsolete This was replaced with {@link BBMOD_BaseShader.MaxPunctualLights}! + MaxPointLights = 8; + + /// @var {Real} Maximum number of punctual lights in the shader. This must + /// match with value defined in the raw GameMaker shader! + MaxPunctualLights = 8; + + /// @func set_texture_offset(_offset) + /// + /// @desc Sets the `bbmod_TextureOffset` uniform to the given offset. + /// + /// @param {Struct.BBMOD_Vec2} _offset The texture offset. + /// + /// @return {Struct.BBMOD_Shader} Returns `self`. + static set_texture_offset = function (_offset) { + gml_pragma("forceinline"); + shader_set_uniform_f( + shader_get_uniform(shader_current(), "bbmod_TextureOffset"), + _offset.X, _offset.Y); + return self; + }; + + /// @func set_texture_scale(_scale) + /// + /// @desc Sets the `bbmod_TextureScale` uniform to the given scale. + /// + /// @param {Struct.BBMOD_Vec2} _scale The texture scale. + /// + /// @return {Struct.BBMOD_Shader} Returns `self`. + static set_texture_scale = function (_scale) { + gml_pragma("forceinline"); + shader_set_uniform_f( + shader_get_uniform(shader_current(), "bbmod_TextureScale"), + _scale.X, _scale.Y); + return self; + }; + + /// @func set_bones(_bones) + /// + /// @desc Sets the `bbmod_Bones` uniform. + /// + /// @param {Array} _bones The array of bone transforms. + /// + /// @return {Struct.BBMOD_Shader} Returns `self`. + /// + /// @see BBMOD_AnimationPlayer.get_transform + static set_bones = function (_bones) { + gml_pragma("forceinline"); + shader_set_uniform_f_array( + shader_get_uniform(shader_current(), "bbmod_Bones"), + _bones); + return self; + }; + + /// @func set_batch_data(_data) + /// + /// @desc Sets the `bbmod_BatchData` uniform. + /// + /// @param {Array} _data The dynamic batch data. + /// + /// @return {Struct.BBMOD_Shader} Returns `self`. + static set_batch_data = function (_data) { + gml_pragma("forceinline"); + shader_set_uniform_f_array( + shader_get_uniform(shader_current(), "bbmod_BatchData"), + _data); + return self; + }; + + /// @func set_alpha_test(_value) + /// + /// @desc Sets the `bbmod_AlphaTest` uniform. + /// + /// @param {Real} _value The alpha test value. + /// + /// @return {Struct.BBMOD_Shader} Returns `self`. + static set_alpha_test = function (_value) { + gml_pragma("forceinline"); + shader_set_uniform_f( + shader_get_uniform(shader_current(), "bbmod_AlphaTest"), + _value); + return self; + }; + + /// @func set_cam_pos([_pos]) + /// + /// @desc Sets a fragment shader uniform `bbmod_CamPos` to the given position. + /// + /// @param {Struct.BBMOD_Vec3} [_pos] The camera position. If `undefined`, + /// then the value set by {@link bbmod_camera_set_position} is used. + /// + /// @return {Struct.BBMOD_Shader} Returns `self`. + static set_cam_pos = function (_pos=undefined) { + gml_pragma("forceinline"); + _pos ??= global.__bbmodCameraPosition; + shader_set_uniform_f( + shader_get_uniform(shader_current(), "bbmod_CamPos"), + _pos.X, _pos.Y, _pos.Z); + return self; + }; + + /// @func set_exposure([_value]) + /// + /// @desc Sets the `bbmod_Exposure` uniform. + /// + /// @param {Real} [_value] The camera exposure. If `undefined`, + /// then the value set by {@link bbmod_camera_set_exposure} is used. + /// + /// @return {Struct.BBMOD_BaseShader} Returns `self`. + static set_exposure = function (_value=undefined) { + gml_pragma("forceinline"); + shader_set_uniform_f( + shader_get_uniform(shader_current(), "bbmod_Exposure"), + _value ?? global.__bbmodCameraExposure); + return self; + }; + + /// @func set_instance_id([_id]) + /// + /// @desc Sets the `bbmod_InstanceID` uniform. + /// + /// @param {Id.Instance} [_id] The instance ID. If `undefined`, + /// then the value set by {@link bbmod_set_instance_id} is used. + /// + /// @return {Struct.BBMOD_BaseShader} Returns `self`. + static set_instance_id = function (_id=undefined) { + gml_pragma("forceinline"); + _id ??= global.__bbmodInstanceID; + shader_set_uniform_f( + shader_get_uniform(shader_current(), "bbmod_InstanceID"), + ((_id & $000000FF) >> 0) / 255, + ((_id & $0000FF00) >> 8) / 255, + ((_id & $00FF0000) >> 16) / 255, + ((_id & $FF000000) >> 24) / 255); + return self; + }; + + /// @func set_material_index(_index) + /// + /// @desc Sets the `bbmod_MaterialIndex` uniform. + /// + /// @param {Real} [_index] The index of the current material. + /// + /// @return {Struct.BBMOD_BaseShader} Returns `self`. + static set_material_index = function (_index) { + gml_pragma("forceinline"); + shader_set_uniform_f( + shader_get_uniform(shader_current(), "bbmod_MaterialIndex"), + _index); + return self; + }; + + /// @func set_ibl([_ibl]) + /// + /// @desc Sets a fragment shader uniform `bbmod_IBLTexel` and samplers + /// `bbmod_IBL` and `bbmod_BRDF`. These are required for image based + /// lighting. + /// + /// @param {Struct.BBMOD_ImageBasedLight} [_ibl] The image based light. + /// If `undefined`, then the value set by {@link bbmod_ibl_set} is used. If + /// the light is not enabled, then it is not passed. + /// + /// @return {Struct.BBMOD_BaseShader} Returns `self`. + static set_ibl = function (_ibl=undefined) { + gml_pragma("forceinline"); + + static _iblNull = sprite_get_texture(BBMOD_SprBlack, 0); + var _texture = _iblNull; + var _texel = 0.0; + + _ibl ??= global.__bbmodImageBasedLight; + if (_ibl != undefined && _ibl.Enabled) + { + _texture = _ibl.Texture; + _texel = _ibl.Texel; + } + + var _shaderCurrent = shader_current(); + var _uIBL = shader_get_sampler_index(_shaderCurrent, "bbmod_IBL"); + + texture_set_stage(_uIBL, _texture); + gpu_set_tex_mip_enable_ext(_uIBL, mip_off) + gpu_set_tex_filter_ext(_uIBL, true); + gpu_set_tex_repeat_ext(_uIBL, false); + shader_set_uniform_f( + shader_get_uniform(_shaderCurrent, "bbmod_IBLTexel"), + _texel, _texel); + + return self; + }; + + /// @func set_ambient_light([_up[, _down]]) + /// + /// @desc Sets the `bbmod_LightAmbientUp`, `bbmod_LightAmbientDown` uniforms. + /// + /// @param {Struct.BBMOD_Color} [_up] RGBM encoded ambient light color on + /// the upper hemisphere. If `undefined`, then the value set by + /// {@link bbmod_light_ambient_set_up} is used. + /// @param {Struct.BBMOD_Color} [_down] RGBM encoded ambient light color on + /// the lower hemisphere. If `undefined`, then the value set by + /// {@link bbmod_light_ambient_set_down} is used. + /// + /// @return {Struct.BBMOD_BaseShader} Returns `self`. + static set_ambient_light = function (_up=undefined, _down=undefined) { + gml_pragma("forceinline"); + _up ??= global.__bbmodAmbientLightUp; + _down ??= global.__bbmodAmbientLightDown; + var _shaderCurrent = shader_current(); + shader_set_uniform_f( + shader_get_uniform(_shaderCurrent, "bbmod_LightAmbientUp"), + _up.Red / 255.0, _up.Green / 255.0, _up.Blue / 255.0, _up.Alpha); + shader_set_uniform_f( + shader_get_uniform(_shaderCurrent, "bbmod_LightAmbientDown"), + _down.Red / 255.0, _down.Green / 255.0, _down.Blue / 255.0, _down.Alpha); + return self; + }; + + /// @func set_directional_light([_light]) + /// + /// @desc Sets uniforms `bbmod_LightDirectionalDir` and + /// `bbmod_LightDirectionalColor`. + /// + /// @param {Struct.BBMOD_DirectionalLight} [_light] The directional light. + /// If `undefined`, then the value set by {@link bbmod_light_directional_set} + /// is used. If the light is not enabled then it is not passed. + /// + /// @return {Struct.BBMOD_BaseShader} Returns `self`. + /// + /// @see BBMOD_DirectionalLight + static set_directional_light = function (_light=undefined) { + gml_pragma("forceinline"); + _light ??= global.__bbmodDirectionalLight; + var _shaderCurrent = shader_current(); + var _uLightDirectionalDir = shader_get_uniform(_shaderCurrent, "bbmod_LightDirectionalDir"); + var _uLightDirectionalColor = shader_get_uniform(_shaderCurrent, "bbmod_LightDirectionalColor"); + if (_light != undefined && _light.Enabled) + { + var _direction = _light.Direction; + shader_set_uniform_f(_uLightDirectionalDir, + _direction.X, _direction.Y, _direction.Z); + var _color = _light.Color; + shader_set_uniform_f(_uLightDirectionalColor, + _color.Red / 255.0, + _color.Green / 255.0, + _color.Blue / 255.0, + _color.Alpha); + } + else + { + shader_set_uniform_f(_uLightDirectionalDir, 0.0, 0.0, -1.0); + shader_set_uniform_f(_uLightDirectionalColor, 0.0, 0.0, 0.0, 0.0); + } + return self; + }; + + /// @func set_point_lights([_lights]) + /// + /// @desc Sets uniforms `bbmod_LightPunctualDataA` and + /// `bbmod_LightPunctualDataB`. + /// + /// @param {Array} [_lights] An array of point + /// lights. If `undefined`, then the lights defined using + /// {@link bbmod_light_point_add} are passed. Only enabled lights will be used! + /// + /// @return {Struct.BBMOD_BaseShader} Returns `self`. + /// + /// @deprecated Please use {@link set_punctual_lights} instead. + static set_point_lights = function (_lights=undefined) { + gml_pragma("forceinline"); + set_punctual_lights(_lights); + return self; + }; + + /// @func set_punctual_lights([_lights]) + /// + /// @desc Sets uniforms `bbmod_LightPunctualDataA` and + /// `bbmod_LightPunctualDataB`. + /// + /// @param {Array} [_lights] An array of punctual + /// lights. If `undefined`, then the lights defined using + /// {@link bbmod_light_punctual_add} are passed. Only enabled lights will be used! + /// + /// @return {Struct.BBMOD_BaseShader} Returns `self`. + static set_punctual_lights = function (_lights=undefined) { + gml_pragma("forceinline"); + + _lights ??= global.__bbmodPunctualLights; + + var _maxLights = MaxPunctualLights; + + var _indexA = 0; + var _indexMaxA = _maxLights * 8; + var _dataA = array_create(_indexMaxA, 0.0); + + var _indexB = 0; + var _indexMaxB = _maxLights * 6; + var _dataB = array_create(_indexMaxB, 0.0); + + var i = 0; + + repeat (array_length(_lights)) + { + var _light = _lights[i++]; + + if (_light.Enabled) + { + _light.Position.ToArray(_dataA, _indexA); + _dataA[@ _indexA + 3] = _light.Range; + var _color = _light.Color; + _dataA[@ _indexA + 4] = _color.Red / 255.0; + _dataA[@ _indexA + 5] = _color.Green / 255.0; + _dataA[@ _indexA + 6] = _color.Blue / 255.0; + _dataA[@ _indexA + 7] = _color.Alpha; + _indexA += 8; + + if (_light.is_instance(BBMOD_SpotLight)) // Ugh, but works! + { + _dataB[@ _indexB] = 1.0; // Is spot light + _dataB[@ _indexB + 1] = dcos(_light.AngleInner); + _dataB[@ _indexB + 2] = dcos(_light.AngleOuter); + _light.Direction.ToArray(_dataB, _indexB + 3); + } + _indexB += 6; + + if (_indexA >= _indexMaxA) + { + break; + } + } + } + + var _shaderCurrent = shader_current(); + + shader_set_uniform_f_array( + shader_get_uniform(_shaderCurrent, "bbmod_LightPunctualDataA"), + _dataA); + shader_set_uniform_f_array( + shader_get_uniform(_shaderCurrent, "bbmod_LightPunctualDataB"), + _dataB); + + return self; + }; + + /// @func set_fog([_color[, _intensity[, _start[, _end]]]]) + /// + /// @desc Sets uniforms `bbmod_FogColor`, `bbmod_FogIntensity`, + /// `bbmod_FogStart` and `bbmod_FogRcpRange`. + /// + /// @param {Struct.BBMOD_Color} [_color] The color of the fog. If `undefined`, + /// then the value set by {@link bbmod_fog_set_color} is used. + /// @param {Real} [_intensity] The fog intensity. If `undefined`, then the + /// value set by {@link bbmod_fog_set_intensity} is used. + /// @param {Real} [_start] The distance at which the fog starts. If + /// `undefined`, then the value set by {@link bbmod_fog_set_start} is used. + /// @param {Real} [_end] The distance at which the fog has maximum intensity. + /// If `undefined`, then the value set by {@link bbmod_fog_set_end} is used. + /// + /// @return {Struct.BBMOD_BaseShader} Returns `self`. + static set_fog = function (_color=undefined, _intensity=undefined, _start=undefined, _end=undefined) { + gml_pragma("forceinline"); + _color ??= global.__bbmodFogColor; + _intensity ??= global.__bbmodFogIntensity; + _start ??= global.__bbmodFogStart; + _end ??= global.__bbmodFogEnd; + var _rcpFogRange = 1.0 / (_end - _start); + var _shaderCurrent = shader_current(); + shader_set_uniform_f( + shader_get_uniform(_shaderCurrent, "bbmod_FogColor"), + _color.Red / 255.0, + _color.Green / 255.0, + _color.Blue / 255.0, + _color.Alpha); + shader_set_uniform_f( + shader_get_uniform(_shaderCurrent, "bbmod_FogIntensity"), + _intensity); + shader_set_uniform_f( + shader_get_uniform(_shaderCurrent, "bbmod_FogStart"), + _start); + shader_set_uniform_f( + shader_get_uniform(_shaderCurrent, "bbmod_FogRcpRange"), + _rcpFogRange); + return self; + }; + + static on_set = function () { + gml_pragma("forceinline"); + set_cam_pos(); + set_exposure(); + set_ibl(); + set_ambient_light(); + set_directional_light(); + set_punctual_lights(); + set_fog(); + texture_set_stage( + shader_get_sampler_index(shader_current(), "bbmod_SSAO"), + sprite_get_texture(BBMOD_SprWhite, 0)); + }; + + /// @func set_material(_material) + /// + /// @desc Sets shader uniforms using values from the material. + /// @param {Struct.BBMOD_BaseMaterial} _material The material to take the + /// values from. + /// + /// @return {Struct.BBMOD_BaseShader} Returns `self`. + /// + /// @see BBMOD_BaseMaterial + static set_material = function (_material) { + gml_pragma("forceinline"); + var _shaderCurrent = shader_current(); + shader_set_uniform_f( + shader_get_uniform(_shaderCurrent, "bbmod_BaseOpacityMultiplier"), + _material.BaseOpacityMultiplier.Red / 255.0, + _material.BaseOpacityMultiplier.Green / 255.0, + _material.BaseOpacityMultiplier.Blue / 255.0, + _material.BaseOpacityMultiplier.Alpha); + set_alpha_test(_material.AlphaTest); + set_texture_offset(_material.TextureOffset); + set_texture_scale(_material.TextureScale); + shader_set_uniform_f( + shader_get_uniform(_shaderCurrent, "bbmod_ShadowmapBias"), + _material.ShadowmapBias); + return self; + }; +} diff --git a/scripts/BBMOD_BaseShader/BBMOD_BaseShader.yy b/scripts/BBMOD_BaseShader/BBMOD_BaseShader.yy new file mode 100644 index 000000000..fbfab722f --- /dev/null +++ b/scripts/BBMOD_BaseShader/BBMOD_BaseShader.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_BaseShader", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Camera/BBMOD_Camera.gml b/scripts/BBMOD_Camera/BBMOD_Camera.gml new file mode 100644 index 000000000..11f52e78f --- /dev/null +++ b/scripts/BBMOD_Camera/BBMOD_Camera.gml @@ -0,0 +1,289 @@ +/// @func BBMOD_Camera() +/// +/// @extends BBMOD_BaseCamera +/// +/// @desc A camera driven by angles and an object to follor, rather than raw +/// vectors. Supports both first-person and third-person view and comes with +/// a mouselook implementation that also works in HTML5. +/// +/// @example +/// ```gml +/// // Create event +/// camera = new BBMOD_Camera(); +/// camera.FollowObject = OPlayer; +/// camera.Zoom = 0.0; // Use 0.0 for FPS, > 0.0 for TPS +/// +/// // End-Step event +/// camera.set_mouselook(true); +/// camera.update(delta_time); +/// +/// // Draw event +/// camera.apply(); +/// // Render scene here... +/// ``` +function BBMOD_Camera() + : BBMOD_BaseCamera() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Bool} If `true` then mouselook is enabled. Defaults to `false`. + /// @readonly + /// @see BBMOD_Camera.set_mouselook + MouseLook = false; + + /// @var {Real} Controls the mouselook sensitivity. Defaults to `1`. + MouseSensitivity = 1.0; + + /// @var {Struct.BBMOD_Vec2} The position on the screen where the cursor + /// is locked when {@link BBMOD_Camera.MouseLook} is `true`. Can be + /// `undefined`. + /// @private + __mouseLockAt = undefined; + + /// @var {Id.Instance} An id of an instance to follow or `undefined`. The + /// object must have a `z` variable (position on the z axis) defined! + /// Defaults to `undefined`. + FollowObject = undefined; + + /// @var {Bool} Used to determine change of the object to follow. + /// @private + __followObjectLast = undefined; + + /// @var {Function} A function which remaps value in range `0..1` to a + /// different `0..1` value. This is used to control the follow curve. + /// If `undefined` then `lerp` is used. Defaults to `undefined`. + FollowCurve = undefined; + + /// @var {Real} Controls lerp factor between the previous camera position + /// and the object it follows. Defaults to `1`, which means the camera is + /// immediately moved to its target position. + /// {@link BBMOD_Camera.FollowObject} must not be `undefined` for this to + /// have any effect. + FollowFactor = 1.0; + + /// @var {Struct.BBMOD_Vec3} The camera's offset from its target. Defaults to + /// `(0, 0, 0)`. + Offset = new BBMOD_Vec3(0.0); + + /// @var {Real} The camera's horizontal direction. Defaults to `0`. + Direction = 0.0; + + /// @var {Real} The camera's vertical direction. Automatically clamped + /// between {@link BBMOD_Camera.DirectionUpMin} and + /// {@link BBMOD_Camera.DirectionUpMax}. Defaults to `0`. + DirectionUp = 0.0; + + /// @var {Real} Minimum angle that {@link BBMOD_Camrea.DirectionUp} + /// can be. Use `undefined` to remove the limit. Default value is `-89`. + DirectionUpMin = -89.0; + + /// @var {Real} Maximum angle that {@link BBMOD_Camrea.DirectionUp} + /// can be. Use `undefined` to remove the limit. Default value is `89`. + DirectionUpMax = 89.0; + + /// @var {Real} The angle of camera's rotation from side to side. Default + /// value is `0`. + Roll = 0.0; + + /// @var {Real} The camera's distance from its target. Use `0` for a + /// first-person camera. Defaults to `0`. + Zoom = 0.0; + + static update_matrices = function () { + gml_pragma("forceinline"); + + var _forward = BBMOD_VEC3_FORWARD; + var _right = BBMOD_VEC3_RIGHT; + var _up = BBMOD_VEC3_UP; + + var _quatZ = new BBMOD_Quaternion().FromAxisAngle(_up, Direction); + _forward = _quatZ.Rotate(_forward); + _right = _quatZ.Rotate(_right); + _up = _quatZ.Rotate(_up); + + var _quatY = new BBMOD_Quaternion().FromAxisAngle(_right, DirectionUp); + _forward = _quatY.Rotate(_forward); + _right = _quatY.Rotate(_right); + _up = _quatY.Rotate(_up); + + var _quatX = new BBMOD_Quaternion().FromAxisAngle(_forward, Roll); + _forward = _quatX.Rotate(_forward); + _right = _quatX.Rotate(_right); + _up = _quatX.Rotate(_up); + + var _target = Position.Add(_forward); + + var _view = matrix_build_lookat( + Position.X, Position.Y, Position.Z, + _target.X, _target.Y, _target.Z, + _up.X, _up.Y, _up.Z); + camera_set_view_mat(Raw, _view); + + var _proj = __build_proj_mat(); + camera_set_proj_mat(Raw, _proj); + + // Note: Using _view and _proj mat straight away leads into a weird result... + ViewProjectionMatrix = matrix_multiply( + get_view_mat(), + get_proj_mat()); + + if (AudioListener) + { + audio_listener_position(Position.X, Position.Y, Position.Z); + audio_listener_orientation( + Target.X, Target.Y, Target.Z, + _up.X, _up.Y, _up.Z); + } + + Up = _up; + + return self; + } + + /// @func set_mouselook(_enable) + /// + /// @desc Enable/disable mouselook. This locks the mouse cursor at its + /// current position when enabled. + /// + /// @param {Bool} _enable USe `true` to enable mouselook. + /// + /// @return {Struct.BBMOD_Camera} Returns `self`. + static set_mouselook = function (_enable) { + if (_enable) + { + if (os_browser != browser_not_a_browser) + { + bbmod_html5_pointer_lock(); + } + + if (__mouseLockAt == undefined) + { + __mouseLockAt = new BBMOD_Vec2( + window_mouse_get_x(), + window_mouse_get_y()); + } + } + else + { + __mouseLockAt = undefined; + } + MouseLook = _enable; + return self; + }; + + /// @func update(_deltaTime[, _positionHandler]) + /// + /// @desc Handles mouselook, updates camera's position, matrices etc. + /// + /// @param {Real} _deltaTime How much time has passed since the last frame + /// (in microseconds). + /// @param {Function} [_positionHandler] A function which takes the camera's + /// position (@{link BBMOD_Vec3}) and returns a new position. This could be + /// used for example for camera collisions in a third-person game. Defaults + /// to `undefined`. + /// + /// @return {Struct.BBMOD_Camera} Returns `self`. + static update = function (_deltaTime, _positionHandler=undefined) { + if (os_browser != browser_not_a_browser) + { + set_mouselook(bbmod_html5_pointer_is_locked()); + } + + if (MouseLook) + { + if (os_browser != browser_not_a_browser) + { + Direction -= bbmod_html5_pointer_get_movement_x() * MouseSensitivity; + DirectionUp -= bbmod_html5_pointer_get_movement_y() * MouseSensitivity; + } + else + { + var _mouseX = window_mouse_get_x(); + var _mouseY = window_mouse_get_y(); + Direction += (__mouseLockAt.X - _mouseX) * MouseSensitivity; + DirectionUp += (__mouseLockAt.Y - _mouseY) * MouseSensitivity; + window_mouse_set(__mouseLockAt.X, __mouseLockAt.Y); + } + } + + if (DirectionUpMin != undefined) + { + DirectionUp = max(DirectionUp, DirectionUpMin); + } + if (DirectionUpMax != undefined) + { + DirectionUp = min(DirectionUp, DirectionUpMax); + } + + var _offsetX = lengthdir_x(Offset.X, Direction - 90.0) + + lengthdir_x(Offset.Y, Direction); + var _offsetY = lengthdir_y(Offset.X, Direction - 90.0) + + lengthdir_y(Offset.Y, Direction); + var _offsetZ = Offset.Z; + + if (Zoom <= 0) + { + // First person camera + if (FollowObject != undefined + && instance_exists(FollowObject)) + { + Position.X = FollowObject.x + _offsetX; + Position.Y = FollowObject.y + _offsetY; + Position.Z = FollowObject.z + _offsetZ; + } + + Target = Position.Add(new BBMOD_Vec3( + +dcos(Direction), + -dsin(Direction), + +dtan(DirectionUp) + )); + } + else + { + // Third person camera + if (FollowObject != undefined + && instance_exists(FollowObject)) + { + var _targetNew = new BBMOD_Vec3( + FollowObject.x + _offsetX, + FollowObject.y + _offsetY, + FollowObject.z + _offsetZ + ); + + if (__followObjectLast == FollowObject + && FollowFactor < 1.0) + { + var _factor = 1.0 + - bbmod_lerp_delta_time(0.0, 1.0, FollowFactor, _deltaTime); + if (FollowCurve != undefined) + { + _factor = FollowCurve(0.0, 1.0, _factor); + } + Target = _targetNew.Lerp(Target, _factor); + } + else + { + Target = _targetNew; + } + } + + var _l = dcos(DirectionUp) * Zoom; + Position = Target.Add(new BBMOD_Vec3( + -dcos(Direction) * _l, + +dsin(Direction) * _l, + -dsin(DirectionUp) * Zoom + )); + } + + if (_positionHandler != undefined) + { + Position = _positionHandler(Position); + } + + update_matrices(); + + __followObjectLast = FollowObject; + + return self; + }; +} diff --git a/scripts/BBMOD_Camera/BBMOD_Camera.yy b/scripts/BBMOD_Camera/BBMOD_Camera.yy new file mode 100644 index 000000000..10bdf5da1 --- /dev/null +++ b/scripts/BBMOD_Camera/BBMOD_Camera.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Camera", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Camera", + "path": "folders/_Extensions/BBMOD/Core/Camera.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Class/BBMOD_Class.gml b/scripts/BBMOD_Class/BBMOD_Class.gml new file mode 100644 index 000000000..dce0a92e6 --- /dev/null +++ b/scripts/BBMOD_Class/BBMOD_Class.gml @@ -0,0 +1,148 @@ +/// @macro Must be the first line when defining a custom class! +/// @example +/// ```gml +/// function CSurface(_width, _height) +/// : BBMOD_Class() constructor +/// { +/// BBMOD_CLASS_GENERATED_BODY; +/// +/// static Class_destroy = destroy; +/// +/// Surface = surface_create(_width, _height); +/// +/// static destroy = function () { +/// Class_destroy(); +/// surface_free(Surface); +/// return undefined; +/// }; +/// } +/// ``` +#macro BBMOD_CLASS_GENERATED_BODY \ + static __ClassName = bbmod_get_calling_function_name(); \ + array_push(__inheritance, __ClassName) + +/// @func BBMOD_Class() +/// +/// @desc Base for BBMOD structs that require more OOP functionality. +function BBMOD_Class() constructor +{ + /// @var {Array} An array of names of inherited classes. + /// @private + __inheritance = []; + + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Array} An array of implemented interfaces. + /// @private + __interfaces = []; + + /// @var {Array} An array of functions executed when the destroy + /// method is called. + /// @private + __destroyActions = []; + + /// @func is_instance(_class) + /// + /// @desc Checks if the struct inherits from given class. + /// + /// @param {Function} _class The class constructor. + /// + /// @return {Bool} Returns `true` if the struct inherits from the class. + static is_instance = function (_class) { + gml_pragma("forceinline"); + var _className = bbmod_class_get_name(_class); + var i = 0; + repeat (array_length(__inheritance)) + { + if (__inheritance[i++] == _className) + { + return true; + } + } + return false; + }; + + /// @func implement(_interface) + /// + /// @desc Implements an interface into the struct. + /// + /// @return {Struct.BBMOD_Class} Returns `self`. + /// @throws {BBMOD_Exception} If the struct already implements the interface. + static implement = function (_interface) { + gml_pragma("forceinline"); + if (implements(_interface)) + { + throw new BBMOD_Exception("Interface already implemented!"); + return self; + } + array_push(__interfaces, _interface); + method(self, _interface)(); + return self; + }; + + /// @func implements(_interface) + /// + /// @desc Checks whether the struct implements an interface. + /// + /// @param {Function} _interface The interface to check. + /// + /// @return {Bool} Returns `true` if the struct implements the interface. + static implements = function (_interface) { + gml_pragma("forceinline"); + var i = 0; + repeat (array_length(__interfaces)) + { + if (__interfaces[i++] == _interface) + { + return true; + } + } + return false; + }; + + /// @func destroy() + /// + /// @desc Frees resources used by the struct from memory. + /// + /// @return {Undefined} Returns `undefined`. + static destroy = function () { + var i = 0; + repeat (array_length(__destroyActions)) + { + method(self, __destroyActions[i++])(); + } + return undefined; + }; +} + +/// @func bbmod_is_class(_value) +/// +/// @desc Checks if a value is an instance of {@link BBMOD_Class}. +/// +/// @param {Any} _value The value to check. +/// +/// @return {Bool} Returns `true` if the value is an instance of {@link BBMOD_Class}. +/// +/// @see BBMOD_Class +function bbmod_is_class(_value) +{ + gml_pragma("forceinline"); + return (is_struct(_value) + && variable_struct_exists(_value, "__ClassName")); +} + +/// @func bbmod_class_get_name(_class) +/// +/// @desc Retrieves class name from class instance or class type. +/// +/// @param {Struct.BBMOD_Class, Function} _class An instance of {@link BBMOD_Class} +/// or the class type (function). +/// +/// @return {String} The name of the class. +function bbmod_class_get_name(_class) +{ + gml_pragma("forceinline"); + return is_struct(_class) + ? _class.__ClassName + : script_get_name(_class); +} diff --git a/scripts/BBMOD_Class/BBMOD_Class.yy b/scripts/BBMOD_Class/BBMOD_Class.yy new file mode 100644 index 000000000..a655867df --- /dev/null +++ b/scripts/BBMOD_Class/BBMOD_Class.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Class", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Core", + "path": "folders/_Extensions/BBMOD/Core.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Collider/BBMOD_Collider.gml b/scripts/BBMOD_Collider/BBMOD_Collider.gml new file mode 100644 index 000000000..a20b2466b --- /dev/null +++ b/scripts/BBMOD_Collider/BBMOD_Collider.gml @@ -0,0 +1,132 @@ +/// @func BBMOD_Collider() +/// +/// @extends BBMOD_Class +/// +/// @desc Base struct for colliders. +/// +/// @see BBMOD_AABBCollider +/// @see BBMOD_FrustumCollider +/// @see BBMOD_PlaneCollider +/// @see BBMOD_SphereCollider +function BBMOD_Collider() + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @func GetClosestPoint(_point) + /// + /// @desc Retrieves a point on the surface of the collider that is closest + /// to the point specified. + /// + /// @param {Struct.BBMOD_Vec3} _point The point to get the closest point to. + /// + /// @return {Struct.BBMOD_Vec3} A point on the surface of the collider that + /// is closest to the point specified. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static GetClosestPoint = function (_point) { + throw new BBMOD_NotImplementedException(); + }; + + /// @func TestAABB(_aabb) + /// + /// @desc Tests whether the collider intersects with an AABB. + /// + /// @param {Struct.BBMOD_AABBCollider} _aabb The AABB to check intersection + /// with. + /// + /// @return {Bool} Returns `true` if the colliders intersect. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static TestAABB = function (_aabb) { + throw new BBMOD_NotImplementedException(); + }; + + /// @func TestFrustum(_frustum) + /// + /// @desc Tests whether the collider intersects with a frustum. + /// + /// @param {Struct.BBMOD_FrustumCollider} _frustum The frustum to check intersection + /// with. + /// + /// @return {Bool} Returns `true` if the colliders intersect. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static TestFrustum = function (_frustum) { + throw new BBMOD_NotImplementedException(); + }; + + /// @func TestPlane(_plane) + /// + /// @desc Tests whether the collider intersects with a plane. + /// + /// @param {Struct.BBMOD_PlaneCollider} _plane The plane to check intersection + /// with. + /// + /// @return {Bool} Returns `true` if the colliders intersect. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static TestPlane = function (_plane) { + throw new BBMOD_NotImplementedException(); + }; + + /// @func TestPoint(_point) + /// + /// @desc Tests whether the collider intersects with a point. + /// + /// @param {Struct.BBMOD_Vec3} _point The point to check intersection with. + /// + /// @return {Bool} Returns `true` if the colliders intersect. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static TestPoint = function (_point) { + throw new BBMOD_NotImplementedException(); + }; + + /// @func TestSphere(_sphere) + /// + /// @desc Tests whether the collider intersects with a sphere. + /// + /// @param {Struct.BBMOD_SphereCollider} _sphere The sphere to check + /// intersection with. + /// + /// @return {Bool} Returns `true` if the colliders intersect. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static TestSphere = function (_sphere) { + throw new BBMOD_NotImplementedException(); + }; + + /// @func Raycast(_ray) + /// + /// @desc Casts a ray against the collider. + /// + /// @param {Struct.BBMOD_Ray} _ray The ray to cast. + /// @param {Struct.BBMOD_RaycastResult} [_result] Where to store + /// additional raycast info to or `undefined`. + /// + /// @return {Bool} Returns `true` if the ray hits the collider. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + /// + /// @see BBMOD_RaycastResult + /// @see BBMOD_Ray.Raycast + static Raycast = function (_ray, _result=undefined) { + throw new BBMOD_NotImplementedException(); + }; + + /// @func DrawDebug([_color]) + /// + /// @desc Draws a debug preview of the collider. + /// + /// @param {Constant.Color} [_color] The preview color. Defaults to + /// `c_white`. + /// @param {Real} [_alpha] The preview alpha. Defaults to 1. + /// + /// @return {Struct.BBMOD_Collider} Returns `self`. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static DrawDebug = function (_color=c_white, _alpha=1.0) { + throw new BBMOD_NotImplementedException(); + }; +} diff --git a/scripts/BBMOD_Collider/BBMOD_Collider.yy b/scripts/BBMOD_Collider/BBMOD_Collider.yy new file mode 100644 index 000000000..afc350011 --- /dev/null +++ b/scripts/BBMOD_Collider/BBMOD_Collider.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Collider", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Raycasting", + "path": "folders/_Extensions/BBMOD/Raycasting.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_CollisionEventModule/BBMOD_CollisionEventModule.gml b/scripts/BBMOD_CollisionEventModule/BBMOD_CollisionEventModule.gml new file mode 100644 index 000000000..1f2be7de5 --- /dev/null +++ b/scripts/BBMOD_CollisionEventModule/BBMOD_CollisionEventModule.gml @@ -0,0 +1,43 @@ +/// @func BBMOD_CollisionEventModule([_callback]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that executes a callback on particle collision. +/// +/// @param {Function} [_callback] The function to execute. Must take the emitter +/// as the first argument and the particle's index as the second argument. +/// Defaults to `undefined`. +/// +/// @see BBMOD_EParticle.HasCollided +/// @see BBMOD_ParticleEmitter +/// @see BBMOD_ParticleEmitter.Particles +function BBMOD_CollisionEventModule(_callback=undefined) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Function} The function to execute on particle collision. Must take + /// the emitter as the first argument and the particle's index as the second + /// argument. Default value is `undefined`. + /// @see BBMOD_ParticleEmitter + /// @see BBMOD_ParticleEmitter.Particles + Callback = _callback; + + static on_update = function (_emitter, _deltaTime) { + var _callback = Callback; + if (!_callback) + { + return; + } + var _particles = _emitter.Particles; + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + if (_particles[# BBMOD_EParticle.HasCollided, _particleIndex]) + { + _callback(_emitter, _particleIndex); + } + ++_particleIndex; + } + }; +} diff --git a/scripts/BBMOD_CollisionEventModule/BBMOD_CollisionEventModule.yy b/scripts/BBMOD_CollisionEventModule/BBMOD_CollisionEventModule.yy new file mode 100644 index 000000000..09cd956ea --- /dev/null +++ b/scripts/BBMOD_CollisionEventModule/BBMOD_CollisionEventModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_CollisionEventModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Event", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Event.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_CollisionKillModule/BBMOD_CollisionKillModule.gml b/scripts/BBMOD_CollisionKillModule/BBMOD_CollisionKillModule.gml new file mode 100644 index 000000000..8c6e60a3c --- /dev/null +++ b/scripts/BBMOD_CollisionKillModule/BBMOD_CollisionKillModule.gml @@ -0,0 +1,51 @@ +/// @func BBMOD_CollisionKillModule() +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that kills all particles that had a collision. +/// +/// @note Make sure to add this *after* a collision module, otherwise no +/// collision will be detected! +/// +/// @see BBMOD_EParticle.HasCollided +function BBMOD_CollisionKillModule() + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static on_update = function (_emitter, _deltaTime) { + var _y2 = _emitter.ParticlesAlive - 1; + if (_y2 >= 0) + { + var _particles = _emitter.Particles; + var _gridCompute = _emitter.GridCompute; + + ds_grid_set_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HealthLeft, 0, + BBMOD_EParticle.HealthLeft, _y2, + 0, 0); + + ds_grid_multiply_region( + _gridCompute, + 0, 0, + 0, _y2, + -1); + + ds_grid_multiply_grid_region( + _gridCompute, + _particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, _y2, + 0, 0); + + ds_grid_add_grid_region( + _particles, + _gridCompute, + 0, 0, + 0, _y2, + BBMOD_EParticle.HealthLeft, 0); + } + }; +} diff --git a/scripts/BBMOD_CollisionKillModule/BBMOD_CollisionKillModule.yy b/scripts/BBMOD_CollisionKillModule/BBMOD_CollisionKillModule.yy new file mode 100644 index 000000000..6c387eea6 --- /dev/null +++ b/scripts/BBMOD_CollisionKillModule/BBMOD_CollisionKillModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_CollisionKillModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Kill", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Kill.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Color/BBMOD_Color.gml b/scripts/BBMOD_Color/BBMOD_Color.gml new file mode 100644 index 000000000..b11bf600b --- /dev/null +++ b/scripts/BBMOD_Color/BBMOD_Color.gml @@ -0,0 +1,330 @@ +/// @macro {Real} +/// @private +#macro __BBMOD_RGBM_RANGE 6.0 + +/// @macro {Real} The maximum value of a single channel when using RGBM encoded +/// colors. This is always at least 255. +#macro BBMOD_RGBM_VALUE_MAX (255.0 * __BBMOD_RGBM_RANGE) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_aqua)`. +/// @see BBMOD_Color +#macro BBMOD_C_AQUA (new BBMOD_Color().FromConstant(c_aqua)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_black)`. +/// @see BBMOD_Color +#macro BBMOD_C_BLACK (new BBMOD_Color().FromConstant(c_black)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_blue)`. +/// @see BBMOD_Color +#macro BBMOD_C_BLUE (new BBMOD_Color().FromConstant(c_blue)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_dkgray)`. +/// @see BBMOD_Color +#macro BBMOD_C_DKGRAY (new BBMOD_Color().FromConstant(c_dkgray)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_fuchsia)`. +/// @see BBMOD_Color +#macro BBMOD_C_FUCHSIA (new BBMOD_Color().FromConstant(c_fuchsia)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_gray)`. +/// @see BBMOD_Color +#macro BBMOD_C_GRAY (new BBMOD_Color().FromConstant(c_gray)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_green)`. +/// @see BBMOD_Color +#macro BBMOD_C_GREEN (new BBMOD_Color().FromConstant(c_green)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_lime)`. +/// @see BBMOD_Color +#macro BBMOD_C_LIME (new BBMOD_Color().FromConstant(c_lime)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_ltgray)`. +/// @see BBMOD_Color +#macro BBMOD_C_LTGRAY (new BBMOD_Color().FromConstant(c_ltgray)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_maroon)`. +/// @see BBMOD_Color +#macro BBMOD_C_MAROON (new BBMOD_Color().FromConstant(c_maroon)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_navy)`. +/// @see BBMOD_Color +#macro BBMOD_C_NAVY (new BBMOD_Color().FromConstant(c_navy)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_olive)`. +/// @see BBMOD_Color +#macro BBMOD_C_OLIVE (new BBMOD_Color().FromConstant(c_olive)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_orange)`. +/// @see BBMOD_Color +#macro BBMOD_C_ORANGE (new BBMOD_Color().FromConstant(c_orange)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_purple)`. +/// @see BBMOD_Color +#macro BBMOD_C_PURPLE (new BBMOD_Color().FromConstant(c_purple)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_red)`. +/// @see BBMOD_Color +#macro BBMOD_C_RED (new BBMOD_Color().FromConstant(c_red)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_silver)`. +/// @see BBMOD_Color +#macro BBMOD_C_SILVER (new BBMOD_Color().FromConstant(c_silver)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_teal)`. +/// @see BBMOD_Color +#macro BBMOD_C_TEAL (new BBMOD_Color().FromConstant(c_teal)) + +/// @macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_white)`. +/// @see BBMOD_Color +#macro BBMOD_C_WHITE (new BBMOD_Color().FromConstant(c_white)) + +///@macro {Struct.BBMOD_Color} Shorthand for `new BBMOD_Color().FromConstant(c_yellow)`. +/// @see BBMOD_Color +#macro BBMOD_C_YELLOW (new BBMOD_Color().FromConstant(c_yellow)) + +/// @func BBMOD_Color([_red[, _green[, _blue[, _alpha]]]]) +/// +/// @desc A color struct with support for high dynamic range. +/// +/// @param {Real} [_red] The value of the red channel. Use values in range +/// 0..`BBMOD_RGBM_VALUE_MAX`. Defaults to 255. +/// @param {Real} [_green] The value of the green channel. Use values in range +/// 0..`BBMOD_RGBM_VALUE_MAX`. Defaults to 255. +/// @param {Real} [_blue] The value of the blue channel. Use values in range +/// 0..`BBMOD_RGBM_VALUE_MAX`. Defaults to 255. +/// @param {Real} [_alpha] The value of the alpha channel. Use values in range +/// 0..1. Defaults to 1. +/// +/// @see BBMOD_C_AQUA +/// @see BBMOD_C_BLACK +/// @see BBMOD_C_BLUE +/// @see BBMOD_C_DKGRAY +/// @see BBMOD_C_FUCHSIA +/// @see BBMOD_C_GRAY +/// @see BBMOD_C_GREEN +/// @see BBMOD_C_LIME +/// @see BBMOD_C_LTGRAY +/// @see BBMOD_C_MAROON +/// @see BBMOD_C_NAVY +/// @see BBMOD_C_OLIVE +/// @see BBMOD_C_ORANGE +/// @see BBMOD_C_PURPLE +/// @see BBMOD_C_RED +/// @see BBMOD_C_SILVER +/// @see BBMOD_C_TEAL +/// @see BBMOD_C_WHITE +/// @see BBMOD_C_YELLOW +/// @see BBMOD_RGBM_VALUE_MAX +function BBMOD_Color( + _red=255.0, + _green=255.0, + _blue=255.0, + _alpha=1.0) constructor +{ + /// @var {Real} The value of the red color channel. + Red = _red; + + /// @var {Real} The value of the green color channel. + Green = _green; + + /// @var {Real} The value of the blue color channel. + Blue = _blue; + + /// @var {Real} The value of the alpha channel. + Alpha = _alpha; + + /// @func Copy(_dest) + /// + /// @desc Copies properties to another color struct. + /// + /// @return {Struct.BBMOD_Color} Returns `self`. + static Copy = function (_dest) { + gml_pragma("forceinline"); + _dest.Red = Red; + _dest.Green = Green; + _dest.Blue = Blue; + _dest.Alpha = Alpha; + return self; + }; + + /// @func Clone() + /// + /// @desc Creates a clone of the color struct. + /// + /// @return {Struct.BBMOD_Color} The created clone. + static Clone = function () { + gml_pragma("forceinline"); + var _clone = new BBMOD_Color(); + Copy(_clone); + return _clone; + }; + + /// @func FromConstant(_color) + /// + /// @desc Initializes the color using a color constant. + /// + /// @param {Real} _color The color constant. + /// + /// @return {Struct.BBMOD_Color} Returns `self`. + /// + /// @example + /// ```gml + /// var _red = new BBMOD_Color().FromConstant(c_red); + /// ``` + static FromConstant = function (_color) { + gml_pragma("forceinline"); + Red = color_get_red(_color); + Green = color_get_green(_color); + Blue = color_get_blue(_color); + return self; + }; + + /// @func FromRGBA(_red, _green, _blue[, _alpha]) + /// + /// @desc Initializes the color using RGBA. + /// + /// @param {Real} _red The value of the red channel. Use values in range + /// 0..`BBMOD_RGBM_VALUE_MAX`. + /// @param {Real} _green The value of the green channel. Use values in range + /// 0..`BBMOD_RGBM_VALUE_MAX`. + /// @param {Real} _blue The value of the blue channel. Use values in range + /// 0..`BBMOD_RGBM_VALUE_MAX`. + /// + /// @param {Real} [_alpha] The value of the alpha channel. Defaults to 1. + /// + /// @return {Struct.BBMOD_Color} Returns `self`. + static FromRGBA = function (_red, _green, _blue, _alpha=1.0) { + gml_pragma("forceinline"); + Red = _red; + Green = _green; + Blue = _blue; + Alpha = _alpha; + return self; + }; + + /// @func FromHex(_hex) + /// + /// @desc Initializes the color using `RRGGBB` hexadecimal format. Alpha + /// channel is set to 1. + /// + /// @param {Real} _hex The hexadecimal color. + /// + /// @return {Struct.BBMOD_Color} Returns `self`. + /// + /// @example + /// ```gml + /// new BBMOD_Color().FromHex($FF0000); // Same as FromConstant(c_red) + /// new BBMOD_Color().FromHex($00FF00); // Same as FromConstant(c_lime) + /// new BBMOD_Color().FromHex($0000FF); // Same as FromConstant(c_blue) + /// ``` + static FromHex = function (_hex) { + gml_pragma("forceinline"); + Red = color_get_blue(_hex); + Green = color_get_green(_hex); + Blue = color_get_red(_hex); + Alpha = 1.0; + return self; + }; + + /// @func FromHSV(_hue, _saturation, _value) + /// + /// @desc Initializes the color using HSV. Alpha channel is set to 1. + /// + /// @param {Real} _hue Color hue. Use values in range 0..255. + /// @param {Real} _saturation Color saturation. Use values in range 0..255. + /// @param {Real} _value Color value. Use values in range 0..255. + /// + /// @return {Struct.BBMOD_Color} Returns `self`. + static FromHSV = function (_hue, _saturation, _value) { + gml_pragma("forceinline"); + var _hsv = make_color_hsv(_hue, _saturation, _value); + Red = color_get_red(_hsv); + Green = color_get_green(_hsv); + Blue = color_get_blue(_hsv); + Alpha = 1.0; + return self; + }; + + /// @func Mix(_color, _factor) + /// + /// @desc Mixes two colors. + /// + /// @param {Struct.BBMOD_Color} _color The other color to mix this one with. + /// + /// @param {Real} _factor The mixing factor. Use values in range 0..1, + /// where 0 would result into this color, 1 would be the other color and + /// 0.5 would return a merge of both colors equally. + /// + /// @return {Struct.BBMOD_Color} The new color. + static Mix = function (_color, _factor) { + gml_pragma("forceinline"); + return new BBMOD_Color( + lerp(Red, _color.Red, _factor), + lerp(Green, _color.Green, _factor), + lerp(Blue, _color.Blue, _factor), + lerp(Alpha, _color.Alpha, _factor)); + }; + + /// @func ToConstant() + /// + /// @desc Encodes the color into a single value, compatible with GameMaker's + /// color constants. Ignores the alpha channel. + /// + /// @return {Constant.Color} The color as a single value. + /// + /// @example + /// ```gml + /// var _red = new BBMOD_Color(255, 0, 0); + /// show_debug_message(_red.ToConstant() == c_red); // Prints true + /// ``` + static ToConstant = function () { + gml_pragma("forceinline"); + return make_color_rgb(Red, Green, Blue); + }; + + /// @func ToHSV([_array[, _index]]) + /// + /// @desc Encodes the color into HSV format, ignoring the alpha channel. + /// + /// @param {Array} [_array] The array to output the values to. A new + /// one is created if not defined. + /// @param {Real} [_index] The index to start writing the values to. Defaults + /// to 0. + /// + /// @return {Array} Returns the array with HSV values. + static ToHSV = function (_array, _index) { + gml_pragma("forceinline"); + _array = (_array != undefined) ? _array : array_create(3, 0); + _index = (_index != undefined) ? _index : 0; + var _rgb = make_color_rgb(Red, Green, Blue); + _array[@ _index] = color_get_hue(_rgb); + _array[@ _index + 1] = color_get_saturation(_rgb); + _array[@ _index + 2] = color_get_value(_rgb); + return _array; + }; + + /// @func ToRGBM([_array[, _index]]) + /// + /// @desc Encodes the color into RGBM format, ignoring the alpha channel. + /// + /// @param {Array} [_array] The array to output the values to. If + /// `undefined`, then a new one is created. + /// @param {Real} [_index] The index to start writing the values to. + /// Defaults to 0. + /// + /// @return {Array} Returns the array with RGBM values. + static ToRGBM = function (_array=undefined, _index=0) { + gml_pragma("forceinline"); + _array ??= array_create(4, 0); + var _red = min(Red / BBMOD_RGBM_VALUE_MAX, 1.0); + var _green = min(Green / BBMOD_RGBM_VALUE_MAX, 1.0); + var _blue = min(Blue / BBMOD_RGBM_VALUE_MAX, 1.0); + var _alpha = clamp(max(_red, _green, _blue, 0.000001), 0.0, 1.0); + _alpha = ceil(_alpha * 255.0) / 255.0; + _array[@ _index] = _red / _alpha; + _array[@ _index + 1] = _green / _alpha; + _array[@ _index + 2] = _blue / _alpha; + _array[@ _index + 3] = _alpha; + return _array; + }; +} diff --git a/scripts/BBMOD_Color/BBMOD_Color.yy b/scripts/BBMOD_Color/BBMOD_Color.yy new file mode 100644 index 000000000..8a2dc5881 --- /dev/null +++ b/scripts/BBMOD_Color/BBMOD_Color.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Color", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Cubemap/BBMOD_Cubemap.gml b/scripts/BBMOD_Cubemap/BBMOD_Cubemap.gml new file mode 100644 index 000000000..3a71f9059 --- /dev/null +++ b/scripts/BBMOD_Cubemap/BBMOD_Cubemap.gml @@ -0,0 +1,241 @@ +/// @enum Enumeration of cube sides, compatible with +/// [Xpanda](https://github.com/GameMakerDiscord/Xpanda)'s cubemap layout. +enum BBMOD_ECubeSide +{ + /// @member Front cube side. + PosX, + /// @member Back cube side. + NegX, + /// @member Right cube side. + PosY, + /// @member Left cube side. + NegY, + /// @member Top cube side. + PosZ, + /// @member Bottom cube side. + NegZ, + /// @member Number of cube sides. + SIZE, +}; + +/// @func BBMOD_Cubemap(_resolution) +/// +/// @extends BBMOD_Class +/// +/// @implements {BBMOD_IRenderTarget} +/// +/// @desc A cubemap. +/// +/// @param {Real} _resolution A resolution of single cubemap side. Must be power +/// of 2! +function BBMOD_Cubemap(_resolution) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + implement(BBMOD_IRenderTarget); + + static Class_destroy = destroy; + + /// @var {Array} The position of the cubemap in the world space. + /// @see BBMOD_Cubemap.get_view_matrix + Position = new BBMOD_Vec3(); + + /// @var {Real} Distance to the near clipping plane used in the cubemap's + /// projection matrix. Defaults to `0.1`. + /// @see BBMOD_Cubemap.get_projection_matrix + ZNear = 0.1; + + /// @var {Real} Distance to the far clipping plane used in the cubemap's + /// projection matrix. Defaults to `8192`. + /// @see BBMOD_Cubemap.get_projection_matrix + ZFar = 8192.0; + + /// @var {Array} An array of surfaces. + /// @readonly + Sides = array_create(BBMOD_ECubeSide.SIZE, noone); + + /// @var {Id.Surface} A single surface containing all cubemap sides. + /// This can be passed as uniform to a shader for cubemapping. + /// @readonly + Surface = noone; + + /// @var {Real} A resolution of single cubemap side. Must be power of two. + /// @readonly + Resolution = _resolution; + + /// @var {Real} An index of a side that we are currently rendering to. + /// Contains values from {@link BBMOD_ECubeSide}. + /// @see BBMOD_Cubemap.set_target + /// @private + __renderTo = 0; + + /// @func get_surface(_side) + /// + /// @desc Gets a surface for given cubemap side. If the surface is corrupted, + /// then a new one is created. + /// + /// @param {Real} _side The cubemap side. + /// + /// @return {Id.Surface} The surface. + /// + /// @see BBMOD_ECubeSide + static get_surface = function (_side) { + var _surOld = Sides[_side]; + var _sur = bbmod_surface_check(_surOld, Resolution, Resolution); + if (_sur != _surOld) + { + Sides[@ _side] = _sur; + } + return _sur; + }; + + /// @func to_single_surface(_clearColor, _clearAlpha) + /// + /// @desc Puts all faces of the cubemap into a single surface. + /// + /// @param {Real} _clearColor The color to clear the target surface with + /// before the cubemap is rendered into it. + /// @param {Real} _clearAlpha The alpha to clear the targe surface with + /// before the cubemap is rendered into it. + /// + /// @see BBMOD_Cubemap.Surface + static to_single_surface = function (_clearColor, _clearAlpha) { + Surface = bbmod_surface_check(Surface, Resolution * 8, Resolution); + surface_set_target(Surface); + draw_clear_alpha(_clearColor, _clearAlpha); + var _x = 0; + var i = 0; + repeat (BBMOD_ECubeSide.SIZE) + { + draw_surface(Sides[i++], _x, 0); + _x += Resolution; + } + surface_reset_target(); + }; + + /// @func get_view_matrix(_side) + /// + /// @desc Creates a view matrix for given cubemap side. + /// + /// @param {Real} _side The cubemap side. Use values from + /// {@link BBMOD_ECubeSide}. + /// + /// @return {Array} The created view matrix. + static get_view_matrix = function (_side) { + var _negEye = Position.Scale(-1.0); + var _x, _y, _z; + + switch (_side) + { + case BBMOD_ECubeSide.PosX: + _x = new BBMOD_Vec3(0.0, +1.0, 0.0); + _y = new BBMOD_Vec3(0.0, 0.0, +1.0); + _z = new BBMOD_Vec3(+1.0, 0.0, 0.0); + break; + + case BBMOD_ECubeSide.NegX: + _x = new BBMOD_Vec3(0.0, -1.0, 0.0); + _y = new BBMOD_Vec3(0.0, 0.0, +1.0); + _z = new BBMOD_Vec3(-1.0, 0.0, 0.0); + break; + + case BBMOD_ECubeSide.PosY: + _x = new BBMOD_Vec3(-1.0, 0.0, 0.0); + _y = new BBMOD_Vec3(0.0, 0.0, +1.0); + _z = new BBMOD_Vec3(0.0, +1.0, 0.0); + break; + + case BBMOD_ECubeSide.NegY: + _x = new BBMOD_Vec3(+1.0, 0.0, 0.0); + _y = new BBMOD_Vec3(0.0, 0.0, +1.0); + _z = new BBMOD_Vec3(0.0, -1.0, 0.0); + break; + + case BBMOD_ECubeSide.PosZ: + _x = new BBMOD_Vec3(0.0, +1.0, 0.0); + _y = new BBMOD_Vec3(-1.0, 0.0, 0.0); + _z = new BBMOD_Vec3(0.0, 0.0, +1.0); + break; + + case BBMOD_ECubeSide.NegZ: + _x = new BBMOD_Vec3(0.0, +1.0, 0.0); + _y = new BBMOD_Vec3(+1.0, 0.0, 0.0); + _z = new BBMOD_Vec3(0.0, 0.0, -1.0); + break; + } + + return [ + _x.X, _y.X, _z.X, 0.0, + _x.Y, _y.Y, _z.Y, 0.0, + _x.Z, _y.Z, _z.Z, 0.0, + _x.Dot(_negEye), _y.Dot(_negEye), _z.Dot(_negEye), 1.0 + ]; + } + + /// @func get_projection_matrix() + /// + /// @desc Creates a projection matrix for the cubemap. + /// + /// @return {Array} The created projection matrix. + static get_projection_matrix = function () { + gml_pragma("forceinline"); + return matrix_build_projection_perspective_fov(90.0, 1.0, ZNear, ZFar); + }; + + /// @func set_target() + /// + /// @desc Sets next cubemap side surface as the render target and sets + /// the current view and projection matrices appropriately. + /// + /// @return {Bool} Returns `true` if the render target was set or `false` + /// if all cubemap sides were iterated through. + /// + /// @example + /// ```gml + /// while (cubemap.set_target()) + /// { + /// draw_clear(c_black); + /// // Render to cubemap here... + /// cubemap.reset_target(); + /// } + /// ``` + /// + /// @see BBMOD_IRenderTarget.reset_target + static set_target = function () { + var _renderTo = __renderTo++; + if (_renderTo < BBMOD_ECubeSide.SIZE) + { + surface_set_target(get_surface(_renderTo)); + matrix_set(matrix_view, get_view_matrix(_renderTo)); + matrix_set(matrix_projection, get_projection_matrix()); + return true; + } + __renderTo = 0; + return false; + }; + + static reset_target = function () { + gml_pragma("forceinline"); + surface_reset_target(); + return self; + }; + + static destroy = function () { + Class_destroy(); + var i = 0; + repeat (BBMOD_ECubeSide.SIZE) + { + var _surface = Sides[i++]; + if (surface_exists(_surface)) + { + surface_free(_surface); + } + } + if (surface_exists(Surface)) + { + surface_free(Surface); + } + return undefined; + }; +} diff --git a/scripts/BBMOD_Cubemap/BBMOD_Cubemap.yy b/scripts/BBMOD_Cubemap/BBMOD_Cubemap.yy new file mode 100644 index 000000000..466ce2938 --- /dev/null +++ b/scripts/BBMOD_Cubemap/BBMOD_Cubemap.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Cubemap", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_DLL/BBMOD_DLL.gml b/scripts/BBMOD_DLL/BBMOD_DLL.gml new file mode 100644 index 000000000..433eb0b04 --- /dev/null +++ b/scripts/BBMOD_DLL/BBMOD_DLL.gml @@ -0,0 +1,867 @@ +/// @macro {Bool} Evaluates to `true` if BBMOD DLL is supported on the current +/// platform and the BBMOD dynamic library exists. +/// +/// @example +/// ```gml +/// if (BBMOD_DLL_IS_SUPPORTED) +/// { +/// var _dll = new BBMOD_DLL(); +/// // Use BBMOD DLL here... +/// _dll = _dll.destroy(); +/// } +/// ``` +/// +/// @see BBMOD_DLL_PATH +#macro BBMOD_DLL_IS_SUPPORTED __bbmod_dll_is_supported() + +/// @macro {String} Path to the BBMOD dynamic library. Defaults to +/// "Data/BBMOD/BBMOD.dll" on Windows and "Data/BBMOD/libBBMOD.dylib" on macOS. +#macro BBMOD_DLL_PATH \ + ((os_type == os_windows) ? "Data/BBMOD/BBMOD.dll" : "Data/BBMOD/libBBMOD.dylib") + +/// @macro {Real} A code returned from the DLL on fail, when none of +/// `BBMOD_DLL_ERR_` is applicable. +/// @private +#macro __BBMOD_DLL_FAILURE -1 + +/// @macro {Real} A code returned from the DLL when a model is successfully +/// converted. +/// @private +#macro __BBMOD_DLL_SUCCESS 0 + +/// @macro {Real} An error code returned from the DLL when model loading fails. +/// @private +#macro __BBMOD_DLL_ERR_LOAD_FAILED 1 + +/// @macro {Real} An error code returned from the DLL when model conversion +/// fails. +/// @private +#macro __BBMOD_DLL_ERR_CONVERSION_FAILED 2 + +/// @macro {Real} An error code returned from the DLL when converted model +/// is not saved. +/// @private +#macro __BBMOD_DLL_ERR_SAVE_FAILED 3 + +/// @func BBMOD_DLL() +/// +/// @extends BBMOD_Class +/// +/// @desc Loads a dynamic library which allows you to convert models into BBMOD. +/// +/// @throws {BBMOD_Exception} If the DLL file does not exist. +/// +/// @example +/// ```gml +/// var _dll = new BBMOD_DLL(); +/// _dll.set_gen_normal(BBMOD_NORMALS_FLAT); +/// _dll.convert("House.fbx", "House.bbmod"); +/// _dll = _dll.destroy(); +/// modHouse = new BBMOD_Model("House.bbmod"); +/// ``` +function BBMOD_DLL() + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {String} Path to the dynamic library. + /// @readonly + /// @obsolete This was replaced with {@link BBMOD_DLL_PATH}. + Path = BBMOD_DLL_PATH; + + if (!file_exists(BBMOD_DLL_PATH)) + { + throw new BBMOD_Exception("File " + BBMOD_DLL_PATH + " does not exist!"); + } + + /// @func convert(_fin, _fout) + /// + /// @desc Converts a model into a BBMOD. + /// + /// @param {String} _fin Path to the original model. + /// @param {String} _fout Path to the converted model. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the model conversion fails. + static convert = function (_fin, _fout) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_convert", dll_cdecl, ty_real, 2, ty_string, ty_string); + var _retval = external_call(_fn, _fin, _fout); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_disable_bone() + /// + /// @desc Checks whether bones are disabled. + /// + /// @return {Bool} If `true` then bones are disabled. + /// + /// @see BBMOD_DLL.set_disable_bone + static get_disable_bone = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_disable_bone", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_disable_bone(_disable) + /// + /// @desc Enables/disables bones and animations. These are by default + /// **enabled**. + /// + /// @param {Bool} _disable `true` to disable. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_disable_bone + static set_disable_bone = function (_disable) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_disable_bone", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _disable); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_disable_color() + /// + /// @desc Checks whether vertex colors are disabled. + /// + /// @return {Bool} If `true` then vertex colors are disabled. + /// + /// @see BBMOD_DLL.set_disable_color + static get_disable_color = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_disable_color", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_disable_color(_disable) + /// + /// @desc Enables/disables vertex colors. Vertex colors are by default + /// **disabled**. Changing this makes the model incompatible with the + /// default shaders! + /// + /// @param {Bool} _disable `true` to disable. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_disable_color + static set_disable_color = function (_disable) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_disable_color", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _disable); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_disable_normal() + /// + /// @desc Checks whether vertex normals are disabled. + /// + /// @return {Bool} If `true` then vertex normals are disabled. + /// + /// @see BBMOD_DLL.set_disable_normal + static get_disable_normal = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_disable_normal", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_disable_normal(_disable) + /// + /// @desc Enables/disables vertex normals. Vertex normals are by default + /// **enabled**. Changing this makes the model incompatible with the default + /// shaders! + /// + /// @param {Bool} _disable `true` to disable. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_disable_normal + static set_disable_normal = function (_disable) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_disable_normal", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _disable); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_flip_normal() + /// + /// @desc Checks whether flipping vertex normals is enabled. + /// + /// @return {Bool} Returns `true` if enabled. + /// + /// @see BBMOD_DLL.set_flip_normal + static get_flip_normal = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_flip_normal", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_flip_normal(_flip) + /// + /// @desc Enables/disables flipping vertex normals. This is by default + /// **disabled**. + /// + /// @param {Bool} _flip `true` to enable. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_flip_normal + static set_flip_normal = function (_flip) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_flip_normal", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _flip); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_gen_normal() + /// + /// @desc Checks whether generating normal vectors is enabled. + /// + /// @return {Real} Returns one of the `BBMOD_NORMALS_*` macros. + /// + /// @see BBMOD_DLL.set_gen_normal + /// @see BBMOD_NORMALS_NONE + /// @see BBMOD_NORMALS_FLAT + /// @see BBMOD_NORMALS_SMOOTH + static get_gen_normal = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_gen_normal", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_gen_normal(_normals) + /// + /// @desc Configures generating normal vectors. This is by default + /// set to {@link BBMOD_NORMALS_SMOOTH}. Vertex normals are required + /// by the default shaders! + /// + /// @param {Real} _normals Use one of the `BBMOD_NORMALS_*` macros. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_gen_normal + /// @see BBMOD_NORMALS_NONE + /// @see BBMOD_NORMALS_FLAT + /// @see BBMOD_NORMALS_SMOOTH + static set_gen_normal = function (_normals) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_gen_normal", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _normals); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_disable_tangent() + /// + /// @desc Checks whether tangent and bitangent vectors are disabled. + /// + /// @return {Bool} If `true` then tangent and bitangent vectors are disabled. + /// + /// @see BBMOD_DLL.set_disable_tangent + static get_disable_tangent = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_disable_tangent", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_disable_tangent(_disable) + /// + /// @desc Enables/disables tangent and bitangent vectors. These are by + /// default **enabled**. Changing this makes the model incompatible with + /// the default shaders! + /// + /// @param {Bool} _disable `true` to disable tangent and bitangent vectors. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_disable_tangent + static set_disable_tangent = function (_disable) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_disable_tangent", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _disable); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_disable_uv() + /// + /// @desc Checks whether texture coordinates are disabled. + /// + /// @return {Bool} If `true` then texture coordinates are disabled. + /// + /// @see BBMOD_DLL.set_disable_uv + static get_disable_uv = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_disable_uv", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_disable_uv(_disable) + /// + /// @desc Enables/disables texture coordinates. Texture coordinates + /// are by default **enabled**. Changing this makes the model incompatible + /// with the default shaders! + /// + /// @param {Bool} _disable `true` to disable texture coordinates. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_disable_uv + static set_disable_uv = function (_disable) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_disable_uv", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _disable); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_disable_uv2() + /// + /// @desc Checks whether second UV channel is disabled. + /// + /// @return {Bool} If `true` then second UV channel is disabled. + /// + /// @see BBMOD_DLL.set_disable_uv2 + static get_disable_uv2 = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_disable_uv2", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_disable_uv2(_disable) + /// + /// @desc Enables/disables second UV channel. Second UV channel is by + /// default **disabled**. Changing this makes the model incompatible + /// with the default shaders! + /// + /// @param {Bool} _disable `true` to disable second UV channel. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_disable_uv2 + static set_disable_uv2 = function (_disable) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_disable_uv2", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _disable); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_flip_uv_horizontally() + /// + /// @desc Checks whether flipping texture coordinates horizontally is + /// enabled. + /// + /// @return {Bool} Returns `true` if enabled. + /// + /// @see BBMOD_DLL.set_flip_uv_horizontally + static get_flip_uv_horizontally = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_flip_uv_horizontally", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_flip_uv_horizontally(_flip) + /// + /// @desc Enables/disables flipping texture coordinates horizontally. This + /// is by default **disabled**. + /// + /// @param {Bool} _flip `true` to enable. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_flip_uv_horizontally + static set_flip_uv_horizontally = function (_flip) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_flip_uv_horizontally", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _flip); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_flip_uv_vertically() + /// + /// @desc Checks whether flipping texture coordinates vertically is enabled. + /// + /// @return {Bool} Returns `true` if enabled. + /// + /// @see BBMOD_DLL.set_flip_uv_vertically + static get_flip_uv_vertically = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_flip_uv_vertically", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_flip_uv_vertically(_flip) + /// + /// @desc Enables/disables flipping texture coordinates vertically. This is + /// by default **enabled**. + /// + /// @param {Bool} _flip `true` to enable. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_flip_uv_vertically + static set_flip_uv_vertically = function (_flip) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_flip_uv_vertically", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _flip); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_invert_winding() + /// + /// @desc Checks whether inverse vertex winding is enabled. + /// + /// @return {Bool} If `true` then inverse vertex winding is enabled. + /// + /// @see BBMOD_DLL.set_invert_winding + static get_invert_winding = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_invert_winding", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_invert_winding(_invert) + /// + /// @desc Enables/disables inverse vertex winding. This is by default + /// **disabled**. + /// + /// @param {Bool} _invert `true` to invert winding. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_invert_winding + static set_invert_winding = function (_invert) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_invert_winding", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _invert); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_left_handed() + /// + /// @desc Checks whether conversion to left-handed coordinate system is + /// enabled. + /// + /// @return {Bool} If `true` then conversion to left-handed coordinate + /// system is enabled. + /// + /// @see BBMOD_DLL.set_left_handed + static get_left_handed = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_left_handed", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_left_handed(_leftHanded) + /// + /// @desc Enables/disables conversion to left-handed coordinate system. + /// This is by default **enabled**. + /// + /// @param {Bool} _leftHanded `true` to enable conversion to left-handed + /// coordinate system. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_left_handed + static set_left_handed = function (_leftHanded) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_left_handed", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _leftHanded); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_optimize_nodes() + /// + /// @desc Checks whether node optimization is enabled. + /// + /// @return {Bool} If `true` then node optimization is enabled. + /// + /// @see BBMOD_DLL.set_optimize_nodes + static get_optimize_nodes = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_optimize_nodes", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_optimize_nodes(_optimize) + /// + /// @desc Enable/disable node optimization. When enabled, multiple + /// nodes (without bones, animations, ...) are joined into one. + /// This is by default **enabled**. + /// + /// @param {Bool} _optimize `true` to enable node optimization. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_optimize_nodes + static set_optimize_nodes = function (_optimize) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_optimize_nodes", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _optimize); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_optimize_meshes() + /// + /// @desc Checks whether mesh optimization is enabled. + /// + /// @return {Bool} If `true` then mesh optimization is enabled. + /// + /// @see BBMOD_DLL.set_optimize_meshes + static get_optimize_meshes = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_optimize_meshes", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_optimize_meshes(_optimize) + /// + /// @desc Enables/disables mesh optimization. When enabled, multiple meshes + /// with the same material are joined into one to reduce draw calls. This is + /// by default **enabled**. + /// + /// @param {Bool} _optimize `true` to enable mesh optimization. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_optimize_meshes + static set_optimize_meshes = function (_optimize) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_optimize_meshes", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _optimize); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_optimize_materials() + /// + /// @desc Checks whether material optimization is enabled. + /// + /// @return {Bool} If `true` then material optimization is enabled. + /// + /// @see BBMOD_DLL.set_optimize_materials + static get_optimize_materials = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_optimize_materials", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_optimize_materials(_optimize) + /// + /// @desc Enables/disables material optimization. When enabled, redundant + /// materials are joined into one and unused materials are removed. + /// This is by default **enabled**. + /// + /// @param {Bool} _optimize `true` to enable material optimization. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_optimize_materials + static set_optimize_materials = function (_optimize) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_optimize_materials", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _optimize); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_apply_scale() + /// + /// @desc Checks whether the "apply scale" option is enabled. + /// + /// @return {Bool} If `true` then the "apply scale" option is enabled. + /// + /// @see BBMOD_DLL.set_apply_scale + static get_apply_scale = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_apply_scale", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_apply_scale(_enable) + /// + /// @desc Enables/disables the "apply scale" option, which applies global + /// scaling factor defined in the model file if enabled. + /// This is by default **disabled**. + /// + /// @param {Bool} _enable `true` to enable the option. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_apply_scale + static set_apply_scale = function (_enable) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_apply_scale", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _enable); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_pre_transform() + /// + /// @desc Checks whether the "pre-transform" option is enabled. + /// + /// @return {Bool} If `true` then the "pre-transform" option is enabled. + /// + /// @see BBMOD_DLL.set_pre_transform + static get_pre_transform = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_pre_transform", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_pre_transform(_enable) + /// + /// @desc Enables/disables the "pre-transform" option, which pre-transforms + /// the models and collapses all nodes into one if possible. + /// This is by default **disabled**. + /// + /// @param {Bool} _enable `true` to enable the option. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_pre_transform + static set_pre_transform = function (_enable) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_apply_scale", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, _enable); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_optimize_animations() + /// + /// @desc Retrieves the animation optimization level. + /// + /// @return {Real} The animation optimization level. See section + /// [Animation optimization levels](./AnimationOptimizationLevels.html) for + /// more info. + /// + /// @see BBMOD_DLL.set_optimize_animations + static get_optimize_animations = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_optimize_animations", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_optimize_animations(_level) + /// + /// @desc Sets the animation optimization level. + /// This is by default set to **0**. + /// + /// @param {Real} _level The new animation optimization level. See section + /// [Animation optimization levels](./AnimationOptimizationLevels.html) for + /// more info. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_optimize_animations + static set_optimize_animations = function (_level) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_optimize_animations", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, clamp(_level, 0, 2)); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + /// @func get_sampling_rate() + /// + /// @desc Retrieves the animation samping rate (frames per second). + /// + /// @return {Real} The animation sampling rate. + /// + /// @see BBMOD_DLL.set_sampling_rate + static get_sampling_rate = function () { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_get_sampling_rate", dll_cdecl, ty_real, 0); + return external_call(_fn); + }; + + /// @func set_sampling_rate(_fps) + /// + /// @desc Sets the animation sampling rate (frames per second). + /// This is by default set to **60**. + /// + /// @param {Real} _fps The new animation sampling rate. + /// + /// @return {Struct.BBMOD_DLL} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the operation fails. + /// + /// @see BBMOD_DLL.get_sampling_rate + static set_sampling_rate = function (_fps) { + gml_pragma("forceinline"); + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_dll_set_sampling_rate", dll_cdecl, ty_real, 1, ty_real); + var _retval = external_call(_fn, max(floor(_fps), 1)); + if (_retval != __BBMOD_DLL_SUCCESS) + { + throw new BBMOD_Exception(); + } + return self; + }; + + static destroy = function () { + Class_destroy(); + // This is basically a singleton, so we shouldn't call free! + //external_free(BBMOD_DLL_PATH); + return undefined; + }; +} + +/// @func __bbmod_dll_is_supported() +/// +/// @return {Bool} +/// +/// @private +function __bbmod_dll_is_supported() +{ + gml_pragma("forceinline"); + static _isSupported = ((os_type == os_windows || os_type == os_macosx) + && file_exists(BBMOD_DLL_PATH)); + return _isSupported; +} diff --git a/scripts/BBMOD_DLL/BBMOD_DLL.yy b/scripts/BBMOD_DLL/BBMOD_DLL.yy new file mode 100644 index 000000000..fa465c05d --- /dev/null +++ b/scripts/BBMOD_DLL/BBMOD_DLL.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_DLL", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Core", + "path": "folders/_Extensions/BBMOD/Core.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_DefaultLightmapMaterial/BBMOD_DefaultLightmapMaterial.gml b/scripts/BBMOD_DefaultLightmapMaterial/BBMOD_DefaultLightmapMaterial.gml new file mode 100644 index 000000000..3fbf5e88f --- /dev/null +++ b/scripts/BBMOD_DefaultLightmapMaterial/BBMOD_DefaultLightmapMaterial.gml @@ -0,0 +1,36 @@ +/// @func BBMOD_DefaultLightmapMaterial([_shader]) +/// +/// @extends BBMOD_DefaultMaterial +/// +/// @desc A material that can be used when rendering lightmapped models with +/// two UV channels. +/// +/// @param {Struct.BBMOD_LightmapShader} [_shader] A shader that the material +/// uses in the {@link BBMOD_ERenderPass.Forward} pass. Leave `undefined` if you +/// would like to use {@link BBMOD_Material.set_shader} to specify shaders +/// used in specific render passes. +/// +/// @see BBMOD_LightmapShader +function BBMOD_DefaultLightmapMaterial(_shader=undefined) + : BBMOD_DefaultMaterial(_shader) constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static DefaultMaterial_copy = copy; + + /// @var {Pointer.Texture} A texture with RGBM encoded lightmap. Overrides + /// the default lightmap texture defined with {@link bbmod_lightmap_set}. + Lightmap = undefined; + + static copy = function (_dest) { + DefaultMaterial_copy(_dest); + _dest.Lightmap = Lightmap; + return self; + }; + + static clone = function () { + var _clone = new BBMOD_DefaultLightmapMaterial(); + copy(_clone); + return _clone; + }; +} diff --git a/scripts/BBMOD_DefaultLightmapMaterial/BBMOD_DefaultLightmapMaterial.yy b/scripts/BBMOD_DefaultLightmapMaterial/BBMOD_DefaultLightmapMaterial.yy new file mode 100644 index 000000000..2aa1f060e --- /dev/null +++ b/scripts/BBMOD_DefaultLightmapMaterial/BBMOD_DefaultLightmapMaterial.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_DefaultLightmapMaterial", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "DefaultRenderer", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_DefaultLightmapShader/BBMOD_DefaultLightmapShader.gml b/scripts/BBMOD_DefaultLightmapShader/BBMOD_DefaultLightmapShader.gml new file mode 100644 index 000000000..9dba73eb0 --- /dev/null +++ b/scripts/BBMOD_DefaultLightmapShader/BBMOD_DefaultLightmapShader.gml @@ -0,0 +1,223 @@ +/// @func BBMOD_DefaultLightmapShader(_shader, _vertexFormat) +/// +/// @extends BBMOD_DefaultShader +/// +/// @desc Shader used by lightmapped materials. +/// +/// @param {Asset.GMShader} _shader The shader resource. +/// @param {Struct.BBMOD_VertexFormat} _vertexFormat The vertex format required +/// by the shader. +/// +/// @see BBMOD_LightmapMaterial +function BBMOD_DefaultLightmapShader(_shader, _vertexFormat) + : BBMOD_DefaultShader(_shader, _vertexFormat) constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static DefaultShader_on_set = on_set; + static DefaultShader_set_material = set_material; + + static set_ibl = function (_ibl=undefined) { + gml_pragma("forceinline"); + + static _iblNull = sprite_get_texture(BBMOD_SprBlack, 0); + var _texture = _iblNull; + var _texel = 0.0; + + _ibl ??= global.__bbmodImageBasedLight; + if (_ibl != undefined + && _ibl.Enabled + && _ibl.AffectLightmaps) + { + _texture = _ibl.Texture; + _texel = _ibl.Texel; + } + + var _shaderCurrent = shader_current(); + var _uIBL = shader_get_sampler_index(_shaderCurrent, "bbmod_IBL"); + + texture_set_stage(_uIBL, _texture); + gpu_set_tex_mip_enable_ext(_uIBL, mip_off); + gpu_set_tex_filter_ext(_uIBL, true); + gpu_set_tex_repeat_ext(_uIBL, false); + shader_set_uniform_f( + shader_get_uniform(_shaderCurrent, "bbmod_IBLTexel"), + _texel, _texel) + + return self; + }; + + static set_ambient_light = function (_up=undefined, _down=undefined) { + gml_pragma("forceinline"); + var _shaderCurrent = shader_current(); + var _uLightAmbientUp = shader_get_uniform(_shaderCurrent, "bbmod_LightAmbientUp"); + var _uLightAmbientDown = shader_get_uniform(_shaderCurrent, "bbmod_LightAmbientDown"); + if (global.__bbmodAmbientAffectLightmap) + { + _up ??= global.__bbmodAmbientLightUp; + _down ??= global.__bbmodAmbientLightDown; + shader_set_uniform_f(_uLightAmbientUp, + _up.Red / 255.0, _up.Green / 255.0, _up.Blue / 255.0, _up.Alpha); + shader_set_uniform_f(_uLightAmbientDown, + _down.Red / 255.0, _down.Green / 255.0, _down.Blue / 255.0, _down.Alpha); + } + else + { + shader_set_uniform_f(_uLightAmbientUp, 0.0, 0.0, 0.0, 0.0); + shader_set_uniform_f(_uLightAmbientDown, 0.0, 0.0, 0.0, 0.0); + } + return self; + }; + + static set_directional_light = function (_light=undefined) { + gml_pragma("forceinline"); + _light ??= global.__bbmodDirectionalLight; + var _shaderCurrent = shader_current(); + var _uLightDirectionalDir = shader_get_uniform(_shaderCurrent, "bbmod_LightDirectionalDir"); + var _uLightDirectionalColor = shader_get_uniform(_shaderCurrent, "bbmod_LightDirectionalColor"); + if (_light != undefined + && _light.Enabled + && _light.AffectLightmaps) + { + var _direction = _light.Direction; + shader_set_uniform_f(_uLightDirectionalDir, + _direction.X, _direction.Y, _direction.Z); + var _color = _light.Color; + shader_set_uniform_f(_uLightDirectionalColor, + _color.Red / 255.0, + _color.Green / 255.0, + _color.Blue / 255.0, + _color.Alpha); + } + else + { + shader_set_uniform_f(_uLightDirectionalDir, 0.0, 0.0, -1.0); + shader_set_uniform_f(_uLightDirectionalColor, 0.0, 0.0, 0.0, 0.0); + } + return self; + }; + + static set_punctual_lights = function (_lights=undefined) { + gml_pragma("forceinline"); + + _lights ??= global.__bbmodPunctualLights; + + var _maxLights = MaxPunctualLights; + + var _indexA = 0; + var _indexMaxA = _maxLights * 8; + var _dataA = array_create(_indexMaxA, 0.0); + + var _indexB = 0; + var _indexMaxB = _maxLights * 6; + var _dataB = array_create(_indexMaxB, 0.0); + + var i = 0; + + repeat (array_length(_lights)) + { + var _light = _lights[i++]; + + if (_light.Enabled + && _light.AffectLightmaps) + { + _light.Position.ToArray(_dataA, _indexA); + _dataA[@ _indexA + 3] = _light.Range; + var _color = _light.Color; + _dataA[@ _indexA + 4] = _color.Red / 255.0; + _dataA[@ _indexA + 5] = _color.Green / 255.0; + _dataA[@ _indexA + 6] = _color.Blue / 255.0; + _dataA[@ _indexA + 7] = _color.Alpha; + _indexA += 8; + + if (_light.is_instance(BBMOD_SpotLight)) // Ugh, but works! + { + _dataB[@ _indexB] = 1.0; // Is spot light + _dataB[@ _indexB + 1] = dcos(_light.AngleInner); + _dataB[@ _indexB + 2] = dcos(_light.AngleOuter); + _light.Direction.ToArray(_dataB, _indexB + 3); + } + _indexB += 6; + + if (_indexA >= _indexMaxA) + { + break; + } + } + } + + var _shaderCurrent = shader_current(); + + shader_set_uniform_f_array( + shader_get_uniform(_shaderCurrent, "bbmod_LightPunctualDataA"), + _dataA); + shader_set_uniform_f_array( + shader_get_uniform(_shaderCurrent, "bbmod_LightPunctualDataB"), + _dataB); + + return self; + }; + + /// @func set_lightmap(_texture) + /// + /// @desc Sets the `bbmod_Lightmap` uniform. + /// + /// @param {Pointer.Texture} [_texture] The new RGBM encoded lightmap + /// texture. If not specified, defaults to the one configured using + /// {@link bbmod_lightmap_set}. + /// + /// @return {Struct.BBMOD_DefaultLightmapShader} Returns `self`. + static set_lightmap = function (_texture=global.__bbmodLightmap) { + gml_pragma("forceinline"); + var _uLightmap = shader_get_sampler_index(shader_current(), "bbmod_Lightmap"); + texture_set_stage(_uLightmap, _texture); + gpu_set_tex_mip_enable_ext(_uLightmap, mip_off); + gpu_set_tex_filter_ext(_uLightmap, true); + return self; + }; + + static on_set = function () { + DefaultShader_on_set(); + set_lightmap(); + return self; + }; + + static set_material = function (_material) { + gml_pragma("forceinline"); + DefaultShader_set_material(_material); + if (_material.Lightmap != undefined) + { + set_lightmap(_material.Lightmap); + } + return self; + }; +} + +/// @var {Pointer.Texture} +/// @private +global.__bbmodLightmap = sprite_get_texture(BBMOD_SprBlack, 0); + +/// @func bbmod_lightmap_get() +/// +/// @desc Retrieves the default lightmap texture used by all lightmapped +/// materials. +/// +/// @return {Pointer.Texture} The default RGBM encoded lightmap texture. +function bbmod_lightmap_get() +{ + gml_pragma("forceinline"); + return global.__bbmodLightmap; +} + +/// @func bbmod_lightmap_set(_texture) +/// +/// @desc Changes the default lightmap texture used by all lightmapped +/// materials. +/// +/// @param {Pointer.Texture} _texture The new default RGBM encoded lightmap +/// texture. +function bbmod_lightmap_set(_texture) +{ + gml_pragma("forceinline"); + global.__bbmodLightmap = _texture; +} diff --git a/scripts/BBMOD_DefaultLightmapShader/BBMOD_DefaultLightmapShader.yy b/scripts/BBMOD_DefaultLightmapShader/BBMOD_DefaultLightmapShader.yy new file mode 100644 index 000000000..95f093186 --- /dev/null +++ b/scripts/BBMOD_DefaultLightmapShader/BBMOD_DefaultLightmapShader.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_DefaultLightmapShader", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "DefaultRenderer", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_DefaultMaterial/BBMOD_DefaultMaterial.gml b/scripts/BBMOD_DefaultMaterial/BBMOD_DefaultMaterial.gml new file mode 100644 index 000000000..393e0be75 --- /dev/null +++ b/scripts/BBMOD_DefaultMaterial/BBMOD_DefaultMaterial.gml @@ -0,0 +1,455 @@ +/// @func BBMOD_DefaultMaterial([_shader]) +/// +/// @extends BBMOD_BaseMaterial +/// +/// @desc A material that can be used when rendering models. +/// +/// @param {Struct.BBMOD_DefaultShader} [_shader] A shader that the material +/// uses in the {@link BBMOD_ERenderPass.Forward} pass. Leave `undefined` if you +/// would like to use {@link BBMOD_Material.set_shader} to specify shaders used +/// in specific render passes. +/// +/// @see BBMOD_DefaultShader +function BBMOD_DefaultMaterial(_shader=undefined) + : BBMOD_BaseMaterial(_shader) constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static BaseMaterial_copy = copy; + static BaseMaterial_from_json = from_json; + static BaseMaterial_destroy = destroy; + + /// @var {Pointer.Texture} A texture with tangent-space normals in the RGB + /// channels and smoothness in the alpha channel or `undefined`. + NormalSmoothness = sprite_get_texture(BBMOD_SprDefaultNormalW, 0); + + __normalSmoothnessSprite = undefined; + + /// @var {Pointer.Texture} A texture specular color in the RGB channels + /// or `undefined`. + SpecularColor = sprite_get_texture(BBMOD_SprDefaultSpecularColor, 0); + + __specularColorSprite = undefined; + + /// @var {Pointer.Texture} A texture with tangent-space normals in the RGB + /// channels and roughness in the alpha channel or `undefined`. + NormalRoughness = undefined; + + __normalRoughnessSprite = undefined; + + /// @var {Pointer.Texture} A texture with metallic in the red channel and + /// ambient occlusion in the green channel or `undefined`. + MetallicAO = undefined; + + __metallicAOSprite = undefined; + + /// @var {Pointer.Texture} A texture with subsurface color in the RGB + /// channels and subsurface effect intensity in the alpha channel. + Subsurface = sprite_get_texture(BBMOD_SprBlack, 0); + + __subsurfaceSprite = undefined; + + /// @var {Pointer.Texture} RGBM encoded emissive texture. + Emissive = sprite_get_texture(BBMOD_SprBlack, 0); + + __emissiveSprite = undefined; + + // TODO: Add to_json + + static from_json = function (_json) { + BaseMaterial_from_json(_json); + + if (variable_struct_exists(_json, "NormalSmoothness")) + { + if (__normalSmoothnessSprite != undefined) + { + sprite_delete(__normalSmoothnessSprite); + __normalSmoothnessSprite = undefined; + } + + NormalSmoothness = _json.NormalSmoothness; + } + + if (variable_struct_exists(_json, "SpecularColor")) + { + if (__specularColorSprite != undefined) + { + sprite_delete(__specularColorSprite); + __specularColorSprite = undefined; + } + + SpecularColor = _json.SpecularColor; + } + + if (variable_struct_exists(_json, "NormalRoughness")) + { + if (__normalRoughnessSprite != undefined) + { + sprite_delete(__normalRoughnessSprite); + __normalRoughnessSprite = undefined; + } + + NormalRoughness = _json.NormalRoughness; + } + + if (variable_struct_exists(_json, "MetallicAO")) + { + if (__metallicAOSprite != undefined) + { + sprite_delete(__metallicAOSprite); + __metallicAOSprite = undefined; + } + + MetallicAO = _json.MetallicAO; + } + + if (variable_struct_exists(_json, "Subsurface")) + { + if (__subsurfaceSprite != undefined) + { + sprite_delete(__subsurfaceSprite); + __subsurfaceSprite = undefined; + } + + Subsurface = _json.Subsurface; + } + + if (variable_struct_exists(_json, "Emissive")) + { + if (__emissiveSprite != undefined) + { + sprite_delete(__emissiveSprite); + __emissiveSprite = undefined; + } + + Emissive = _json.Emissive; + } + + return self; + }; + + /// @func set_normal_smoothness(_normal, _smoothness) + /// + /// @desc Changes the normal vector and smoothness to a uniform value for + /// the entire material. + /// + /// @param {Struct.BBMOD_Vec3} _normal The new normal vector. If you are not + /// sure what this value should be, use {@link BBMOD_VEC3_UP}. + /// @param {Real} _smoothness The new smoothness. Use values in range 0..1. + /// + /// @return {Struct.BBMOD_DefaultMaterial} Returns `self`. + static set_normal_smoothness = function (_normal, _smoothness) { + NormalRoughness = undefined; + if (__normalRoughnessSprite != undefined) + { + sprite_delete(__normalRoughnessSprite); + __normalRoughnessSprite = undefined; + } + + if (__normalSmoothnessSprite != undefined) + { + sprite_delete(__normalSmoothnessSprite); + } + _normal = _normal.Normalize(); + __normalSmoothnessSprite = _make_sprite( + (_normal.X * 0.5 + 0.5) * 255.0, + (_normal.Y * 0.5 + 0.5) * 255.0, + (_normal.Z * 0.5 + 0.5) * 255.0, + _smoothness + ); + NormalSmoothness = sprite_get_texture(__normalSmoothnessSprite, 0); + return self; + }; + + /// @func set_specular_color(_color) + /// + /// @desc Changes the specular color to a uniform value for the entire + /// material. + /// + /// @param {Struct.BBMOD_Color} _color The new specular color. + /// + /// @return {Struct.BBMOD_DefaultMaterial} Returns `self`. + static set_specular_color = function (_color) { + MetallicAO = undefined; + if (__metallicAOSprite != undefined) + { + sprite_delete(__metallicAOSprite); + __metallicAOSprite = undefined; + } + + if (__specularColorSprite != undefined) + { + sprite_delete(__specularColorSprite); + } + __specularColorSprite = _make_sprite( + _color.Red, + _color.Green, + _color.Blue, + 1.0 + ); + SpecularColor = sprite_get_texture(__specularColorSprite, 0); + return self; + }; + + /// @func set_normal_roughness(_normal, _roughness) + /// + /// @desc Changes the normal vector and roughness to a uniform value for the + /// entire material. + /// + /// @param {Struct.BBMOD_Vec3} _normal The new normal vector. If you are not + /// sure what this value should be, use {@link BBMOD_VEC3_UP}. + /// @param {Real} _roughness The new roughness. Use values in range 0..1. + /// + /// @return {Struct.BBMOD_PBRMaterial} Returns `self`. + static set_normal_roughness = function (_normal, _roughness) { + NormalSmoothness = undefined; + if (__normalSmoothnessSprite != undefined) + { + sprite_delete(__normalSmoothnessSprite); + __normalSmoothnessSprite = undefined; + } + + if (__normalRoughnessSprite != undefined) + { + sprite_delete(__normalRoughnessSprite); + } + _normal = _normal.Normalize(); + __normalRoughnessSprite = _make_sprite( + (_normal.X * 0.5 + 0.5) * 255.0, + (_normal.Y * 0.5 + 0.5) * 255.0, + (_normal.Z * 0.5 + 0.5) * 255.0, + _roughness + ); + NormalRoughness = sprite_get_texture(__normalRoughnessSprite, 0); + return self; + }; + + /// @func set_metallic_ao(_metallic, _ao) + /// + /// @desc Changes the metalness and ambient occlusion to a uniform value for + /// the entire material. + /// + /// @param {Real} _metallic The new metalness. You can use any value in range + /// 0..1, but in general this is usually either 0 for dielectric materials + /// and 1 for metals. + /// @param {Real} _ao The new ambient occlusion value. Use values in range + /// 0..1, where 0 means full occlusion and 1 means no occlusion. + /// + /// @return {Struct.BBMOD_PBRMaterial} Returns `self`. + static set_metallic_ao = function (_metallic, _ao) { + SpecularColor = undefined; + if (__specularColorSprite != undefined) + { + sprite_delete(__specularColorSprite); + __specularColorSprite = undefined; + } + + if (__metallicAOSprite != undefined) + { + sprite_delete(__metallicAOSprite); + } + __metallicAOSprite = _make_sprite( + _metallic * 255.0, + _ao * 255.0, + 0.0, + 0.0 + ); + MetallicAO = sprite_get_texture(__metallicAOSprite, 0); + return self; + }; + + /// @func set_subsurface(_color, _intensity) + /// + /// @desc Changes the subsurface color to a uniform value for the entire + /// material. + /// + /// @param {Real} _color The new subsurface color. + /// @param {Real} _intensity The subsurface color intensity. Use values in + /// range 0..1. The higher the value, the more visible the effect is. + /// + /// @return {Struct.BBMOD_PBRMaterial} Returns `self`. + static set_subsurface = function (_color, _intensity) { + if (__subsurfaceSprite != undefined) + { + sprite_delete(__subsurfaceSprite); + } + __subsurfaceSprite = _make_sprite( + color_get_red(_color), + color_get_green(_color), + color_get_blue(_color), + _intensity + ); + Subsurface = sprite_get_texture(__subsurfaceSprite, 0); + return self; + }; + + /// @func set_emissive(_color) + /// + /// @desc Changes the emissive color to a uniform value for the entire + /// material. + /// + /// @param {Struct.BBMOD_Color} _color The new emissive color. + /// + /// @return {Struct.BBMOD_PBRMaterial} Returns `self`. + static set_emissive = function () { + var _color = (argument_count == 3) + ? new BBMOD_Color(argument[0], argument[1], argument[2]) + : argument[0]; + var _rgbm = _color.ToRGBM(); + if (__emissiveSprite != undefined) + { + sprite_delete(__emissiveSprite); + } + __emissiveSprite = _make_sprite( + _rgbm[0] * 255.0, + _rgbm[1] * 255.0, + _rgbm[2] * 255.0, + _rgbm[3] + ); + Emissive = sprite_get_texture(__emissiveSprite, 0); + return self; + }; + + static copy = function (_dest) { + BaseMaterial_copy(_dest); + + // NormalSmoothness + if (_dest.__normalSmoothnessSprite != undefined) + { + sprite_delete(_dest.__normalSmoothnessSprite); + _dest.__normalSmoothnessSprite = undefined; + } + + if (__normalSmoothnessSprite != undefined) + { + _dest.__normalSmoothnessSprite = sprite_duplicate(__normalSmoothnessSprite); + _dest.NormalSmoothness = sprite_get_texture(_dest.__normalSmoothnessSprite, 0); + } + else + { + _dest.NormalSmoothness = NormalSmoothness; + } + + // SpecularColor + if (_dest.__specularColorSprite != undefined) + { + sprite_delete(_dest.__specularColorSprite); + _dest.__specularColorSprite = undefined; + } + + if (__specularColorSprite != undefined) + { + _dest.__specularColorSprite = sprite_duplicate(__specularColorSprite); + _dest.SpecularColor = sprite_get_texture(_dest.__specularColorSprite, 0); + } + else + { + _dest.SpecularColor = SpecularColor; + } + + // NormalRoughness + if (_dest.__normalRoughnessSprite != undefined) + { + sprite_delete(_dest.__normalRoughnessSprite); + _dest.__normalRoughnessSprite = undefined; + } + + if (__normalRoughnessSprite != undefined) + { + _dest.__normalRoughnessSprite = sprite_duplicate(__normalRoughnessSprite); + _dest.NormalRoughness = sprite_get_texture(_dest.__normalRoughnessSprite, 0); + } + else + { + _dest.NormalRoughness = NormalRoughness; + } + + // MetallicAO + if (_dest.__metallicAOSprite != undefined) + { + sprite_delete(_dest.__metallicAOSprite); + _dest.__metallicAOSprite = undefined; + } + + if (__metallicAOSprite != undefined) + { + _dest.__metallicAOSprite = sprite_duplicate(__metallicAOSprite); + _dest.MetallicAO = sprite_get_texture(_dest.__metallicAOSprite, 0); + } + else + { + _dest.MetallicAO = MetallicAO; + } + + // Subsurface + if (_dest.__subsurfaceSprite != undefined) + { + sprite_delete(_dest.__subsurfaceSprite); + _dest.__subsurfaceSprite = undefined; + } + + if (__subsurfaceSprite != undefined) + { + _dest.__subsurfaceSprite = sprite_duplicate(__subsurfaceSprite); + _dest.Subsurface = sprite_get_texture(_dest.__subsurfaceSprite, 0); + } + else + { + _dest.Subsurface = Subsurface; + } + + // Emissive + if (_dest.__emissiveSprite != undefined) + { + sprite_delete(_dest.__emissiveSprite); + _dest.__emissiveSprite = undefined; + } + + if (__emissiveSprite != undefined) + { + _dest.__emissiveSprite = sprite_duplicate(__emissiveSprite); + _dest.Emissive = sprite_get_texture(_dest.__emissiveSprite, 0); + } + else + { + _dest.Emissive = Emissive; + } + + return self; + }; + + static clone = function () { + var _clone = new BBMOD_DefaultMaterial(); + copy(_clone); + return _clone; + }; + + static destroy = function () { + BaseMaterial_destroy(); + if (__normalSmoothnessSprite != undefined) + { + sprite_delete(__normalSmoothnessSprite); + } + if (__specularColorSprite != undefined) + { + sprite_delete(__specularColorSprite); + } + if (__normalRoughnessSprite != undefined) + { + sprite_delete(__normalRoughnessSprite); + } + if (__metallicAOSprite != undefined) + { + sprite_delete(__metallicAOSprite); + } + if (__subsurfaceSprite != undefined) + { + sprite_delete(__subsurfaceSprite); + } + if (__emissiveSprite != undefined) + { + sprite_delete(__emissiveSprite); + } + return undefined; + }; +} diff --git a/scripts/BBMOD_DefaultMaterial/BBMOD_DefaultMaterial.yy b/scripts/BBMOD_DefaultMaterial/BBMOD_DefaultMaterial.yy new file mode 100644 index 000000000..0deb21996 --- /dev/null +++ b/scripts/BBMOD_DefaultMaterial/BBMOD_DefaultMaterial.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_DefaultMaterial", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "DefaultRenderer", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_DefaultRenderer/BBMOD_DefaultRenderer.gml b/scripts/BBMOD_DefaultRenderer/BBMOD_DefaultRenderer.gml new file mode 100644 index 000000000..d738c8f09 --- /dev/null +++ b/scripts/BBMOD_DefaultRenderer/BBMOD_DefaultRenderer.gml @@ -0,0 +1,239 @@ +/// @func BBMOD_DefaultRenderer() +/// +/// @extends BBMOD_BaseRenderer +/// +/// @desc The default renderer. +/// +/// @example +/// Following code is a typical use of the renderer. +/// ```gml +/// // Create event +/// renderer = new BBMOD_DefaultRenderer() +/// .add(OCharacter) +/// .add(OTree) +/// .add(OTerrain) +/// .add(OSky); +/// renderer.UseAppSurface = true; +/// renderer.EnableShadows = true; +/// +/// camera = new BBMOD_Camera(); +/// camera.FollowObject = OPlayer; +/// +/// // Step event +/// camera.set_mouselook(true); +/// camera.update(delta_time); +/// renderer.update(delta_time); +/// +/// // Draw event +/// camera.apply(); +/// renderer.render(); +/// +/// // Post-Draw event +/// renderer.present(); +/// +/// // Clean Up event +/// renderer = renderer.destroy(); +/// ``` +/// +/// @see BBMOD_IRenderable +/// @see BBMOD_Camera +function BBMOD_DefaultRenderer() + : BBMOD_BaseRenderer() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static BaseRenderer_destroy = destroy; + + /// @var {Bool} Enables rendering scene depth into a depth buffer during the + /// {@link BBMOD_ERenderPass.DepthOnly} render pass pass. Defaults to `false`. + EnableGBuffer = false; + + /// @var {Real} Resolution multiplier for the depth buffer surface. Defaults + /// to 1. + GBufferScale = 1.0; + + /// @var {Id.Surface} The G-buffer surface. + /// @private + __surDepthBuffer = noone; + + /// @var {Bool} Enables screen-space ambient occlusion. This requires + /// the depth buffer. Defaults to `false`. Enabling this requires the + /// [SSAO submodule](./SSAOSubmodule.html)! + /// @see BBMOD_DefaultRenderer.EnableGBuffer + EnableSSAO = false; + + /// @var {Id.Surface} The SSAO surface. + /// @private + __surSSAO = noone; + + /// @var {Id.Surface} Surface used for blurring SSAO. + /// @private + __surWork = noone; + + /// @var {Real} Resolution multiplier for SSAO surface. Defaults to 1. + SSAOScale = 1.0; + + /// @var {Real} Screen-space radius of SSAO. Default value is 16. + SSAORadius = 16.0; + + /// @var {Real} Strength of the SSAO effect. Should be greater than 0. + /// Default value is 1. + SSAOPower = 1.0; + + /// @var {Real} SSAO angle bias in radians. Default value is 0.03. + SSAOAngleBias = 0.03; + + /// @var {Real} Maximum depth difference of SSAO samples. Samples farther + /// away from the origin than this will not contribute to the effect. + /// Default value is 10. + SSAODepthRange = 10.0; + + /// @var {Real} Defaults to 0.01. Increase to fix self-occlusion. + SSAOSelfOcclusionBias = 0.01; + + /// @var {Real} Maximum depth difference over which can be SSAO samples + /// blurred. Defaults to 2. + SSAOBlurDepthRange = 2.0; + + static render = function (_clearQueues=true) { + global.__bbmodRendererCurrent = self; + + static _renderQueues = bbmod_render_queues_get(); + + var _world = matrix_get(matrix_world); + var _view = matrix_get(matrix_view); + var _projection = matrix_get(matrix_projection); + var _renderWidth = get_render_width(); + var _renderHeight = get_render_height(); + + var i = 0; + repeat (array_length(Renderables)) + { + with (Renderables[i++]) + { + render(); + } + } + + bbmod_material_reset(); + + //////////////////////////////////////////////////////////////////////// + // + // Edit mode + // + __render_gizmo_and_instance_ids(); + + //////////////////////////////////////////////////////////////////////// + // + // Shadow map + // + __render_shadowmap(); + + bbmod_shader_set_global_f("bbmod_ZFar", bbmod_camera_get_zfar()); + + //////////////////////////////////////////////////////////////////////// + // + // G-buffer pass + // + if (EnableGBuffer) + { + var _width = _renderWidth * GBufferScale; + var _height = _renderHeight * GBufferScale; + + __surDepthBuffer = bbmod_surface_check(__surDepthBuffer, _width, _height); + + surface_set_target(__surDepthBuffer); + draw_clear(c_white); + matrix_set(matrix_view, _view); + matrix_set(matrix_projection, _projection); + bbmod_render_pass_set(BBMOD_ERenderPass.DepthOnly); + var _rqi = 0; + repeat (array_length(_renderQueues)) + { + _renderQueues[_rqi++].submit(); + } + surface_reset_target(); + } + + //////////////////////////////////////////////////////////////////////// + // + // Render SSAO + // + if (EnableGBuffer && EnableSSAO) + { + var _width = _renderWidth * SSAOScale; + var _height = _renderHeight * SSAOScale; + + __surSSAO = bbmod_surface_check(__surSSAO, _width, _height); + __surWork = bbmod_surface_check(__surWork, _width, _height); + + bbmod_material_reset(); + + bbmod_ssao_draw(SSAORadius * SSAOScale, SSAOPower, SSAOAngleBias, + SSAODepthRange, __surSSAO, __surWork, __surDepthBuffer, _projection, + bbmod_camera_get_zfar(), SSAOSelfOcclusionBias, SSAOBlurDepthRange); + + bbmod_shader_set_global_sampler( + "bbmod_SSAO", surface_get_texture(__surSSAO)); + } + else + { + bbmod_shader_set_global_sampler( + "bbmod_SSAO", sprite_get_texture(BBMOD_SprWhite, 0)); + } + + //////////////////////////////////////////////////////////////////////// + // + // Forward pass + // + bbmod_shader_set_global_sampler("bbmod_GBuffer", EnableGBuffer + ? surface_get_texture(__surDepthBuffer) + : sprite_get_texture(BBMOD_SprWhite, 0)); + + matrix_set(matrix_view, _view); + matrix_set(matrix_projection, _projection); + + bbmod_render_pass_set(BBMOD_ERenderPass.Forward); + + var _rqi = 0; + repeat (array_length(_renderQueues)) + { + var _queue = _renderQueues[_rqi++].submit(); + if (_clearQueues) + { + _queue.clear(); + } + } + + // Unset in case it gets destroyed when the room changes etc. + bbmod_shader_unset_global("bbmod_Shadowmap"); + bbmod_shader_unset_global("bbmod_SSAO"); + bbmod_shader_unset_global("bbmod_GBuffer"); + + bbmod_material_reset(); + + matrix_set(matrix_world, _world); + return self; + }; + + static destroy = function () { + BaseRenderer_destroy(); + + if (surface_exists(__surDepthBuffer)) + { + surface_free(__surDepthBuffer); + } + + if (surface_exists(__surSSAO)) + { + surface_free(__surSSAO); + } + + if (surface_exists(__surWork)) + { + surface_free(__surWork); + } + + return undefined; + }; +} diff --git a/scripts/BBMOD_DefaultRenderer/BBMOD_DefaultRenderer.yy b/scripts/BBMOD_DefaultRenderer/BBMOD_DefaultRenderer.yy new file mode 100644 index 000000000..09e14ac57 --- /dev/null +++ b/scripts/BBMOD_DefaultRenderer/BBMOD_DefaultRenderer.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_DefaultRenderer", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "DefaultRenderer", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_DefaultShader/BBMOD_DefaultShader.gml b/scripts/BBMOD_DefaultShader/BBMOD_DefaultShader.gml new file mode 100644 index 000000000..49a0752a8 --- /dev/null +++ b/scripts/BBMOD_DefaultShader/BBMOD_DefaultShader.gml @@ -0,0 +1,154 @@ +/// @func BBMOD_DefaultShader(_shader, _vertexFormat) +/// +/// @extends BBMOD_BaseShader +/// +/// @desc Shader used by the default BBMOD materials. +/// +/// @param {Asset.GMShader} _shader The shader resource. +/// @param {Struct.BBMOD_VertexFormat} _vertexFormat The vertex format required +/// by the shader. +/// +/// @see BBMOD_DefaultMaterial +function BBMOD_DefaultShader(_shader, _vertexFormat) + : BBMOD_BaseShader(_shader, _vertexFormat) constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static BaseShader_set_material = set_material; + + /// @func set_normal_smoothness(_texture) + /// + /// @desc Sets the `bbmod_NormalW` uniform. + /// + /// @param {Pointer.Texture} _texture The new texture with normal vector in + /// the RGB channels and smoothness in the A channel. + /// + /// @return {Struct.BBMOD_DefaultShader} Returns `self`. + static set_normal_smoothness = function (_texture) { + gml_pragma("forceinline"); + var _shaderCurrent = shader_current(); + var _uIsRoughness = shader_get_uniform(_shaderCurrent, "bbmod_IsRoughness"); + var _uNormalW = shader_get_sampler_index(_shaderCurrent, "bbmod_NormalW"); + shader_set_uniform_f(_uIsRoughness, 0.0); + texture_set_stage(_uNormalW, _texture); + return self; + }; + + /// @func set_specular_color(_texture) + /// + /// @desc Sets the `bbmod_Material` uniform. + /// + /// @param {Pointer.Texture} _texture The new texture with specular color in + /// the RGB channels. + /// + /// @return {Struct.BBMOD_DefaultShader} Returns `self`. + static set_specular_color = function (_texture) { + gml_pragma("forceinline"); + var _shaderCurrent = shader_current(); + var _uIsMetallic = shader_get_uniform(_shaderCurrent, "bbmod_IsMetallic"); + var _uMaterial = shader_get_sampler_index(_shaderCurrent, "bbmod_Material"); + shader_set_uniform_f(_uIsMetallic, 0.0); + texture_set_stage(_uMaterial, _texture); + }; + + /// @func set_normal_roughness(_texture) + /// + /// @desc Sets the `bbmod_NormalW` uniform. + /// + /// @param {Pointer.Texture} _texture The new texture with normal vector in + /// the RGB channels and roughness in the A channel. + /// + /// @return {Struct.BBMOD_DefaultShader} Returns `self`. + static set_normal_roughness = function (_texture) { + gml_pragma("forceinline"); + var _shaderCurrent = shader_current(); + var _uIsRoughness = shader_get_uniform(_shaderCurrent, "bbmod_IsRoughness"); + var _uNormalW = shader_get_sampler_index(_shaderCurrent, "bbmod_NormalW"); + shader_set_uniform_f(_uIsRoughness, 1.0); + texture_set_stage(_uNormalW, _texture); + return self; + }; + + /// @func set_metallic_ao(_texture) + /// + /// @desc Sets the `bbmod_Material` uniform. + /// + /// @param {Pointer.Texture} _texture The new texture with metalness in the + /// R channel and ambient occlusion in the G channel. + /// + /// @return {Struct.BBMOD_DefaultShader} Returns `self`. + static set_metallic_ao = function (_texture) { + gml_pragma("forceinline"); + var _shaderCurrent = shader_current(); + var _uIsMetallic = shader_get_uniform(_shaderCurrent, "bbmod_IsMetallic"); + var _uMaterial = shader_get_sampler_index(_shaderCurrent, "bbmod_Material"); + shader_set_uniform_f(_uIsMetallic, 1.0); + texture_set_stage(_uMaterial, _texture); + return self; + }; + + /// @func set_subsurface(_texture) + /// + /// @desc Sets the `bbmod_Subsurface` uniform. + /// + /// @param {Pointer.Texture} _texture The new texture with subsurface color + /// in the RGB channels and its intensity in the A channel. + /// + /// @return {Struct.BBMOD_DefaultShader} Returns `self`. + static set_subsurface = function (_texture) { + gml_pragma("forceinline"); + var _uSubsurface = shader_get_sampler_index(shader_current(), "bbmod_Subsurface"); + texture_set_stage(_uSubsurface, _texture); + return self; + }; + + /// @func set_emissive(_texture) + /// + /// @desc Sets the `bbmod_Emissive` uniform. + /// + /// @param {Pointer.Texture} _texture The new texture with RGBM encoded + /// emissive color. + /// + /// @return {Struct.BBMOD_DefaultShader} Returns `self`. + static set_emissive = function (_texture) { + gml_pragma("forceinline"); + var _uEmissive = shader_get_sampler_index(shader_current(), "bbmod_Emissive"); + texture_set_stage(_uEmissive, _texture); + return self; + }; + + static set_material = function (_material) { + gml_pragma("forceinline"); + BaseShader_set_material(_material); + + // Normal smoothness/roughness + if (_material.NormalSmoothness != undefined) + { + set_normal_smoothness(_material.NormalSmoothness); + } + + if (_material.NormalRoughness != undefined) + { + set_normal_roughness(_material.NormalRoughness); + } + + // Specular color/Metallic and AO + if (_material.SpecularColor != undefined) + { + set_specular_color(_material.SpecularColor); + } + + if (_material.MetallicAO != undefined) + { + set_metallic_ao(_material.MetallicAO); + } + + // Subsurface + set_subsurface(_material.Subsurface); + + // Emissive + set_emissive(_material.Emissive); + + return self; + }; +} diff --git a/scripts/BBMOD_DefaultShader/BBMOD_DefaultShader.yy b/scripts/BBMOD_DefaultShader/BBMOD_DefaultShader.yy new file mode 100644 index 000000000..30b1b44f8 --- /dev/null +++ b/scripts/BBMOD_DefaultShader/BBMOD_DefaultShader.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_DefaultShader", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "DefaultRenderer", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_DefaultSpriteShader/BBMOD_DefaultSpriteShader.gml b/scripts/BBMOD_DefaultSpriteShader/BBMOD_DefaultSpriteShader.gml new file mode 100644 index 000000000..9549417f3 --- /dev/null +++ b/scripts/BBMOD_DefaultSpriteShader/BBMOD_DefaultSpriteShader.gml @@ -0,0 +1,49 @@ +/// @func BBMOD_DefaultSpriteShader(_shader, _vertexFormat) +/// +/// @extends BBMOD_DefaultShader +/// +/// @desc A variant of {@link BBMOD_DefaultShader} which can be used when +/// rendering GameMaker sprites. +/// +/// @param {Asset.GMShader} _shader The shader resource. +/// @param {Struct.BBMOD_VertexFormat} _vertexFormat The vertex format required +/// by the shader. +/// +/// @see BBMOD_DefaultMaterial +function BBMOD_DefaultSpriteShader(_shader, _vertexFormat) + : BBMOD_DefaultShader(_shader, _vertexFormat) constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static DefaultShader_set_material = set_material; + + static set_material = function (_material) { + gml_pragma("forceinline"); + DefaultShader_set_material(_material); + + var _shaderCurrent = shader_current(); + + var _texture = _material.BaseOpacity; + if (_texture != pointer_null) + { + var _uBaseOpacityUV = shader_get_uniform(_shaderCurrent, "bbmod_BaseOpacityUV"); + shader_set_uniform_f_array(_uBaseOpacityUV, texture_get_uvs(_texture)); + } + + _texture = _material.NormalSmoothness ?? _material.NormalRoughness; + if (_texture != undefined) + { + var _uNormalWUV = shader_get_uniform(_shaderCurrent, "bbmod_NormalWUV"); + shader_set_uniform_f_array(_uNormalWUV, texture_get_uvs(_texture)); + } + + _texture = _material.SpecularColor ?? _material.MetallicAO; + if (_texture != undefined) + { + var _uMaterialUV = shader_get_uniform(_shaderCurrent, "bbmod_MaterialUV"); + shader_set_uniform_f_array(_uMaterialUV, texture_get_uvs(_texture)); + } + + return self; + }; +} diff --git a/scripts/BBMOD_DefaultSpriteShader/BBMOD_DefaultSpriteShader.yy b/scripts/BBMOD_DefaultSpriteShader/BBMOD_DefaultSpriteShader.yy new file mode 100644 index 000000000..a36807f99 --- /dev/null +++ b/scripts/BBMOD_DefaultSpriteShader/BBMOD_DefaultSpriteShader.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_DefaultSpriteShader", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "DefaultRenderer", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_DirectionalLight/BBMOD_DirectionalLight.gml b/scripts/BBMOD_DirectionalLight/BBMOD_DirectionalLight.gml new file mode 100644 index 000000000..1b93866a8 --- /dev/null +++ b/scripts/BBMOD_DirectionalLight/BBMOD_DirectionalLight.gml @@ -0,0 +1,98 @@ +/// @var {Struct.BBMOD_DirectionalLight} +/// @private +global.__bbmodDirectionalLight = undefined; + +/// @func BBMOD_DirectionalLight([_color[, _direction]]) +/// +/// @extends BBMOD_Light +/// +/// @desc A directional light. +/// +/// @param {Struct.BBMOD_Color} [_color] The light's color. Defaults to +/// {@link BBMOD_C_WHITE} if `undefined`. +/// @param {Struct.BBMOD_Vec3} [_direction] The light's direction. Defaults to +/// `(-1, 0, -1)` if `undefined`. +function BBMOD_DirectionalLight(_color=undefined, _direction=undefined) + : BBMOD_Light() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_Color} The color of the light. Defaul value is + /// {@link BBMOD_C_WHITE}. + Color = _color ?? BBMOD_C_WHITE; + + /// @var {Struct.BBMOD_Vec3} The direction of the light. Default value is + /// `(-1, 0, -1)`. + Direction = _direction ?? new BBMOD_Vec3(-1.0, 0.0, -1.0).Normalize(); + + /// @var {Real} The area captured by the shadowmap. Defaults to 1024. + ShadowmapArea = 1024; + + __getZFar = __get_shadowmap_zfar; + + __getViewMatrix = __get_shadowmap_view; + + __getProjMatrix = __get_shadowmap_projection; + + __getShadowmapMatrix = __get_shadowmap_matrix; + + static __get_shadowmap_zfar = function () { + gml_pragma("forceinline"); + return ShadowmapArea; + }; + + static __get_shadowmap_view = function () { + gml_pragma("forceinline"); + var _position = bbmod_camera_get_position(); + return matrix_build_lookat( + _position.X, + _position.Y, + _position.Z, + _position.X + Direction.X, + _position.Y + Direction.Y, + _position.Z + Direction.Z, + 0.0, 0.0, 1.0); // TODO: Find the up vector + }; + + static __get_shadowmap_projection = function () { + gml_pragma("forceinline"); + return matrix_build_projection_ortho( + ShadowmapArea, ShadowmapArea, -ShadowmapArea * 0.5, ShadowmapArea * 0.5); + }; + + static __get_shadowmap_matrix = function () { + gml_pragma("forceinline"); + return matrix_multiply( + __getViewMatrix(), + __getProjMatrix()); + }; +} + +/// @func bbmod_light_directional_get() +/// +/// @desc Retrieves the directional light passed to shaders. +/// +/// @return {Struct.BBMOD_DirectionalLight} The directional light or `undefined`. +/// +/// @see bbmod_light_directional_set +/// @see BBMOD_DirectionalLight +function bbmod_light_directional_get() +{ + gml_pragma("forceinline"); + return global.__bbmodDirectionalLight; +} + +/// @func bbmod_light_directional_set(_light) +/// +/// @desc Defines the directional light passed to shaders. +/// +/// @param {Struct.BBMOD_DirectionalLight} _light The new directional light or +/// `undefined`. +/// +/// @see bbmod_light_directional_get +/// @see BBMOD_DirectionalLight +function bbmod_light_directional_set(_light) +{ + gml_pragma("forceinline"); + global.__bbmodDirectionalLight = _light; +} diff --git a/scripts/BBMOD_DirectionalLight/BBMOD_DirectionalLight.yy b/scripts/BBMOD_DirectionalLight/BBMOD_DirectionalLight.yy new file mode 100644 index 000000000..24042d7e6 --- /dev/null +++ b/scripts/BBMOD_DirectionalLight/BBMOD_DirectionalLight.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_DirectionalLight", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Lights", + "path": "folders/_Extensions/BBMOD/Core/Lights.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_DragModule/BBMOD_DragModule.gml b/scripts/BBMOD_DragModule/BBMOD_DragModule.gml new file mode 100644 index 000000000..e91c46f7f --- /dev/null +++ b/scripts/BBMOD_DragModule/BBMOD_DragModule.gml @@ -0,0 +1,35 @@ +/// @func BBMOD_DragModule() +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that applies drag force to particles. +/// +/// @see BBMOD_EParticle.Drag +function BBMOD_DragModule() + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static on_update = function (_emitter, _deltaTime) { + var _particles = _emitter.Particles; + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _mass = _particles[# BBMOD_EParticle.Mass, _particleIndex]; + if (_mass != 0.0) + { + var _dragHalf = _particles[# BBMOD_EParticle.Drag, _particleIndex] * 0.5; + var _velocityX = _particles[# BBMOD_EParticle.VelocityX, _particleIndex]; + var _velocityY = _particles[# BBMOD_EParticle.VelocityY, _particleIndex]; + var _velocityZ = _particles[# BBMOD_EParticle.VelocityZ, _particleIndex]; + _particles[# BBMOD_EParticle.AccelerationX, _particleIndex] -= + (_dragHalf * _velocityX * abs(_velocityX)) / _mass; + _particles[# BBMOD_EParticle.AccelerationY, _particleIndex] -= + (_dragHalf * _velocityY * abs(_velocityY)) / _mass; + _particles[# BBMOD_EParticle.AccelerationZ, _particleIndex] -= + (_dragHalf * _velocityZ * abs(_velocityZ)) / _mass; + } + ++_particleIndex; + } + }; +} diff --git a/scripts/BBMOD_DragModule/BBMOD_DragModule.yy b/scripts/BBMOD_DragModule/BBMOD_DragModule.yy new file mode 100644 index 000000000..23df226d6 --- /dev/null +++ b/scripts/BBMOD_DragModule/BBMOD_DragModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_DragModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Physics", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Physics.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_DualQuaternion/BBMOD_DualQuaternion.gml b/scripts/BBMOD_DualQuaternion/BBMOD_DualQuaternion.gml new file mode 100644 index 000000000..c93888303 --- /dev/null +++ b/scripts/BBMOD_DualQuaternion/BBMOD_DualQuaternion.gml @@ -0,0 +1,517 @@ +/// @func BBMOD_DualQuaternion([_x, _y, _z, _w, _dx, _dy, _dz, _dw]) +/// +/// @desc A dual quaternion. +/// +/// @param {Real} [_x] The first component of the real part of the dual +/// quaternion. Defaults to 0. +/// @param {Real} [_y] The second component of the real part of the dual +/// quaternion. Defaults to 0. +/// @param {Real} [_z] The third component of the real part of the dual +/// quaternion. Defaults to 0. +/// @param {Real} [_w] The fourth component of the real part of the dual +/// quaternion. Defaults to 1. +/// @param {Real} [_dx] The first component of the dual part of the dual +/// quaternion. Defaults to 0. +/// @param {Real} [_dy] The second component of the dual part of the dual +/// quaternion. Defaults to 0. +/// @param {Real} [_dz] The third component of the dual part of the dual +/// quaternion. Defaults to 0. +/// @param {Real} [_dw] The fourth component of the dual part of the dual +/// quaternion. Defaults to 0. +/// +/// @note If you leave all the arguments to their default values, an identity +/// dual quaternion is created. +function BBMOD_DualQuaternion( + _x=0.0, _y=0.0, _z=0.0, _w=1.0, + _dx=0.0, _dy=0.0, _dz=0.0, _dw=0.0) constructor +{ + /// @var {Struct.BBMOD_Quaternion} The real part of the dual quaternion. + Real = new BBMOD_Quaternion(_x, _y, _z, _w); + + /// @var {Struct.BBMOD_Quaternion} The dual part of the dual quaternion. + Dual = new BBMOD_Quaternion(_dx, _dy, _dz, _dw); + + /// @func Add(_dq) + /// + /// @desc Adds dual quaternions and returns the result as a new dual + /// quaternion. + /// + /// @param {Struct.BBMOD_DualQuaternion} _dq The other dual quaternion. + /// + /// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion. + static Add = function (_dq) { + gml_pragma("forceinline"); + return new BBMOD_DualQuaternion() + .FromRealDual( + Real.Add(_dq.Real), + Dual.Add(_dq.Dual)); + }; + + /// @func Clone() + /// + /// @desc Creates a clone of the dual quaternion. + /// + /// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion. + static Clone = function () { + gml_pragma("forceinline"); + var _dq = new BBMOD_DualQuaternion(); + _dq.Real = Real; + _dq.Dual = Dual; + return _dq; + }; + + /// @func Conjugate() + /// + /// @desc Conjugates the dual quaternion and returns the result as a new + /// dual quaternion. + /// + /// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion. + static Conjugate = function () { + gml_pragma("forceinline"); + return new BBMOD_DualQuaternion() + .FromRealDual( + Real.Conjugate(), + Dual.Conjugate()); + }; + + /// @func Copy(_dq) + /// + /// @desc Copies components of the dual quaternion into other dual + /// quaternion. + /// + /// @param {Struct.BBMOD_DualQuaternion} _dest The destination dual + /// quaternion. + /// + /// @return {Struct.BBMOD_DualQuaternion} Returns `self`. + static Copy = function (_dest) { + gml_pragma("forceinline"); + _dest.Real = Real; + _dest.Dual = Dual; + return self; + }; + + /// @func Dot(_dq) + /// + /// @desc Computes a dot product of two dual quaternions. + /// + /// @param {Struct.BBMOD_DualQuaternion} _dq The other dual quaternion. + /// + /// @return {Real} The dot product of the dual quaternions. + static Dot = function (_dq) { + gml_pragma("forceinline"); + return Real.Dot(_dq.Real); + }; + + /// @func Exp() + /// + /// @desc Computes an exponential map of the dual quaternion and returns + /// the result as a new dual quaternion. + /// + /// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion. + static Exp = function () { + gml_pragma("forceinline"); + var _real = Real.Exp(); + return new BBMOD_DualQuaternion() + .FromRealDual( + _real, + _real.Mul(Dual)); + }; + + /// @func FromArray(_array[, _index]) + /// + /// @desc Loads dual quaternion components `(rX, rY, rZ, rW, dX, dY, dZ, dW)` + /// from an array. + /// + /// @param {Array} _array The array to read the dual quaternion + /// components from. + /// @param {Real} [_index] The index to start reading the dual quaternion + /// components from. Defaults to 0. + /// + /// @return {Struct.BBMOD_DualQuaternion} Returns `self`. + static FromArray = function (_array, _index=0) { + gml_pragma("forceinline"); + Real = Real.FromArray(_array, _index); + Dual = Dual.FromArray(_array, _index + 4); + return self; + }; + + /// @func FromBuffer(_buffer, _type) + /// + /// @desc Loads dual quaternion components `(rX, rY, rZ, rW, dX, dY, dZ, dW)` + /// from a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to read the dual quaternion + /// components from. + /// + /// @param {Constant.BufferDataType} [_type] The type of each component. + /// + /// @return {Struct.BBMOD_DualQuaternion} Returns `self`. + static FromBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + Real = Real.FromBuffer(_buffer, _type); + Dual = Dual.FromBuffer(_buffer, _type); + return self; + }; + + /// @func FromRealDual(_real, _dual) + /// + /// @desc Initializes the dual quaternion using real and dual part. + /// + /// @param {Struct.BBMOD_Quaternion} _real The real part of the dual + /// quaternion. + /// @param {Struct.BBMOD_Quaternion} _dual The dual part of the dual + /// quaternion. + /// + /// @return {Struct.BBMOD_DualQuaternion} Returns `self`. + static FromRealDual = function (_real, _dual) { + gml_pragma("forceinline"); + Real = _real.Normalize(); + Dual = _dual; + return self; + }; + + /// @func FromTranslationRotation(_t, _r) + /// + /// @desc Initializes the dual quaternion from translation and rotation + /// (quaternion). + /// + /// @param {Struct.BBMOD_Vec3} _t The translation. + /// + /// @param {Struct.BBMOD_Quaternion} _r The rotation. + /// + /// @return {Struct.BBMOD_DualQuaternion} Returns `self`. + static FromTranslationRotation = function (_t, _r) { + gml_pragma("forceinline"); + + Real = _r.Normalize(); + + //Dual = new BBMOD_Quaternion(_t.X, _t.Y, _t.Z, 0).Mul(Real).Scale(0.5); + var _realX = Real.X; + var _realY = Real.Y; + var _realZ = Real.Z; + var _realW = Real.W; + + var _tX = _t.X; + var _tY = _t.Y; + var _tZ = _t.Z; + // var _tW = 0; + + Dual.X = (_tY * _realZ - _tZ * _realY + /*+ _tW * _realX*/ + _tX * _realW) * 0.5; + Dual.Y = (_tZ * _realX - _tX * _realZ + /*+ _tW * _realY*/ + _tY * _realW) * 0.5; + Dual.Z = (_tX * _realY - _tY * _realX + /*+ _tW * _realZ*/ + _tZ * _realW) * 0.5; + Dual.W = (/*_tW * _realW*/ - _tX * _realX + - _tY * _realY - _tZ * _realZ) * 0.5; + + return self; + }; + + /// @func GetRotation() + /// + /// @desc Extracts rotation (quaternion) from dual quaternion. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static GetRotation = function () { + gml_pragma("forceinline"); + return Real.Clone(); + }; + + /// @func GetTranslation() + /// + /// @desc Extracts translation (vec3) from dual quaternion. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + static GetTranslation = function () { + gml_pragma("forceinline"); + + // Dual.Scale(2.0) + var _q10 = Dual.X * 2.0; + var _q11 = Dual.Y * 2.0; + var _q12 = Dual.Z * 2.0; + var _q13 = Dual.W * 2.0; + + // Real.Conjugate() + var _q20 = -Real.X; + var _q21 = -Real.Y; + var _q22 = -Real.Z; + var _q23 = Real.W; + + //return Dual.Scale(2.0).Mul(Real.Conjugate()); + return new BBMOD_Vec3( + _q13 * _q20 + _q10 * _q23 + _q11 * _q22 - _q12 * _q21, + _q13 * _q21 + _q11 * _q23 + _q12 * _q20 - _q10 * _q22, + _q13 * _q22 + _q12 * _q23 + _q10 * _q21 - _q11 * _q20 + ); + }; + + /// @func Log() + /// + /// @desc Computes the logarithm map of the dual quaternion and returns the + /// result as a new dual quaternion. + /// + /// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion. + static Log = function () { + gml_pragma("forceinline"); + var _scale = 1.0 / Real.Length(); + return new BBMOD_DualQuaternion() + .FromRealDual( + Real.Log(), + Real.Conjugate().Mul(Dual).Scale(_scale * _scale)); + }; + + /// @func Mul(_dq) + /// + /// @desc Multiplies two dual quaternions and returns the result as a new + /// dual quaternion. + /// + /// @param {Struct.BBMOD_DualQuaternion} _dq The other dual quaternion. + /// + /// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion. + static Mul = function (_dq) { + gml_pragma("forceinline"); + + var _dq1r0 = Real.X; + var _dq1r1 = Real.Y; + var _dq1r2 = Real.Z; + var _dq1r3 = Real.W; + var _dq1d0 = Dual.X; + var _dq1d1 = Dual.Y; + var _dq1d2 = Dual.Z; + var _dq1d3 = Dual.W; + var _dq2r0 = _dq.Real.X; + var _dq2r1 = _dq.Real.Y; + var _dq2r2 = _dq.Real.Z; + var _dq2r3 = _dq.Real.W; + var _dq2d0 = _dq.Dual.X; + var _dq2d1 = _dq.Dual.Y; + var _dq2d2 = _dq.Dual.Z; + var _dq2d3 = _dq.Dual.W; + + var _res = new BBMOD_DualQuaternion(); + + _res.Real.X = (_dq2r3 * _dq1r0 + _dq2r0 * _dq1r3 + _dq2r1 * _dq1r2 - _dq2r2 * _dq1r1); + _res.Real.Y = (_dq2r3 * _dq1r1 + _dq2r1 * _dq1r3 + _dq2r2 * _dq1r0 - _dq2r0 * _dq1r2); + _res.Real.Z = (_dq2r3 * _dq1r2 + _dq2r2 * _dq1r3 + _dq2r0 * _dq1r1 - _dq2r1 * _dq1r0); + _res.Real.W = (_dq2r3 * _dq1r3 - _dq2r0 * _dq1r0 - _dq2r1 * _dq1r1 - _dq2r2 * _dq1r2); + + _res.Dual.X = (_dq2d3 * _dq1r0 + _dq2d0 * _dq1r3 + _dq2d1 * _dq1r2 - _dq2d2 * _dq1r1) + + (_dq2r3 * _dq1d0 + _dq2r0 * _dq1d3 + _dq2r1 * _dq1d2 - _dq2r2 * _dq1d1); + _res.Dual.Y = (_dq2d3 * _dq1r1 + _dq2d1 * _dq1r3 + _dq2d2 * _dq1r0 - _dq2d0 * _dq1r2) + + (_dq2r3 * _dq1d1 + _dq2r1 * _dq1d3 + _dq2r2 * _dq1d0 - _dq2r0 * _dq1d2); + _res.Dual.Z = (_dq2d3 * _dq1r2 + _dq2d2 * _dq1r3 + _dq2d0 * _dq1r1 - _dq2d1 * _dq1r0) + + (_dq2r3 * _dq1d2 + _dq2r2 * _dq1d3 + _dq2r0 * _dq1d1 - _dq2r1 * _dq1d0); + _res.Dual.W = (_dq2d3 * _dq1r3 - _dq2d0 * _dq1r0 - _dq2d1 * _dq1r1 - _dq2d2 * _dq1r2) + + (_dq2r3 * _dq1d3 - _dq2r0 * _dq1d0 - _dq2r1 * _dq1d1 - _dq2r2 * _dq1d2); + + return _res; + }; + + /// @func Normalize() + /// + /// @desc Normalizes the dual quaternion and returns the result as a new + /// dual quaternion. + /// + /// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion. + static Normalize = function () { + gml_pragma("forceinline"); + var _dq = Clone(); + var _mag = Real.Dot(Real); + if (_mag > math_get_epsilon()) + { + _dq.Real = _dq.Real.Scale(1.0 / _mag); + _dq.Dual = _dq.Dual.Scale(1.0 / _mag); + } + return _dq; + }; + + /// @func Pow(_p) + /// + /// @desc Computes the power of the dual quaternion raised to a real number + /// and returns the result as a new dual quaternion. + /// + /// @param {Real} _p The power value. + /// + /// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion. + static Pow = function (_p) { + gml_pragma("forceinline"); + return Log().Scale(_p).Exp(); + }; + + /// @func Rotate(_v) + /// + /// @desc Rotates a vector using the dual quaternion and returns the result + /// as a new vector. + /// + /// @param {Struct.BBMOD_Vec3} _v The vector to rotate. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + static Rotate = function (_v) { + gml_pragma("forceinline"); + return Real.Rotate(_v); + }; + + /// @func Scale(_s) + /// + /// @desc Scales each component of the dual quaternion by a real value and + /// returns the result as a new dual quaternion. + /// + /// @param {Real} _s The value to scale the dual quaternion by. + /// + /// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion. + static Scale = function (_s) { + gml_pragma("forceinline"); + var _dq = new BBMOD_DualQuaternion(); + _dq.Real = Real.Scale(_s); + _dq.Dual = Dual.Scale(_s); + return _dq; + }; + + /// @func Sclerp(_dq, _s) + /// + /// @desc Computes a screw linear interpolation of two dual quaternions + /// and returns the result as a new dual quaternion. + /// + /// @param {Struct.BBMOD_DualQuaternion} _dq The other dual quaternion. + /// @param {Real} _s The interpolation factor. + /// + /// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion. + static Sclerp = function (_dq, _s) { + gml_pragma("forceinline"); + return _dq.Mul(Conjugate()).Pow(_s).Mul(self).Normalize(); + }; + + /// @func ToArray([_array[, _index]]) + /// + /// @desc Writes components `(rX, rY, rZ, rW, dX, dY, dZ, dW)` of the dual + /// quaternion into an array. + /// + /// @param {Array} [_array] The destination array. If not defined, a + /// new one is created. + /// + /// @param {Real} [_index] The index to start writing to. Defaults to 0. + /// + /// @return {Array} Returns the destination array. + static ToArray = function (_array=undefined, _index=0) { + gml_pragma("forceinline"); + _array ??= array_create(8, 0.0); + Real.ToArray(_array, _index); + Dual.ToArray(_array, _index + 4); + return _array; + }; + + /// @func ToBuffer(_buffer, _type) + /// + /// @desc Writes components `(rX, rY, rZ, rW, dX, dY, dZ, dW)` of the dual + /// quaternion into a buffer. + /// + /// @param {Id.Buffer} _buffer The destination buffer. + /// @param {Constant.BufferDataType} _type The type of each component. + /// + /// @return {Struct.BBMOD_DualQuaternion} Returns `self`. + static ToBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + Real.ToBuffer(_buffer, _type); + Dual.ToBuffer(_buffer, _type); + return self; + }; + + /// @func ToMatrix([_dest[, _index]]) + /// + /// @desc Converts dual quaternion into a matrix. + /// + /// @param {Array} [_dest] The destination array. If not specified, + /// a new one is created. + /// @param {Real} [_index] The starting index in the destination array. + /// Defaults to 0. + /// + /// @return {Array} Returns the destination array. + static ToMatrix = function (_dest=undefined, _index=0) { + gml_pragma("forceinline"); + + _dest ??= array_create(16, 0.0); + + // Rotation + Real.ToMatrix(_dest, _index); + _dest[@ _index + 3] = 0.0; + _dest[@ _index + 7] = 0.0; + _dest[@ _index + 11] = 0.0; + + // Translation + var _translation = GetTranslation(); + _dest[@ _index + 12] = _translation.X; + _dest[@ _index + 13] = _translation.Y; + _dest[@ _index + 14] = _translation.Z; + _dest[@ _index + 15] = 1.0; + + return _dest; + }; + + /// @func Transform(_v) + /// + /// @desc Translates and rotates a vector using the dual quaternion + /// and returns the result as a new vector. + /// + /// @param {Struct.BBMOD_Vec3} _v The vector to transform. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + static Transform = function (_v) { + gml_pragma("forceinline"); + return GetTranslation().Add(Real.Rotate(_v)); + }; +} + +/// @func __bbmod_dual_quaternion_array_multiply(_dq1, _dq1Index, _dq2, _dq2Index, _dest, _destIndex) +/// +/// @desc Multiplies two dual quaternions stored in arrays and writes the result +/// into the destination array. +/// +/// @param {Array} _dq1 An array containing the first dual quaternion. +/// @param {Real} _dq1Index The starting index of the first dual quaternion. +/// @param {Array} _dq2 An array containing the second dual quaternion. +/// @param {Real} _dq2Index The starting index of the second dual quaternion. +/// @param {Array} _dest The destination array. +/// @param {Real} _destIndex The index to start writing to within the +/// destination array. +/// +/// @note The arguments can overlap, as the input values are stored into local +/// variables before the multiplication. +/// +/// @private +function __bbmod_dual_quaternion_array_multiply( + _dq1, _dq1Index, _dq2, _dq2Index, _dest, _destIndex) +{ + gml_pragma("forceinline"); + + var _dq1r0 = _dq1[_dq1Index + 0]; + var _dq1r1 = _dq1[_dq1Index + 1]; + var _dq1r2 = _dq1[_dq1Index + 2]; + var _dq1r3 = _dq1[_dq1Index + 3]; + var _dq1d0 = _dq1[_dq1Index + 4]; + var _dq1d1 = _dq1[_dq1Index + 5]; + var _dq1d2 = _dq1[_dq1Index + 6]; + var _dq1d3 = _dq1[_dq1Index + 7]; + var _dq2r0 = _dq2[_dq2Index + 0]; + var _dq2r1 = _dq2[_dq2Index + 1]; + var _dq2r2 = _dq2[_dq2Index + 2]; + var _dq2r3 = _dq2[_dq2Index + 3]; + var _dq2d0 = _dq2[_dq2Index + 4]; + var _dq2d1 = _dq2[_dq2Index + 5]; + var _dq2d2 = _dq2[_dq2Index + 6]; + var _dq2d3 = _dq2[_dq2Index + 7]; + + _dest[@ _destIndex + 0] = (_dq2r3 * _dq1r0 + _dq2r0 * _dq1r3 + _dq2r1 * _dq1r2 - _dq2r2 * _dq1r1); + _dest[@ _destIndex + 1] = (_dq2r3 * _dq1r1 + _dq2r1 * _dq1r3 + _dq2r2 * _dq1r0 - _dq2r0 * _dq1r2); + _dest[@ _destIndex + 2] = (_dq2r3 * _dq1r2 + _dq2r2 * _dq1r3 + _dq2r0 * _dq1r1 - _dq2r1 * _dq1r0); + _dest[@ _destIndex + 3] = (_dq2r3 * _dq1r3 - _dq2r0 * _dq1r0 - _dq2r1 * _dq1r1 - _dq2r2 * _dq1r2); + + _dest[@ _destIndex + 4] = (_dq2d3 * _dq1r0 + _dq2d0 * _dq1r3 + _dq2d1 * _dq1r2 - _dq2d2 * _dq1r1) + + (_dq2r3 * _dq1d0 + _dq2r0 * _dq1d3 + _dq2r1 * _dq1d2 - _dq2r2 * _dq1d1); + _dest[@ _destIndex + 5] = (_dq2d3 * _dq1r1 + _dq2d1 * _dq1r3 + _dq2d2 * _dq1r0 - _dq2d0 * _dq1r2) + + (_dq2r3 * _dq1d1 + _dq2r1 * _dq1d3 + _dq2r2 * _dq1d0 - _dq2r0 * _dq1d2); + _dest[@ _destIndex + 6] = (_dq2d3 * _dq1r2 + _dq2d2 * _dq1r3 + _dq2d0 * _dq1r1 - _dq2d1 * _dq1r0) + + (_dq2r3 * _dq1d2 + _dq2r2 * _dq1d3 + _dq2r0 * _dq1d1 - _dq2r1 * _dq1d0); + _dest[@ _destIndex + 7] = (_dq2d3 * _dq1r3 - _dq2d0 * _dq1r0 - _dq2d1 * _dq1r1 - _dq2d2 * _dq1r2) + + (_dq2r3 * _dq1d3 - _dq2r0 * _dq1d0 - _dq2r1 * _dq1d1 - _dq2r2 * _dq1d2); +} diff --git a/scripts/BBMOD_DualQuaternion/BBMOD_DualQuaternion.yy b/scripts/BBMOD_DualQuaternion/BBMOD_DualQuaternion.yy new file mode 100644 index 000000000..c40ea89d9 --- /dev/null +++ b/scripts/BBMOD_DualQuaternion/BBMOD_DualQuaternion.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_DualQuaternion", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Math", + "path": "folders/_Extensions/BBMOD/Core/Math.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_DynamicBatch/BBMOD_DynamicBatch.gml b/scripts/BBMOD_DynamicBatch/BBMOD_DynamicBatch.gml new file mode 100644 index 000000000..e4092d29e --- /dev/null +++ b/scripts/BBMOD_DynamicBatch/BBMOD_DynamicBatch.gml @@ -0,0 +1,583 @@ +/// @macro {Real} Maximum number of vec4 uniforms for dynamic batch data +/// available in the default shaders. Equals to 192. +#macro BBMOD_MAX_BATCH_VEC4S 192 + +/// @func BBMOD_DynamicBatch([_model[, _size[, _slotsPerInstance]]]) +/// +/// @extends BBMOD_Class +/// +/// @desc A dynamic batch is a structure that allows you to render multiple +/// instances of a single model at once, each with its own position, scale and +/// rotation. Compared to {@link BBMOD_Model.submit}, this drastically reduces +/// draw calls and increases performance, but requires more memory. Number of +/// model instances per batch is also affected by maximum number of uniforms +/// that a vertex shader can accept. +/// +/// @param {Struct.BBMOD_Model} [_model] The model to create a dynamic batch of. +/// @param {Real} [_size] Number of model instances in the batch. Default value +/// is 32. +/// @param {Real} [_slotsPerInstance] Number of slots that each instance takes +/// in the data array. Default value is 12. +/// +/// @example +/// Following code renders all instances of a car object in batches of 64. +/// ```gml +/// /// @desc Create event +/// modCar = new BBMOD_Model("Car.bbmod"); +/// matCar = new BBMOD_DefaultMaterial(BBMOD_ShDefaultBatched, +/// sprite_get_texture(SprCar, 0)); +/// carBatch = new BBMOD_DynamicBatch(modCar, 64); +/// +/// /// @desc Draw event +/// carBatch.render_object(OCar, matCar); +/// ``` +/// +/// @see BBMOD_StaticBatch +function BBMOD_DynamicBatch(_model=undefined, _size=32, _slotsPerInstance=12) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Struct.BBMOD_Model} A model that is being batched. + /// @readonly + Model = _model; + + /// @var {Struct.BBMOD_Model} The batched model. + /// @readonly + Batch = undefined; + + /// @var {Real} Number of model instances in the batch. + /// @readonly + Size = _size; + + /// @var {Real} Number of instances currently added to the dynamic batch. + /// @readonly + /// @see BBMOD_DynamicBatch.add_instance + InstanceCount = 0; + + /// @var {Real} Number of slots that each instance takes in the data array. + /// @readonly + SlotsPerInstance = _slotsPerInstance; + + /// @var {Real} Total length of batch data array for a single draw call. + /// @readonly + BatchLength = Size * SlotsPerInstance; + + /// @var {Function} A function that writes instance data into the batch data + /// array. It must take the instance, array and starting index as arguments! + /// Defaults to {@link BBMOD_DynamicBatch.default_fn}. + DataWriter = default_fn; + + /// @var {Array>} + /// @private + __data = []; + + /// @var {Array}} + /// @private + __ids = []; + + /// @var {Id.DsMap} Mapping from instances to indices at which they are + /// stored in the data array. + /// @private + __instanceToIndex = ds_map_create(); + + /// @var {Id.DsMap} Mapping from data array indices to instances that they + /// hold. + /// @private + __indexToInstance = ds_map_create(); + + // @func from_model(_model) + /// + /// @desc + /// + /// @param {Struct.BBMOD_Model} _model + /// + /// @return {Struct.BBMOD_DynamicBatch} Returns `self`. + static from_model = function (_model) { + Model = _model; + build_batch(); + return self; + }; + + /// @func __resize_data() + /// + /// @desc Resizes `__data` and `__ids` arrays to required size. + /// + /// @private + static __resize_data = function () { + var _requiredArrayCount = ceil(InstanceCount / Size); + var _currentArrayCount = array_length(__data); + + if (_currentArrayCount > _requiredArrayCount) + { + array_resize(__data, _requiredArrayCount); + array_resize(__ids, _requiredArrayCount); + } + else if (_currentArrayCount < _requiredArrayCount) + { + repeat (_requiredArrayCount - _currentArrayCount) + { + array_push(__data, array_create(BatchLength, 0.0)); + array_push(__ids, array_create(Size, 0.0)); + } + } + }; + + /// @func add_instance(_instance) + /// + /// @desc Adds an instance to the dynamic batch. + /// + /// @param {Id.Instance, Struct} _instance The instance to be added. + /// + /// @return {Struct.BBMOD_DynamicBatch} Returns `self`. + static add_instance = function (_instance) { + var _indexIds = InstanceCount; + var _indexData = _indexIds * SlotsPerInstance; + __instanceToIndex[? _instance] = _indexData; + __indexToInstance[? _indexData] = _instance; + ++InstanceCount; + __resize_data(); + method(_instance, DataWriter)(__data[_indexData div BatchLength], _indexData mod BatchLength); + __ids[_indexIds div Size][@ _indexIds mod Size] = real(_instance[$ "id"] ?? 0.0); + return self; + }; + + /// @func update_instance(_instance) + /// + /// @desc Updates batch data for given instance. + /// + /// @param {Id.Instance, Struct} _instance The instance to update. + /// + /// @return {Struct.BBMOD_DynamicBatch} Returns `self`. + /// + /// @see BBMOD_DynamicBatch.DataWriter + static update_instance = function (_instance) { + gml_pragma("forceinline"); + var _index = __instanceToIndex[? _instance]; + method(_instance, DataWriter)(__data[_index div BatchLength], _index mod BatchLength); + return self; + }; + + /// @func remove_instance(_instance) + /// + /// @desc Removes an instance from the dynamic batch. + /// + /// @param {Id.Instance, Struct} _instance The instance to remove. + /// + /// @return {Struct.BBMOD_DynamicBatch} Returns `self`. + static remove_instance = function (_instance) { + var _indexDataDeleted = __instanceToIndex[? _instance]; + if (_indexDataDeleted != undefined) + { + var _indexIdDeleted = _indexDataDeleted / SlotsPerInstance; + + --InstanceCount; + if (InstanceCount > 0) + { + //////////////////////////////////////////////////////////////// + // Data + + // Get last used index + var _indexLast = InstanceCount * SlotsPerInstance; + // Get instance that is stored on that index + var _instanceLast = __indexToInstance[? _indexLast]; + // Find the exact array that stores the data + var _dataLast = __data[_indexLast div BatchLength]; + // Get starting index within that array + var i = _indexLast mod BatchLength; + + // Copy data of the last instance over the data of the removed instance + array_copy( + __data[_indexDataDeleted div BatchLength], _indexDataDeleted mod BatchLength, + _dataLast, i, SlotsPerInstance); + + // Clear slots + repeat (SlotsPerInstance) + { + _dataLast[i++] = 0.0; + } + + //////////////////////////////////////////////////////////////// + // Ids + + // Get last used index + var _indexLast = InstanceCount; + // Find the exact array that stores the id + var _idsLast = __ids[_indexLast div Size]; + // Get starting index within that array + var i = _indexLast mod Size; + + // Copy id of the last instance over the id of the removed instance + __ids[_indexIdDeleted div Size][@ _indexIdDeleted mod Size] = _idsLast[i]; + + // Clear slots + _idsLast[i] = 0.0; + + //////////////////////////////////////////////////////////////// + + // Last instance is now stored instead of the deleted one + __instanceToIndex[? _instanceLast] = _indexDataDeleted; + __indexToInstance[? _indexDataDeleted] = _instanceLast; + ds_map_delete(__indexToInstance, _indexLast); + } + __resize_data(); + } + return self; + }; + + /// @func submit([_materials[, _batchData]]) + /// + /// @desc Immediately submits the dynamic batch for rendering. + /// + /// @param {Array} [_materials] An array of materials. + /// @param {Array, Array>} [_batchData] Data for dynamic + /// batching. + /// + /// @return {Struct.BBMOD_DynamicBatch} Returns `self`. + /// + /// @see BBMOD_DynamicBatch.submit_object + /// @see BBMOD_DynamicBatch.render + /// @see BBMOD_DynamicBatch.render_object + /// @see BBMOD_Material + /// @see BBMOD_ERenderPass + static submit = function (_materials=undefined, _batchData=undefined) { + gml_pragma("forceinline"); + _batchData ??= __data; + if (array_length(_batchData) > 0) + { + if (_materials != undefined + && !is_array(_materials)) + { + _materials = [_materials]; + } + matrix_set(matrix_world, matrix_build_identity()); + Batch.submit(_materials, undefined, _batchData); + } + return self; + }; + + /// @func render([_materials[, _batchData[, _ids]]]) + /// + /// @desc Enqueues the dynamic batch for rendering. + /// + /// @param {Array} [_materials] An array of materials. + /// @param {Array, Array>} [_batchData] Data for dynamic + /// batching. Defaults to data of instances added with + /// {@link BBMOD_DynamicBatch.add_instance}. + /// @param {Array, Array>} [_ids] IDs of + /// instances in the `_batchData` array(s). Defaults to IDs of instances + /// added with {@link BBMOD_DynamicBatch.add_instance}. Applicable only when + /// `_batchData` is `undefined`! + /// + /// @return {Struct.BBMOD_DynamicBatch} Returns `self`. + /// + /// @see BBMOD_DynamicBatch.submit + /// @see BBMOD_DynamicBatch.submit_object + /// @see BBMOD_DynamicBatch.render_object + /// @see BBMOD_Material + static render = function (_materials=undefined, _batchData=undefined, _ids=undefined) { + gml_pragma("forceinline"); + + if (_batchData == undefined) + { + _batchData = __data; + global.__bbmodInstanceIDBatch = __ids; + } + else + { + global.__bbmodInstanceIDBatch = _ids; + } + + if (array_length(_batchData) > 0) + { + if (_materials != undefined + && !is_array(_materials)) + { + _materials = [_materials]; + } + matrix_set(matrix_world, matrix_build_identity()); + Batch.render(_materials, undefined, _batchData); + } + + return self; + }; + + /// @func default_fn(_data, _index) + /// + /// @desc The default data writer function. Uses instance's variables + /// `x`, `y`, `z` for position, `image_xscale` for uniform scale and + /// `image_angle` for rotation around the `z` axis. + /// + /// @param {Array} _data An array to which the function will write + /// instance data. The data layout is compatible with shader `BBMOD_ShDefaultBatched` + /// and hence with material {@link BBMOD_MATERIAL_DEFAULT_BATCHED}. + /// @param {Real} _index An index at which the first variable will be written. + /// + /// @see BBMOD_DynamicBatch.submit_object + /// @see BBMOD_DynamicBatch.render_object + static default_fn = function (_data, _index) { + // Position + _data[@ _index] = x; + _data[@ _index + 1] = y; + _data[@ _index + 2] = z; + // Uniform scale + _data[@ _index + 3] = image_xscale; + // Rotation + new BBMOD_Quaternion() + .FromAxisAngle(BBMOD_VEC3_UP, image_angle) + .ToArray(_data, _index + 4); + // ID + _data[@ _index + 8] = ((id & $000000FF) >> 0) / 255; + _data[@ _index + 9] = ((id & $0000FF00) >> 8) / 255; + _data[@ _index + 10] = ((id & $00FF0000) >> 16) / 255; + _data[@ _index + 11] = ((id & $FF000000) >> 24) / 255; + }; + + static __draw_object = function (_method, _object, _materials, _fn=undefined) { + if (!instance_exists(_object)) + { + return; + } + + _fn ??= DataWriter; + + var _slotsPerInstance = SlotsPerInstance; + var _size = Size; + var _dataSize = _size * _slotsPerInstance; + var _data = array_create(_dataSize, 0.0); + var _ids = array_create(_size, 0.0); + var _indexData = 0; + var _indexId = 0; + var _batchData = [_data]; + var _batchIds = [_ids]; + + with (_object) + { + method(self, _fn)(_data, _indexData); + _indexData += _slotsPerInstance; + + _ids[@ _indexId++] = real(self[$ "id"] ?? 0.0); + + if (_indexData >= _dataSize) + { + _data = array_create(_dataSize, 0.0); + _indexData = 0; + array_push(_batchData, _data); + + _ids = array_create(_size, 0.0); + _indexId = 0; + array_push(_batchIds, _ids); + } + } + + _method(_material, _batchData, _batchIds); + }; + + /// @func submit_object(_object[, _materials[, _fn]]) + /// + /// @desc Immediately submits all instances of an object for rendering in + /// batches of {@link BBMOD_DynamicBatch.size}. + /// + /// @param {Real} _object An object to submit. + /// @param {Array} [_material] An array of materials + /// to use. + /// @param {Function} [_fn] A function that writes instance data to an array + /// which is then passed to the material's shader. Defaults to + /// {@link BBMOD_DynamicBatch.default_fn} if `undefined`. + /// + /// @return {Struct.BBMOD_DynamicBatch} Returns `self`. + /// + /// @example + /// ```gml + /// carBatch.submit_object(OCar, [matCar], function (_data, _index) { + /// // Position + /// _data[@ _index] = x; + /// _data[@ _index + 1] = y; + /// _data[@ _index + 2] = z; + /// // Uniform scale + /// _data[@ _index + 3] = image_xscale; + /// // Rotation + /// new BBMOD_Quaternion() + /// .FromAxisAngle(BBMOD_VEC3_UP, image_angle) + /// .ToArray(_data, _index + 4); + /// // ID + /// _data[@ _index + 8] = ((id & $000000FF) >> 0) / 255; + /// _data[@ _index + 9] = ((id & $0000FF00) >> 8) / 255; + /// _data[@ _index + 10] = ((id & $00FF0000) >> 16) / 255; + /// _data[@ _index + 11] = ((id & $FF000000) >> 24) / 255; + /// }); + /// ``` + /// The function defined in this example is actually the implementation of + /// {@link BBMOD_DynamicBatch.DataWriter}. You can use this to create you own + /// variation of it. + /// + /// @see BBMOD_DynamicBatch.submit + /// @see BBMOD_DynamicBatch.render + /// @see BBMOD_DynamicBatch.render_object + /// @see BBMOD_DynamicBatch.DataWriter + static submit_object = function (_object, _materials=undefined, _fn=undefined) { + gml_pragma("forceinline"); + __draw_object(method(self, submit), _object, _materials, _fn); + return self; + }; + + /// @func render_object(_object[, _materials[, _fn]]) + /// + /// @desc Enqueues all instances of an object for rendering in batches of + /// {@link BBMOD_DynamicBatch.size}. + /// + /// @param {Asset.GMObject} _object An object to render. + /// @param {Array} [_materials] An array of materials + /// to use. + /// @param {Function} [_fn] A function that writes instance data to an + /// array which is then passed to the material's shader. Defaults to + /// {@link BBMOD_DynamicBatch.DataWriter} if `undefined`. + /// + /// @return {Struct.BBMOD_DynamicBatch} Returns `self`. + /// + /// @example + /// ```gml + /// carBatch.render_object(OCar, [matCar], function (_data, _index) { + /// // Position + /// _data[@ _index] = x; + /// _data[@ _index + 1] = y; + /// _data[@ _index + 2] = z; + /// // Uniform scale + /// _data[@ _index + 3] = image_xscale; + /// // Rotation + /// new BBMOD_Quaternion() + /// .FromAxisAngle(BBMOD_VEC3_UP, image_angle) + /// .ToArray(_data, _index + 4); + /// // ID + /// _data[@ _index + 8] = ((id & $000000FF) >> 0) / 255; + /// _data[@ _index + 9] = ((id & $0000FF00) >> 8) / 255; + /// _data[@ _index + 10] = ((id & $00FF0000) >> 16) / 255; + /// _data[@ _index + 11] = ((id & $FF000000) >> 24) / 255; + /// }); + /// ``` + /// The function defined in this example is actually the implementation of + /// {@link BBMOD_DynamicBatch.default_fn}. You can use this to create your + /// own variation of it. + /// + /// @see BBMOD_DynamicBatch.submit + /// @see BBMOD_DynamicBatch.submit_object + /// @see BBMOD_DynamicBatch.render + /// @see BBMOD_DynamicBatch.DataWriter + static render_object = function (_object, _materials=undefined, _fn=undefined) { + gml_pragma("forceinline"); + __draw_object(method(self, render), _object, _materials, _fn); + return self; + }; + + /// @func freeze() + /// + /// @desc Freezes the dynamic batch. This makes it render faster. + /// + /// @return {Struct.BBMOD_DynamicBatch} Returns `self`. + static freeze = function () { + gml_pragma("forceinline"); + Batch.freeze(); + return self; + }; + + static build_batch = function () { + if (Batch != undefined) + { + return; + } + + Batch = Model.clone(); + var _vertexFormatOld = Batch.VertexFormat; + var _vertexFormatNew; + + if (_vertexFormatOld != undefined) + { + _vertexFormatNew = new BBMOD_VertexFormat({ + Vertices: _vertexFormatOld.Vertices, + Normals: _vertexFormatOld.Normals, + TextureCoords: _vertexFormatOld.TextureCoords, + TextureCoords2: _vertexFormatOld.TextureCoords2, + Colors: _vertexFormatOld.Colors, + TangentW: _vertexFormatOld.TangentW, + Bones: _vertexFormatOld.Bones, + Ids: true, + }); + Batch.VertexFormat = _vertexFormatNew; + } + + for (var i = array_length(Batch.Meshes) - 1; i >= 0; --i) + { + var _mesh = Batch.Meshes[i]; + var _meshVertexFormatOld = _mesh.VertexFormat ?? _vertexFormatOld; + var _byteSizeOld = _meshVertexFormatOld.get_byte_size(); + + var _meshVertexFormatNew; + if (_mesh.VertexFormat) + { + _meshVertexFormatNew = new BBMOD_VertexFormat({ + Vertices: _meshVertexFormatOld.Vertices, + Normals: _meshVertexFormatOld.Normals, + TextureCoords: _meshVertexFormatOld.TextureCoords, + TextureCoords2: _meshVertexFormatOld.TextureCoords2, + Colors: _meshVertexFormatOld.Colors, + TangentW: _meshVertexFormatOld.TangentW, + Bones: _meshVertexFormatOld.Bones, + Ids: true, + }); + } + else + { + _meshVertexFormatNew = _vertexFormatNew; + } + + var _byteSizeNew = _meshVertexFormatNew.get_byte_size(); + var _vertexBufferOld = _mesh.VertexBuffer; + var _bufferOld = buffer_create_from_vertex_buffer(_vertexBufferOld, buffer_fixed, 1); + var _vertexCount = buffer_get_size(_bufferOld) / _byteSizeOld; + var _bufferNew = buffer_create(Size * _vertexCount * _byteSizeNew, buffer_fixed, 1); + var _offsetNew = 0; + var _sizeOfF32 = buffer_sizeof(buffer_f32); + + var _id = 0; + repeat (Size) + { + var _offsetOld = 0; + repeat (_vertexCount) + { + buffer_copy(_bufferOld, _offsetOld, _byteSizeOld, _bufferNew, _offsetNew); + _offsetOld += _byteSizeOld; + _offsetNew += _byteSizeOld; + buffer_poke(_bufferNew, _offsetNew, buffer_f32, _id); + _offsetNew += _sizeOfF32; + } + ++_id; + } + + _mesh.VertexBuffer = vertex_create_buffer_from_buffer(_bufferNew, _meshVertexFormatNew.Raw); + _mesh.VertexFormat = _meshVertexFormatNew; + buffer_delete(_bufferNew); + + vertex_delete_buffer(_vertexBufferOld); + buffer_delete(_bufferOld); + } + }; + + static destroy = function () { + Class_destroy(); + if (Batch != undefined) + { + Batch = Batch.destroy(); + } + __data = undefined; + ds_map_destroy(__instanceToIndex); + ds_map_destroy(__indexToInstance); + return undefined; + }; + + if (Model != undefined) + { + build_batch(); + } +} diff --git a/scripts/BBMOD_DynamicBatch/BBMOD_DynamicBatch.yy b/scripts/BBMOD_DynamicBatch/BBMOD_DynamicBatch.yy new file mode 100644 index 000000000..2b052b045 --- /dev/null +++ b/scripts/BBMOD_DynamicBatch/BBMOD_DynamicBatch.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_DynamicBatch", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Batching", + "path": "folders/_Extensions/BBMOD/Core/Batching.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_EAntialiasing/BBMOD_EAntialiasing.gml b/scripts/BBMOD_EAntialiasing/BBMOD_EAntialiasing.gml new file mode 100644 index 000000000..d997b6f02 --- /dev/null +++ b/scripts/BBMOD_EAntialiasing/BBMOD_EAntialiasing.gml @@ -0,0 +1,9 @@ +/// @enum Enumeration of anti-aliasing techniques. +enum BBMOD_EAntialiasing +{ + /// @member Anti-aliasing is turned off. + None, + /// @member Use fast approximate anti-aliasing. + /// Requires the [FXAA submodule](./FXAASubmodule.html)! + FXAA, +}; diff --git a/scripts/BBMOD_EAntialiasing/BBMOD_EAntialiasing.yy b/scripts/BBMOD_EAntialiasing/BBMOD_EAntialiasing.yy new file mode 100644 index 000000000..fb03f4947 --- /dev/null +++ b/scripts/BBMOD_EAntialiasing/BBMOD_EAntialiasing.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_EAntialiasing", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "PostProcessing", + "path": "folders/_Extensions/BBMOD/Rendering/PostProcessing.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_EParticle/BBMOD_EParticle.gml b/scripts/BBMOD_EParticle/BBMOD_EParticle.gml new file mode 100644 index 000000000..908a6d82d --- /dev/null +++ b/scripts/BBMOD_EParticle/BBMOD_EParticle.gml @@ -0,0 +1,84 @@ +/// @enum Enumeration of particle properties. +/// @see BBMOD_ParticleEmitter.Particles +enum BBMOD_EParticle +{ + /// @member The ID of the particle, unique within the emitter which spawned + /// it. + Id, + /// @member Whether the particle is alive. If not, then the rest of the data + /// can be nonsense. All particles within a particle system are dead at the + /// start. + IsAlive, + /// @member How long in seconds has the particle been alive for. This is set + /// to 0 on spawn and increases on every update. + TimeAlive, + /// @member The particle's initial health value. Default value is 1. + Health, + /// @member The particle's remaining health. The particle dies when this + /// reaches 0. Default value is 1, same as for {@link BBMOD_EParticle.Health}. + HealthLeft, + /// @member The particle's X position in world-space. This is set to the + /// emitter's X position on spawn. + PositionX, + /// @member The particle's Y position in world-space. This is set to the + /// emitter's Y position on spawn. + PositionY, + /// @member The particle's Z position in world-space. This is set to the + /// emitter's Z position on spawn. + PositionZ, + /// @member The particle's velocity on the X axis. Default value is 0. + VelocityX, + /// @member The particle's velocity on the Y axis. Default value is 0. + VelocityY, + /// @member The particle's velocity on the Z axis. Default value is 0. + VelocityZ, + /// @member The particle's acceleration on the X axis. Default value is 0. + AccelerationX, + /// @member The particle's acceleration on the Y axis. Default value is 0. + AccelerationY, + /// @member The particle's acceleration on the Z axis. Default value is 0. + AccelerationZ, + /// @member The mass of the particle. Default value is 1 unit. + Mass, + /// @member The particle's resistance to motion. Default value is 0. + Drag, + /// @member Modulates particle velocity on collision. Default value is 0. + Bounce, + /// @member If `true` then the particle has collided. This is set to + /// `false` at the beginning of every update. + HasCollided, + /// @member Internal use only! + AccelerationRealX, + /// @member Internal use only! + AccelerationRealY, + /// @member Internal use only! + AccelerationRealZ, + /// @member The first component of the particle's quaternion rotation. + /// Default value is 0. + RotationX, + /// @member The second component of the particle's quaternion rotation. + /// Default value is 0. + RotationY, + /// @member The third component of the particle's quaternion rotation. + /// Default value is 0. + RotationZ, + /// @member The fourth component of the particle's quaternion rotation. + /// Default value is 1. + RotationW, + /// @member The particle's scale on the X axis. Default value is 1. + ScaleX, + /// @member The particle's scale on the Y axis. Default value is 1. + ScaleY, + /// @member The particle's scale on the Z axis. Default value is 1. + ScaleZ, + /// @member The red value of the particle's color. Default value is 255. + ColorR, + /// @member The green value of the particle's color. Default value is 255. + ColorG, + /// @member The blue value of the particle's color. Default value is 255. + ColorB, + /// @member The alpha value of the particle's color. Default value is 1. + ColorA, + /// @member Total number of members of this enum. + SIZE, +}; diff --git a/scripts/BBMOD_EParticle/BBMOD_EParticle.yy b/scripts/BBMOD_EParticle/BBMOD_EParticle.yy new file mode 100644 index 000000000..eadb0fb35 --- /dev/null +++ b/scripts/BBMOD_EParticle/BBMOD_EParticle.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_EParticle", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Particles", + "path": "folders/_Extensions/BBMOD/Particles.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_EPropertyType/BBMOD_EPropertyType.gml b/scripts/BBMOD_EPropertyType/BBMOD_EPropertyType.gml new file mode 100644 index 000000000..3f25495b5 --- /dev/null +++ b/scripts/BBMOD_EPropertyType/BBMOD_EPropertyType.gml @@ -0,0 +1,49 @@ +/// @enum An enumeration of all property types. +/// @see BBMOD_Property +enum BBMOD_EPropertyType +{ + /// @member A boolean. + Bool, + /// @member A {@link BBMOD_Color}. + Color, + /// @member A GameMaker Font asset. + GMFont, + /// @member A GameMaker Object asset. + GMObject, + /// @member A GameMaker Path asset. + GMPath, + /// @member A GameMaker Room asset. + GMRoom, + /// @member A GameMaker Script asset. + GMScript, + /// @member A GameMaker Shader asset. + GMShader, + /// @member A GameMaker Sound asset. + GMSound, + /// @member A GameMaker Sprite asset. + GMSprite, + /// @member A GameMaker Tile Set asset. + GMTileSet, + /// @member A GameMaker Timeline asset. + GMTimeline, + /// @member A matrix. + Matrix, + /// @member A path to a file or a directory. + Path, + /// @member A {@link BBMOD_Quaternion}. + Quaternion, + /// @member A real number. + Real, + /// @member An array of real numbers. + RealArray, + /// @member A string. + String, + /// @member A {@link BBMOD_Vec2}. + Vec2, + /// @member A {@link BBMOD_Vec3}. + Vec3, + /// @member A {@link BBMOD_Vec4}. + Vec4, + // WARNING: Any new property type must be added at the end of this enum, + // otherwise save files would be corrupted!!! +}; diff --git a/scripts/BBMOD_EPropertyType/BBMOD_EPropertyType.yy b/scripts/BBMOD_EPropertyType/BBMOD_EPropertyType.yy new file mode 100644 index 000000000..bf2b06c1b --- /dev/null +++ b/scripts/BBMOD_EPropertyType/BBMOD_EPropertyType.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_EPropertyType", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Properties", + "path": "folders/_Extensions/BBMOD/Core/Properties.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_ERenderCommand/BBMOD_ERenderCommand.gml b/scripts/BBMOD_ERenderCommand/BBMOD_ERenderCommand.gml new file mode 100644 index 000000000..f145260e0 --- /dev/null +++ b/scripts/BBMOD_ERenderCommand/BBMOD_ERenderCommand.gml @@ -0,0 +1,133 @@ +/// @enum An enumeration of all possible render commands. +enum BBMOD_ERenderCommand +{ + /// @member Applies a material if it has a shader that can be used in the + /// current render pass. + ApplyMaterial, + /// @member Marks the beginning of a conditional block. Commands within this + /// block are executed only if the last command was successfully executed. + /// @example + /// ```gml + /// renderQueue.apply_material(material, vertexFormat) + /// .begin_conditional_block() + /// // Commands here will be executed only if the material was applied... + /// .end_conditional_block(); + /// ``` + /// @see BBMOD_ERenderCommand.EndConditionalBlock + BeginConditionalBlock, + /// @member Checks if the current render pass is one of specified passes. + CheckRenderPass, + /// @member Draws a mesh if its material can be used in the current render + /// pass. + DrawMesh, + /// @member Draws an animated mesh if its material can be used in the current + /// render pass. + DrawMeshAnimated, + /// @member Draws a dynamically batched mesh if its material can be used in + /// the current render pass. + DrawMeshBatched, + /// @member Marks the end of a conditional block. + /// @see BBMOD_ERenderCommand.BeginConditionalBlock + EndConditionalBlock, + /// @member Pops the GPU state. + PopGpuState, + /// @member Pushes the GPU state. + PushGpuState, + /// @member Resets material. + ResetMaterial, + /// @member Resets shader. + ResetShader, + /// @member Enables/disables alpha testing. + SetGpuAlphaTestEnable, + /// @member Configures the alpha testing threshold value. + SetGpuAlphaTestRef, + /// @member Enables/disables alpha blending. + SetGpuBlendEnable, + /// @member Sets a blend mode. + SetGpuBlendMode, + /// @member Sets source and destination blend modes. + SetGpuBlendModeExt, + /// @member Sets source and destination blend modes with separate blend modes + /// for the alpha channel. + SetGpuBlendModeExtSepAlpha, + /// @member Enables/disables writing into individual color channels. + SetGpuColorWriteEnable, + /// @member Sets the culling mode. + SetGpuCullMode, + /// @member Configures fog. + SetGpuFog, + /// @member Enables/disables texture filtering. + SetGpuTexFilter, + /// @member Enables/disables texture filtering for a specific sampler. + SetGpuTexFilterExt, + /// @member Sets maximum anisotropy. + SetGpuTexMaxAniso, + /// @member Sets maximum anisotropy for a specific sampler. + SetGpuTexMaxAnisoExt, + /// @member Sets maximum mipmap level. + SetGpuTexMaxMip, + /// @member Sets maximum mipmap level for a specific sampler. + SetGpuTexMaxMipExt, + /// @member Sets miminum mipmap level. + SetGpuTexMinMip, + /// @member Sets miminum mipmap level for a specific sampler. + SetGpuTexMinMipExt, + /// @member Sets mipmapping bias. + SetGpuTexMipBias, + /// @member Sets mipmapping bias for a specific sampler. + SetGpuTexMipBiasExt, + /// @member Enables/disables mipmapping. + SetGpuTexMipEnable, + /// @member Enables/disables mipmapping for a specific sampler. + SetGpuTexMipEnableExt, + /// @member Sets mipmap filter function. + SetGpuTexMipFilter, + /// @member Sets mipmap filter function for a specific sampler. + SetGpuTexMipFilterExt, + /// @member Enables/disables texture repeat. + SetGpuTexRepeat, + /// @member Enables/disables texture repeat for a specific sampler. + SetGpuTexRepeatExt, + /// @member Sets the depth buffer test function. + SetGpuZFunc, + /// @member Enables/disables testing against the depth buffer. + SetGpuZTestEnable, + /// @member Enables/disables writing to the depth buffer. + SetGpuZWriteEnable, + /// @member Sets the projection matrix. + SetProjectionMatrix, + /// @member Sets a shader texture sampler. + SetSampler, + /// @member Sets a shader. + SetShader, + /// @member Sets a float shader uniform. + SetUniformFloat, + /// @member Sets a float2 shader uniform. + SetUniformFloat2, + /// @member Sets a float3 shader uniform. + SetUniformFloat3, + /// @member Sets a float4 shader uniform. + SetUniformFloat4, + /// @member Sets a float array shader uniform. + SetUniformFloatArray, + /// @member Sets an int shader uniform. + SetUniformInt, + /// @member Sets an int2 shader uniform. + SetUniformInt2, + /// @member Sets an int3 shader uniform. + SetUniformInt3, + /// @member Sets an int4 shader uniform. + SetUniformInt4, + /// @member Sets an int array shader uniform. + SetUniformIntArray, + /// @member Sets a matrix shader uniform. + SetUniformMatrix, + /// @member Sets a matrix array shader uniform. + SetUniformMatrixArray, + /// @member Sets the view matrix. + SetViewMatrix, + /// @member Sets the world matrix. + SetWorldMatrix, + /// @member Submits a vertex buffer. + SubmitVertexBuffer, +}; diff --git a/scripts/BBMOD_ERenderCommand/BBMOD_ERenderCommand.yy b/scripts/BBMOD_ERenderCommand/BBMOD_ERenderCommand.yy new file mode 100644 index 000000000..57508b6eb --- /dev/null +++ b/scripts/BBMOD_ERenderCommand/BBMOD_ERenderCommand.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_ERenderCommand", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_EmissionModule/BBMOD_EmissionModule.yy b/scripts/BBMOD_EmissionModule/BBMOD_EmissionModule.yy new file mode 100644 index 000000000..5e2e34ca4 --- /dev/null +++ b/scripts/BBMOD_EmissionModule/BBMOD_EmissionModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_EmissionModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Emission", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Emission.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_EmissionModule/bbmod_emissionmodule.gml b/scripts/BBMOD_EmissionModule/bbmod_emissionmodule.gml new file mode 100644 index 000000000..6558f256b --- /dev/null +++ b/scripts/BBMOD_EmissionModule/bbmod_emissionmodule.gml @@ -0,0 +1,23 @@ +/// @func BBMOD_EmissionModule([_count]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that spawns particles at the start of a particle +/// emitter's life. +/// +/// @param {Real} [_count] Number of particles to spawn. Defaults to 1. +function BBMOD_EmissionModule(_count=1) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} Number of particles to spawn. + Count = _count; + + static on_start = function (_emitter) { + repeat (Count) + { + _emitter.spawn_particle(); + } + }; +} diff --git a/scripts/BBMOD_EmissionOverTimeModule/BBMOD_EmissionOverTimeModule.gml b/scripts/BBMOD_EmissionOverTimeModule/BBMOD_EmissionOverTimeModule.gml new file mode 100644 index 000000000..0f2b8e8c1 --- /dev/null +++ b/scripts/BBMOD_EmissionOverTimeModule/BBMOD_EmissionOverTimeModule.gml @@ -0,0 +1,39 @@ +/// @func BBMOD_EmissionOverTimeModule([_count[, _interval]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that emits particles over time. +/// +/// @param {Real} [_count] Number of particles to spawn each time. Defaults +/// to 1. +/// @param {Real} [_interval] Number of seconds to wait before spawning +/// next particles. Defaults to 1.0. +function BBMOD_EmissionOverTimeModule(_count=1, _interval=1.0) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} Number of particles to spawn each time. Default + /// value is 1. + Count = _count; + + /// @var {Real} Number of seconds to wait before spawning + /// next particles. Default value is 1.0. + Interval = _interval; + + /// @var {Real} + /// @private + __timer = 0.0; + + static on_update = function (_emitter, _deltaTime) { + __timer += _deltaTime * 0.000001; + if (__timer >= Interval) + { + repeat (Count) + { + _emitter.spawn_particle(); + } + __timer = 0.0; + } + }; +} diff --git a/scripts/BBMOD_EmissionOverTimeModule/BBMOD_EmissionOverTimeModule.yy b/scripts/BBMOD_EmissionOverTimeModule/BBMOD_EmissionOverTimeModule.yy new file mode 100644 index 000000000..139885bc0 --- /dev/null +++ b/scripts/BBMOD_EmissionOverTimeModule/BBMOD_EmissionOverTimeModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_EmissionOverTimeModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Emission", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Emission.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Exception/BBMOD_Exception.gml b/scripts/BBMOD_Exception/BBMOD_Exception.gml new file mode 100644 index 000000000..046c78a33 --- /dev/null +++ b/scripts/BBMOD_Exception/BBMOD_Exception.gml @@ -0,0 +1,16 @@ +/// @func BBMOD_Exception([_msg]) +/// +/// @extends BBMOD_Class +/// +/// @desc The base struct for exceptions thrown by the BBMOD library. +/// +/// @param {String} [_msg] An exception message. Defaults to an empty string. +function BBMOD_Exception(_msg="") + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {string} The exception message. + /// @readonly + Message = _msg; +} diff --git a/scripts/BBMOD_Exception/BBMOD_Exception.yy b/scripts/BBMOD_Exception/BBMOD_Exception.yy new file mode 100644 index 000000000..59344df35 --- /dev/null +++ b/scripts/BBMOD_Exception/BBMOD_Exception.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Exception", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Exceptions", + "path": "folders/_Extensions/BBMOD/Core/Exceptions.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_FrustumCollider/BBMOD_FrustumCollider.gml b/scripts/BBMOD_FrustumCollider/BBMOD_FrustumCollider.gml new file mode 100644 index 000000000..a2b95e606 --- /dev/null +++ b/scripts/BBMOD_FrustumCollider/BBMOD_FrustumCollider.gml @@ -0,0 +1,259 @@ +/// @enum Enumeration of frustum planes. +enum BBMOD_EFrustumPlane +{ + /// @member The left plane. + Left, + /// @member The right plane. + Right, + /// @member The top plane. + Top, + /// @member The bottom plane. + Bottom, + /// @member The near plane. + Near, + /// @member The far plane. + Far, + /// @member Total number of frustum planes. + SIZE +}; + +/// @func BBMOD_FrustumCollider() +/// +/// @extends BBMOD_Collider +/// +/// @desc A frustum collider. +/// +/// @see BBMOD_AABBCollider +/// @see BBMOD_PlaneCollider +/// @see BBMOD_SphereCollider +function BBMOD_FrustumCollider() + : BBMOD_Collider() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Array} + /// @see BBMOD_PlaneCollider + /// @see BBMOD_EFrustumPlane + Planes = [ + new BBMOD_PlaneCollider(), + new BBMOD_PlaneCollider(), + new BBMOD_PlaneCollider(), + new BBMOD_PlaneCollider(), + new BBMOD_PlaneCollider(), + new BBMOD_PlaneCollider(), + ]; + + /// @func FromViewProjectionMatrix(_vp) + /// + /// @desc Initializes the frustum from a view-projection matrix. + /// + /// @param {Array} _vp The view-projection matrix. + /// + /// @return {Struct.BBMOD_FrustumCollider} Returns `self`. + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Camera.cpp#L269 + static FromViewProjectionMatrix = function (_vp) { + var _col1 = new BBMOD_Vec3(_vp[0], _vp[4], _vp[ 8]); + var _col2 = new BBMOD_Vec3(_vp[1], _vp[5], _vp[ 9]); + var _col3 = new BBMOD_Vec3(_vp[2], _vp[6], _vp[10]); + var _col4 = new BBMOD_Vec3(_vp[3], _vp[7], _vp[11]); + + // Find plane magnitudes + Planes[BBMOD_EFrustumPlane.Left ].Normal = _col4.Add(_col1); + Planes[BBMOD_EFrustumPlane.Right ].Normal = _col4.Sub(_col1); + Planes[BBMOD_EFrustumPlane.Bottom].Normal = _col4.Add(_col2); + Planes[BBMOD_EFrustumPlane.Top ].Normal = _col4.Sub(_col2); + Planes[BBMOD_EFrustumPlane.Near ].Normal = /*_col4.Add(*/_col3/*)*/; + Planes[BBMOD_EFrustumPlane.Far ].Normal = _col4.Sub(_col3); + + // Find plane distances + var _vp12 = _vp[12]; + var _vp13 = _vp[13]; + var _vp14 = _vp[14]; + var _vp15 = _vp[15]; + + Planes[BBMOD_EFrustumPlane.Left ].Distance = _vp15 + _vp12; + Planes[BBMOD_EFrustumPlane.Right ].Distance = _vp15 - _vp12; + Planes[BBMOD_EFrustumPlane.Bottom].Distance = _vp15 + _vp13; + Planes[BBMOD_EFrustumPlane.Top ].Distance = _vp15 - _vp13; + Planes[BBMOD_EFrustumPlane.Near ].Distance = /*_vp15 +*/ _vp14; + Planes[BBMOD_EFrustumPlane.Far ].Distance = _vp15 - _vp14; + + // Normalize all 6 planes + for (var i = 0; i < BBMOD_EFrustumPlane.SIZE; ++i) + { + with (Planes[i]) + { + var _n = 1.0 / Normal.Length(); + Normal = Normal.Scale(_n); + Distance *= _n; + } + } + + return self; + }; + + /// @func FromCamera(_camera) + /// + /// @desc Initializes the frustum using a camera's view-projection matrix. + /// + /// @param {Struct.BBMOD_BaseCamera} _camera The camera. + /// + /// @return {Struct.BBMOD_FrustumCollider} Returns `self`. + /// + /// @see BBMOD_BaseCamera.ViewProjectionMatrix + static FromCamera = function (_camera) { + gml_pragma("forceinline"); + FromViewProjectionMatrix(_camera.ViewProjectionMatrix); + return self; + }; + + /// @func __intersectPlanes(_p1, _p2, _p3) + /// + /// @desc + /// + /// @param {Struct.BBMOD_PlaneCollider} _p1 + /// @param {Struct.BBMOD_PlaneCollider} _p2 + /// @param {Struct.BBMOD_PlaneCollider} _p3 + /// + /// @returns {Struct.BBMOD_Vec3,Undefined} + /// + /// @private + // Source: https://donw.io/post/frustum-point-extraction/ + static __intersectPlanes = function (_p0, _p1, _p2) { + var bxc = _p1.Normal.Cross(_p2.Normal); + var cxa = _p2.Normal.Cross(_p0.Normal); + var axb = _p0.Normal.Cross(_p1.Normal); + var r = bxc.Scale(-_p0.Distance).Sub(cxa.Scale(_p1.Distance)).Sub(axb.Scale(_p2.Distance)); + return r.Scale(1.0 / _p0.Normal.Dot(bxc)); + }; + + /// @func GetCorners() + /// + /// @desc Retrieves an array of frustum corners. + /// + /// @return {Array} The array of frustum corners in order + /// `nearTopLeft`, `nearTopRight`, `nearBottomLeft`, `nearBottomRight`, + /// `farTopLeft`, `farTopRight`, `farBottomLeft` and `farBottomRight`. + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L1850 + static GetCorners = function () { + var _near = Planes[BBMOD_EFrustumPlane.Near]; + var _far = Planes[BBMOD_EFrustumPlane.Far]; + var _top = Planes[BBMOD_EFrustumPlane.Top]; + var _bottom = Planes[BBMOD_EFrustumPlane.Bottom]; + var _left = Planes[BBMOD_EFrustumPlane.Left]; + var _right = Planes[BBMOD_EFrustumPlane.Right]; + + return [ + __intersectPlanes(_near, _top, _left), + __intersectPlanes(_near, _top, _right), + __intersectPlanes(_near, _bottom, _left), + __intersectPlanes(_near, _bottom, _right), + __intersectPlanes(_far, _top, _left), + __intersectPlanes(_far, _top, _right), + __intersectPlanes(_far, _bottom, _left), + __intersectPlanes(_far, _bottom, _right), + ]; + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L1861 + static TestPoint = function (_point) { + for (var i = 0; i < BBMOD_EFrustumPlane.SIZE; ++i) + { + var _plane = Planes[i]; + if ((_point.Dot(_plane.Normal) + _plane.Distance) < 0.0) + { + return false; + } + } + return true; + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L1874 + static TestSphere = function (_sphere) { + for (var i = 0; i < BBMOD_EFrustumPlane.SIZE; ++i) + { + var _plane = Planes[i]; + if ((_sphere.Position.Dot(_plane.Normal) + _plane.Distance) < -_sphere.Radius) + { + return false; + } + } + return true; + }; + + static DrawDebug = function (_color=c_white, _alpha=1.0) { + var _vbuffer = global.__bbmodVBufferDebug; + + var _corners = GetCorners(); + var _ntl = _corners[0]; + var _ntlx = _ntl.X; + var _ntly = _ntl.Y; + var _ntlz = _ntl.Z; + var _ntr = _corners[1]; + var _ntrx = _ntr.X; + var _ntry = _ntr.Y; + var _ntrz = _ntr.Z; + var _nbl = _corners[2]; + var _nblx = _nbl.X; + var _nbly = _nbl.Y; + var _nblz = _nbl.Z; + var _nbr = _corners[3]; + var _nbrx = _nbr.X; + var _nbry = _nbr.Y; + var _nbrz = _nbr.Z; + var _ftl = _corners[4]; + var _ftlx = _ftl.X; + var _ftly = _ftl.Y; + var _ftlz = _ftl.Z; + var _ftr = _corners[5]; + var _ftrx = _ftr.X; + var _ftry = _ftr.Y; + var _ftrz = _ftr.Z; + var _fbl = _corners[6]; + var _fblx = _fbl.X; + var _fbly = _fbl.Y; + var _fblz = _fbl.Z; + var _fbr = _corners[7]; + var _fbrx = _fbr.X; + var _fbry = _fbr.Y; + var _fbrz = _fbr.Z; + + vertex_begin(_vbuffer, BBMOD_VFORMAT_DEBUG.Raw); + + // Near plane + vertex_position_3d(_vbuffer, _ntlx, _ntly, _ntlz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _ntrx, _ntry, _ntrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _ntrx, _ntry, _ntrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _nbrx, _nbry, _nbrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _nbrx, _nbry, _nbrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _nblx, _nbly, _nblz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _nblx, _nbly, _nblz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _ntlx, _ntly, _ntlz); vertex_color(_vbuffer, _color, _alpha); + + // Far plane + vertex_position_3d(_vbuffer, _ftlx, _ftly, _ftlz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _ftrx, _ftry, _ftrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _ftrx, _ftry, _ftrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _fbrx, _fbry, _fbrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _fbrx, _fbry, _fbrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _fblx, _fbly, _fblz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _fblx, _fbly, _fblz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _ftlx, _ftly, _ftlz); vertex_color(_vbuffer, _color, _alpha); + + // Sides + vertex_position_3d(_vbuffer, _ntlx, _ntly, _ntlz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _ftlx, _ftly, _ftlz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _ntrx, _ntry, _ntrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _ftrx, _ftry, _ftrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _nbrx, _nbry, _nbrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _fbrx, _fbry, _fbrz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _nblx, _nbly, _nblz); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _fblx, _fbly, _fblz); vertex_color(_vbuffer, _color, _alpha); + + vertex_end(_vbuffer); + + vertex_submit(_vbuffer, pr_linelist, -1); + + return self; + }; +} diff --git a/scripts/BBMOD_FrustumCollider/BBMOD_FrustumCollider.yy b/scripts/BBMOD_FrustumCollider/BBMOD_FrustumCollider.yy new file mode 100644 index 000000000..f06908a6f --- /dev/null +++ b/scripts/BBMOD_FrustumCollider/BBMOD_FrustumCollider.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_FrustumCollider", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Raycasting", + "path": "folders/_Extensions/BBMOD/Raycasting.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Gizmo/BBMOD_Gizmo.gml b/scripts/BBMOD_Gizmo/BBMOD_Gizmo.gml new file mode 100644 index 000000000..4d0318e66 --- /dev/null +++ b/scripts/BBMOD_Gizmo/BBMOD_Gizmo.gml @@ -0,0 +1,1224 @@ +/// @enum Enumeration of edit spaces. +enum BBMOD_EEditSpace +{ + /// @member Edit instances in world-space. + Global, + /// @member Edit instance relatively to its transformation. + Local, + /// @member Total number of members of this enum. + SIZE, +}; + +/// @enum Enumeration of edit types. +enum BBMOD_EEditType +{ + /// @member Translate selected instances. + Position, + /// @member Rotate selected instances. + Rotation, + /// @member Scale selected instances. + Scale, + /// @member Total number of members of this enum. + SIZE, +}; + +/// @enum Enumeration of edit axes. +enum BBMOD_EEditAxis +{ + /// @member No edit. + None = 0, + /// @member Edit on X axis. + X = $1, + /// @member Edit on Y axis. + Y = $10, + /// @member Edit on Z axis. + Z = $100, + /// @member Edit on all axes. + All = $111, +}; + +/// @func BBMOD_Gizmo([_size]) +/// +/// @extends BBMOD_Class +/// +/// @desc A gizmo for transforming instances. +/// +/// @param {Real} [_size] The size of the gizmo. Default value is 10 units. +/// +/// @note This requries synchronnous loading of models, therefore it cannot +/// be used on platforms like HTML5, which require asynchronnous loading. +/// You also **must** use {@link BBMOD_Camera} for the gizmo to work properly! +function BBMOD_Gizmo(_size=10.0) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Array} Gizmo models for individual edit modes. + /// @note Please note that these are not loaded asynchronnously, therefore + /// the gizmo cannot be used on platforms that require asynchronnous loading, + /// like HTML5! + /// @see BBMOD_EEditType + /// @readonly + static Models = undefined; + + /// @var {Array} Materials used when mouse-picking + /// the gizmo. + static MaterialsSelect = undefined; + + if (Models == undefined) + { + var _shaderSelect = new BBMOD_BaseShader( + BBMOD_ShGizmoSelect, BBMOD_VFORMAT_DEFAULT); + var _materialSelect = new BBMOD_BaseMaterial(_shaderSelect); + _materialSelect.BaseOpacity = sprite_get_texture(BBMOD_SprGizmo, 1); + MaterialsSelect = [_materialSelect]; + + var _shader = new BBMOD_BaseShader(BBMOD_ShGizmo, BBMOD_VFORMAT_DEFAULT); + var _material = new BBMOD_BaseMaterial(_shader); + _material.BaseOpacity = sprite_get_texture(BBMOD_SprGizmo, 0); + + var _modelMove = new BBMOD_Model("Data/BBMOD/Models/GizmoMove.bbmod") + .freeze(); + // TODO: Fix gizmo model + _modelMove.RootNode.Transform = new BBMOD_DualQuaternion(); + _modelMove.Materials[@ 0] = _material; + + var _modelScale = new BBMOD_Model("Data/BBMOD/Models/GizmoScale.bbmod") + .freeze(); + _modelScale.Materials[@ 0] = _material; + + var _modelRotate = new BBMOD_Model("Data/BBMOD/Models/GizmoRotate.bbmod") + .freeze(); + _modelRotate.Materials[@ 0] = _material; + + Models = [ + _modelMove, + _modelRotate, + _modelScale, + ]; + } + + /// @var {Bool} If `true` then the gizmo is editing selected instances. + IsEditing = false; + + /// @var {Struct.BBMOD_Vec2} Screen-space coordinates to lock the mouse + /// cursor at or `undefined`. + /// @private + __mouseLockAt = undefined; + + /// @var {Struct.BBMOD_Vec3} World-space offset from the mouse to the gizmo + /// or `undefined`. + /// @private + __mouseOffset = undefined; + + /// @var {Constant.Cursor} The cursor used before editing started. + /// @private + __cursorBackup = undefined; + + /// @var {Bool} Enables snapping to grid when moving objects. Default value + /// is `true`. + /// @see BBMOD_Gizmo.GridSize + EnableGridSnap = true; + + /// @var {Struct.BBMOD_Vec3} The size of the grid. Default value is + /// `(1, 1, 1)`. + /// @see BBMOD_Gizmo.EnableGridSnap + GridSize = new BBMOD_Vec3(1.0); + + /// @var {Bool} Enables angle snapping when rotating objects. Default value + /// is `true`. + /// @see BBMOD_Gizmo.AngleSnap + EnableAngleSnap = true; + + /// @var {Real} Angle snapping size. Default value is 1. + /// @see BBMOD_Gizmo.EnableAngleSnap + AngleSnap = 1.0; + + /// @var {Real} Determines the space in which are the selected instances + /// transformed. Use values from {@link BBMOD_EEditSpace}. + EditSpace = BBMOD_EEditSpace.Global; + + /// @var {Real} Determines how are the selected instances transformed + /// (translated/rotated/scaled. Use values from {@link BBMOD_EEditType}. + EditType = BBMOD_EEditType.Position; + + /// @var {Real} Determines on which axes are the selected instances edited. + /// Use values from {@link BBMOD_EEditAxis}. + EditAxis = BBMOD_EEditAxis.None; + + /// @var {Constant.MouseButton} The mouse button used for dragging the gizmo. + /// Default is `mb_left`. + ButtonDrag = mb_left; + + /// @var {Constant.VirtualKey} The virtual key used to switch to the next + /// edit type. Default is `vk_tab`. + /// @see BBMOD_Gizmo.EditType + KeyNextEditType = vk_tab; + + /// @var {Constant.VirtualKey} The virtual key used to switch to the next + /// edit space. Default is `vk_space`. + /// @see BBMOD_Gizmo.EditSpace + KeyNextEditSpace = vk_space; + + /// @var {Constant.VirtualKey} The virtual key used to increase + /// speed of editing (e.g. rotate objects by a larger angle). Default is + /// `vk_shift`. + KeyEditFaster = vk_shift; + + /// @var {Constant.VirtualKey} The virtual key used to decrease + /// speed of editing (e.g. rotate objects by a smaller angle). Default is + /// `vk_control`. + KeyEditSlower = vk_control; + + /// @var {Constant.VirtualKey} The virtual key used to cancel editing and + /// revert changes. Default is `vk_escape`. + KeyCancel = vk_escape; + + /// @var {Constant.VirtualKey} The virtual key used to ignore grid and + /// angle snapping when they are enabled. Default is `vk_alt`. + /// @see BBMOD_Gizmo.EnableGridSnap + /// @see BBMOD_Gizmo.EnableAngleSnap + KeyIgnoreSnap = vk_alt; + + /// @var {Real} The size of the gizmo. Default value is 10. + Size = _size; + + /// @var {Struct.BBMOD_Vec3} The gizmo's position in world-space. + /// @readonly + Position = new BBMOD_Vec3(); + + /// @var {Struct.BBMOD_Vec3} The gizmo's position in world-space before + /// editing started or `undefined`. + /// @private + __positionBackup = undefined; + + /// @var {Struct.BBMOD_Vec3} The gizmo's rotation in euler angles. + Rotation = new BBMOD_Vec3(); + + /// @var {Id.DsList} A list of selected instances. + /// @readonly + Selected = ds_list_create(); + + /// @var {Id.DsList} A list of additional data required for editing + /// instances, e.g. their original offset from the gizmo, rotation and scale. + /// @private + __instanceData = ds_list_create(); + + /// @var {Struct.BBMOD_Vec3} The current scaling factor of selected instances. + /// @private + __scaleBy = new BBMOD_Vec3(0.0); + + /// @var {Struct.BBMOD_Vec3} The current euler angles we are rotating selected + /// instances by. + /// @private + __rotateBy = new BBMOD_Vec3(0.0); + + /// @var {Function} A function that the gizmo uses to check whether an instance + /// exists. Must take the instance as the first argument and return a bool. + /// Defaults a function that returns the result of `instance_exists`. + InstanceExists = function (_instance) { + gml_pragma("forceinline"); + return instance_exists(_instance); + }; + + /// @var {Function} A function that the gizmo uses to retrieve an instance's + /// global matrix. Normally this is an identity matrix. If the instance is + /// attached to another instance for example, then this will be that + /// instance's transformation matrix. Must take the instance as the first + /// argument and return a {@link BBMOD_Matrix}. Defaults to a function that + /// always returns an identity matrix. + GetInstanceGlobalMatrix = function (_instance) { + gml_pragma("forceinline"); + return new BBMOD_Matrix(); + }; + + /// @var {Function} A function that the gizmo uses to retrieve an instance's + /// position on the X axis. Must take the instance as the first argument and + /// return a real. Defaults to a function that returns the instance's `x` + /// variable. + GetInstancePositionX = function (_instance) { + gml_pragma("forceinline"); + return _instance.x; + }; + + /// @var {Function} A function that the gizmo uses to change an instance's + /// position on the X axis. Must take the instance as the first argument and + /// its new position on the X axis as the second argument. Defaults to a + /// function that assings the new position to the instance's `x` variable. + SetInstancePositionX = function (_instance, _x) { + gml_pragma("forceinline"); + _instance.x = _x; + }; + + /// @var {Function} A function that the gizmo uses to retrieve an instance's + /// position on the Y axis. Must take the instance as the first argument and + /// return a real. Defaults to a function that returns the instance's `y` + /// variable. + GetInstancePositionY = function (_instance) { + gml_pragma("forceinline"); + return _instance.y; + }; + + /// @var {Function} A function that the gizmo uses to change an instance's + /// position on the Y axis. Must take the instance as the first argument and + /// its new position on the Y axis as the second argument. Defaults to a + /// function that assings the new position to the instance's `y` variable. + SetInstancePositionY = function (_instance, _y) { + gml_pragma("forceinline"); + _instance.y = _y; + }; + + /// @var {Function} A function that the gizmo uses to retrieve an instance's + /// position on the Z axis. Must take the instance as the first argument and + /// return a real. Defaults to a function that returns the instance's `z` + /// variable. + GetInstancePositionZ = function (_instance) { + gml_pragma("forceinline"); + return _instance.z; + }; + + /// @var {Function} A function that the gizmo uses to change an instance's + /// position on the Z axis. Must take the instance as the first argument and + /// its new position on the Z axis as the second argument. Defaults to a + /// function that assings the new position to the instance's `Z` variable. + SetInstancePositionZ = function (_instance, _z) { + gml_pragma("forceinline"); + _instance.z = _z; + }; + + /// @var {Function} A function that the gizmo uses to retrieve an instance's + /// rotation on the X axis. Must take the instance as the first argument and + /// return a real. Defaults to a function that always returns 0. + GetInstanceRotationX = function (_instance) { + gml_pragma("forceinline"); + return 0.0; + }; + + /// @var {Function} A function that the gizmo uses to change an instance's + /// rotation on the X axis. Must take the instance as the first argument and + /// its new rotation on the X axis as the second argument. Defaults to a + /// function that does not do anything. + SetInstanceRotationX = function (_instance, _x) { + gml_pragma("forceinline"); + }; + + /// @var {Function} A function that the gizmo uses to retrieve an instance's + /// rotation on the Y axis. Must take the instance as the first argument and + /// return a real. Defaults to a function that always returns 0. + GetInstanceRotationY = function (_instance) { + gml_pragma("forceinline"); + return 0.0; + }; + + /// @var {Function} A function that the gizmo uses to change an instance's + /// rotation on the Y axis. Must take the instance as the first argument and + /// its new rotation on the Y axis as the second argument. Defaults to a + /// function that does not do anything. + SetInstanceRotationY = function (_instance, _y) { + gml_pragma("forceinline"); + }; + + /// @var {Function} A function that the gizmo uses to retrieve an instance's + /// rotation on the Z axis. Must take the instance as the first argument and + /// return a real. Defaults to a function that returns the instance's + /// `image_angle` variable. + GetInstanceRotationZ = function (_instance) { + gml_pragma("forceinline"); + return _instance.image_angle; + }; + + /// @var {Function} A function that the gizmo uses to change an instance's + /// rotation on the Z axis. Must take the instance as the first argument and + /// its new rotation on the Z axis as the second argument. Defaults to a + /// function that assings the new rotation to the instance's `image_angle` + /// variable. + SetInstanceRotationZ = function (_instance, _z) { + gml_pragma("forceinline"); + _instance.image_angle = _z; + }; + + /// @var {Function} A function that the gizmo uses to retrieve an instance's + /// scale on the X axis. Must take the instance as the first argument and + /// return a real. Defaults to a function that returns the instance's + /// `image_xscale` variable. + GetInstanceScaleX = function (_instance) { + gml_pragma("forceinline"); + return _instance.image_xscale; + }; + + /// @var {Function} A function that the gizmo uses to change an instance's + /// scale on the X axis. Must take the instance as the first argument and + /// its new scale on the X axis as the second argument. Defaults to a + /// function that assings the new scale to the instance's `image_xscale` + /// variable. + SetInstanceScaleX = function (_instance, _x) { + gml_pragma("forceinline"); + _instance.image_xscale = _x; + }; + + /// @var {Function} A function that the gizmo uses to retrieve an instance's + /// scale on the Y axis. Must take the instance as the first argument and + /// return a real. Defaults to a function that returns the instance's + /// `image_yscale` variable. + GetInstanceScaleY = function (_instance) { + gml_pragma("forceinline"); + return _instance.image_yscale; + }; + + /// @var {Function} A function that the gizmo uses to change an instance's + /// scale on the Y axis. Must take the instance as the first argument and + /// its new scale on the Y axis as the second argument. Defaults to a + /// function that assings the new scale to the instance's `image_yscale` + /// variable. + SetInstanceScaleY = function (_instance, _y) { + gml_pragma("forceinline"); + _instance.image_yscale = _y; + }; + + /// @var {Function} A function that the gizmo uses to retrieve an instance's + /// scale on the Z axis. Must take the instance as the first argument and + /// return a real. Defaults to a function that always returns 1. + GetInstanceScaleZ = function (_instance) { + gml_pragma("forceinline"); + return 1.0; + }; + + /// @var {Function} A function that the gizmo uses to change an instance's + /// scale on the Z axis. Must take the instance as the first argument and + /// its new scale on the Z axis as the second argument. Defaults to a + /// function that does not do anything. + SetInstanceScaleZ = function (_instance, _z) { + gml_pragma("forceinline"); + }; + + /// @func get_instance_position_vec3(_instance) + /// + /// @desc Retrieves an instance's position as {@link BBMOD_Vec3}. + /// + /// @param {Id.Instance} _instance The ID of the instance. + /// + /// @return {Struct.BBMOD_Vec3} The instance's position. + static get_instance_position_vec3 = function (_instance) { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + GetInstancePositionX(_instance), + GetInstancePositionY(_instance), + GetInstancePositionZ(_instance)); + }; + + /// @func set_instance_position_vec3(_instance, _position) + /// + /// @desc Changes an instance's position using a {@link BBMOD_Vec3}. + /// + /// @param {Id.Instance} _instance The ID of the instance. + /// @param {Struct.BBMOD_Vec3} _position The new position of the instance. + /// + /// @return {Struct.BBMOD_Gizmo} Returns `self`. + static set_instance_position_vec3 = function (_instance, _position) { + gml_pragma("forceinline"); + SetInstancePositionX(_instance, _position.X); + SetInstancePositionY(_instance, _position.Y); + SetInstancePositionZ(_instance, _position.Z); + return self; + }; + + /// @func get_instance_rotation_vec3(_instance) + /// + /// @desc Retrieves an instance's rotation as {@link BBMOD_Vec3}. + /// + /// @param {Id.Instance} _instance The ID of the instance. + /// + /// @return {Struct.BBMOD_Vec3} The instance's rotation in euler angles. + static get_instance_rotation_vec3 = function (_instance) { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + GetInstanceRotationX(_instance), + GetInstanceRotationY(_instance), + GetInstanceRotationZ(_instance)); + }; + + /// @func set_instance_rotation_vec3(_instance, _rotation) + /// + /// @desc Changes an instance's rotation using a {@link BBMOD_Vec3}. + /// + /// @param {Id.Instance} _instance The ID of the instance. + /// @param {Struct.BBMOD_Vec3} _rotation The new rotation of the instance + /// in euler angles. + /// + /// @return {Struct.BBMOD_Gizmo} Returns `self`. + static set_instance_rotation_vec3 = function (_instance, _rotation) { + gml_pragma("forceinline"); + SetInstanceRotationX(_instance, _rotation.X); + SetInstanceRotationY(_instance, _rotation.Y); + SetInstanceRotationZ(_instance, _rotation.Z); + return self; + }; + + /// @func get_instance_scale_vec3(_instance) + /// + /// @desc Retrieves an instance's scale as {@link BBMOD_Vec3}. + /// + /// @param {Id.Instance} _instance The ID of the instance. + /// + /// @return {Struct.BBMOD_Vec3} The instance's scale. + static get_instance_scale_vec3 = function (_instance) { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + GetInstanceScaleX(_instance), + GetInstanceScaleY(_instance), + GetInstanceScaleZ(_instance)); + }; + + /// @func set_instance_scale_vec3(_instance, _scale) + /// + /// @desc Changes an instance's scale using a {@link BBMOD_Vec3}. + /// + /// @param {Id.Instance} _instance The ID of the instance. + /// @param {Struct.BBMOD_Vec3} _scale The new scale of the instance. + /// + /// @return {Struct.BBMOD_Gizmo} Returns `self`. + static set_instance_scale_vec3 = function (_instance, _scale) { + gml_pragma("forceinline"); + SetInstanceScaleX(_instance, _scale.X); + SetInstanceScaleY(_instance, _scale.Y); + SetInstanceScaleZ(_instance, _scale.Z); + return self; + }; + + /// @func select(_instance) + /// + /// @desc Adds an instance to selection. + /// + /// @param {Id.Instance} _instance The instance to select. + /// + /// @return {Struct.BBMOD_Gizmo} Returns `self`. + static select = function (_instance) { + gml_pragma("forceinline"); + if (!is_selected(_instance)) + { + ds_list_add(Selected, _instance); + ds_list_add(__instanceData, { + Offset: new BBMOD_Vec3(), + Rotation: new BBMOD_Vec3(), + Scale: new BBMOD_Vec3(), + }); + } + return self; + }; + + /// @func is_selected(_instance) + /// + /// @desc Checks whether an instance is selected. + /// + /// @param {Id.Instance} _instance The instance to check. + /// + /// @return {Bool} Returns `true` if the instance is selected. + static is_selected = function (_instance) { + gml_pragma("forceinline"); + return (ds_list_find_index(Selected, _instance) != -1); + }; + + /// @func unselect(_instance) + /// + /// @desc Removes an instance from selection. + /// + /// @param {Id.Instance} _instance The instance to unselect. + /// + /// @return {Struct.BBMOD_Gizmo} Returns `self`. + static unselect = function (_instance) { + gml_pragma("forceinline"); + var _index = ds_list_find_index(Selected, _instance); + if (_index != -1) + { + ds_list_delete(Selected, _index); + ds_list_delete(__instanceData, _index); + } + return self; + }; + + /// @func toggle_select(_instance) + /// + /// @desc Unselects an instance if it's selected, or selects if it isn't. + /// + /// @param {Id.Instance} _instance The instance to toggle selection of. + /// + /// @return {Struct.BBMOD_Gizmo} Returns `self`. + static toggle_select = function (_instance) { + gml_pragma("forceinline"); + if (is_selected(_instance)) + { + unselect(_instance); + } + else + { + select(_instance); + } + return self; + }; + + /// @func clear_selection() + /// + /// @desc Removes all instances from selection. + /// + /// @return {Struct.BBMOD_Gizmo} Returns `self`. + static clear_selection = function () { + gml_pragma("forceinline"); + ds_list_clear(Selected); + ds_list_clear(__instanceData); + return self; + }; + + /// @func intersect_ray_plane(_origin, _direction, _plane, _normal) + /// + /// @desc Intersects a ray with a plane. + /// + /// @param {Struct.BBMOD_Vec3} _origin The ray origin. + /// @param {Struct.BBMOD_Vec3} _direction The ray direction. + /// @param {Struct.BBMOD_Vec3} _plane The plane origin. + /// @param {Struct.BBMOD_Vec3} _normal The plane normal. + /// + /// @return {Struct.BBMOD_Vec3} The point of intersection or `undefined`. + /// + /// @private + static intersect_ray_plane = function (_origin, _direction, _plane, _normal) { + var _dot = _direction.Dot(_normal); + if (_dot == 0.0) + { + return undefined; + } + var _t = -(_origin.Sub(_plane).Dot(_normal) / _dot); + return _origin.Add(_direction.Scale(_t)); + }; + + /// @func update_position() + /// + /// @desc Updates the gizmo's position, based on its selected instances. + /// + /// @return {Struct.BBMOD_Gizmo} Returns `self`. + static update_position = function () { + var _size = ds_list_size(Selected); + var _posX = 0.0; + var _posY = 0.0; + var _posZ = 0.0; + + for (var i = _size - 1; i >= 0; --i) + { + var _instance = Selected[| i]; + + if (!InstanceExists(_instance)) + { + ds_list_delete(Selected, i); + ds_list_delete(__instanceData, i); + --_size; + continue; + } + + _posX += GetInstancePositionX(_instance); + _posY += GetInstancePositionY(_instance); + _posZ += GetInstancePositionZ(_instance); + } + + if (_size > 0) + { + _posX /= _size; + _posY /= _size; + _posZ /= _size; + + Position.Set(_posX, _posY, _posZ); + + if (EditSpace == BBMOD_EEditSpace.Local) + { + var _lastSelected = Selected[| _size - 1]; + Rotation.Set( + GetInstanceRotationX(_lastSelected), + GetInstanceRotationY(_lastSelected), + GetInstanceRotationZ(_lastSelected)); + } + else + { + Rotation.Set(0.0, 0.0, 0.0); + } + } + + return self; + }; + + /// @func update(_deltaTime) + /// + /// @desc Updates the gizmo. Should be called every frame. + /// + /// @param {Real} _deltaTime How much time has passed since the last frame + /// (in microseconds). + /// + /// @return {Struct.BBMOD_Gizmo} Returns `self`. + /// + /// @note This requires you to use a {@link BBMOD_BaseCamera} and it will + /// not do anything if its [apply](./BBMOD_BaseCamera.apply.html) method has + /// not been called yet! + static update = function (_deltaTime) { + if (!global.__bbmodCameraCurrent) + { + return self; + } + + //////////////////////////////////////////////////////////////////////// + // + // Not editing or finished editing + // + if (!IsEditing || !mouse_check_button(ButtonDrag)) + { + if (KeyNextEditType != undefined + && keyboard_check_pressed(KeyNextEditType)) + { + if (++EditType >= BBMOD_EEditType.SIZE) + { + EditType = 0; + } + } + + if (KeyNextEditSpace != undefined + && keyboard_check_pressed(KeyNextEditSpace)) + { + if (++EditSpace >= BBMOD_EEditSpace.SIZE) + { + EditSpace = 0; + } + } + + // Compute gizmo's new position + var _size = ds_list_size(Selected); + var _posX = 0.0; + var _posY = 0.0; + var _posZ = 0.0; + + for (var i = _size - 1; i >= 0; --i) + { + var _instance = Selected[| i]; + + if (!InstanceExists(_instance)) + { + ds_list_delete(Selected, i); + ds_list_delete(__instanceData, i); + --_size; + continue; + } + + _posX += GetInstancePositionX(_instance); + _posY += GetInstancePositionY(_instance); + _posZ += GetInstancePositionZ(_instance); + } + + if (_size > 0) + { + _posX /= _size; + _posY /= _size; + _posZ /= _size; + + Position.Set(_posX, _posY, _posZ); + + if (EditSpace == BBMOD_EEditSpace.Local) + { + var _lastSelected = Selected[| _size - 1]; + var _mat = GetInstanceGlobalMatrix(_lastSelected); + var _mat2 = new BBMOD_Matrix().RotateEuler(get_instance_rotation_vec3(_lastSelected)); + var _mat3 = _mat2.Mul(_mat); + var _euler = _mat3.ToEuler(); + Rotation.FromArray(_euler); + } + else + { + Rotation.Set(0.0, 0.0, 0.0); + } + } + + // Store instance data + for (var i = _size - 1; i >= 0; --i) + { + var _instance = Selected[| i]; + var _data = __instanceData[| i]; + _data.Offset = get_instance_position_vec3(_instance).Sub(Position); + _data.Rotation = get_instance_rotation_vec3(_instance); + _data.Scale = get_instance_scale_vec3(_instance); + } + + // Clear properties used when editing + IsEditing = false; + __mouseOffset = undefined; + __mouseLockAt = undefined; + __positionBackup = undefined; + if (__cursorBackup != undefined) + { + window_set_cursor(__cursorBackup); + __cursorBackup = undefined; + } + __scaleBy = new BBMOD_Vec3(0.0); + __rotateBy = new BBMOD_Vec3(0.0); + + return self; + } + + //////////////////////////////////////////////////////////////////////// + // + // Editing + // + var _mouseX = window_mouse_get_x(); + var _mouseY = window_mouse_get_y(); + + if (!__mouseLockAt) + { + __mouseLockAt = new BBMOD_Vec2(_mouseX, _mouseY); + __cursorBackup = window_get_cursor(); + } + + var _quaternionGizmo = new BBMOD_Quaternion().FromEuler(Rotation.X, Rotation.Y, Rotation.Z); + var _forwardGizmo = _quaternionGizmo.Rotate(BBMOD_VEC3_FORWARD); + var _rightGizmo = _quaternionGizmo.Rotate(BBMOD_VEC3_RIGHT); + var _upGizmo = _quaternionGizmo.Rotate(BBMOD_VEC3_UP); + + var _matRot = [ + _forwardGizmo.X, _forwardGizmo.Y, _forwardGizmo.Z, 0.0, + _rightGizmo.X, _rightGizmo.Y, _rightGizmo.Z, 0.0, + _upGizmo.X, _upGizmo.Y, _upGizmo.Z, 0.0, + 0.0, 0.0, 0.0, 1.0, + ]; + + var _matRotInverse = [ + _forwardGizmo.X, _rightGizmo.X, _upGizmo.X, 0.0, + _forwardGizmo.Y, _rightGizmo.Y, _upGizmo.Y, 0.0, + _forwardGizmo.Z, _rightGizmo.Z, _upGizmo.Z, 0.0, + 0.0, 0.0, 0.0, 1.0, + ]; + + //////////////////////////////////////////////////////////////////////// + // Handle editing + switch (EditType) + { + case BBMOD_EEditType.Position: + if (!__positionBackup) + { + __positionBackup = Position.Clone(); + } + + var _planeNormal; + + switch (EditAxis) + { + case BBMOD_EEditAxis.X: + var _dot1 = _rightGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + var _dot2 = _upGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + _planeNormal = (abs(_dot1) > abs(_dot2)) ? _rightGizmo : _upGizmo; + break; + + case BBMOD_EEditAxis.Y: + var _dot1 = _forwardGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + var _dot2 = _upGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + _planeNormal = (abs(_dot1) > abs(_dot2)) ? _forwardGizmo : _upGizmo; + break; + + case BBMOD_EEditAxis.Z: + var _dot1 = _forwardGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + var _dot2 = _rightGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + _planeNormal = (abs(_dot1) > abs(_dot2)) ? _forwardGizmo : _rightGizmo; + break; + + case BBMOD_EEditAxis.All: + _planeNormal = global.__bbmodCameraCurrent.get_forward(); + break; + } + + var _mouseWorld = intersect_ray_plane( + global.__bbmodCameraCurrent.Position, + global.__bbmodCameraCurrent.screen_point_to_vec3(new BBMOD_Vec2(_mouseX, _mouseY), global.__bbmodRendererCurrent), + __positionBackup, + _planeNormal); + + if (_mouseWorld) + { + var _snap = (EnableGridSnap && !keyboard_check(KeyIgnoreSnap)); + + if (EditAxis == BBMOD_EEditAxis.All) + { + if (!__mouseOffset) + { + __mouseOffset = _mouseWorld.Sub(Position); + } + + Position = _mouseWorld.Add(__mouseOffset); + } + else + { + if (!__mouseOffset) + { + __mouseOffset = _mouseWorld; + } + + var _diff = _mouseWorld.Sub(__mouseOffset); + + if (EditAxis & BBMOD_EEditAxis.X) + { + var _moveX = _forwardGizmo.Scale(_diff.Dot(_forwardGizmo)); + if (_snap + && EditSpace == BBMOD_EEditSpace.Local + && GridSize.X != 0.0) + { + var _moveXLength = _moveX.Length(); + if (_moveXLength > 0.0) + { + var _s = round(_moveXLength / GridSize.X) * GridSize.X; + _moveX = _moveX.Normalize().Scale(_s); + } + } + Position = __positionBackup.Add(_moveX); + } + + if (EditAxis & BBMOD_EEditAxis.Y) + { + var _moveY = _rightGizmo.Scale(_diff.Dot(_rightGizmo)); + if (_snap + && EditSpace == BBMOD_EEditSpace.Local + && GridSize.Y != 0.0) + { + var _moveYLength = _moveY.Length(); + if (_moveYLength > 0.0) + { + var _s = round(_moveYLength / GridSize.Y) * GridSize.Y; + _moveY = _moveY.Normalize().Scale(_s); + } + } + Position = __positionBackup.Add(_moveY); + } + + if (EditAxis & BBMOD_EEditAxis.Z) + { + var _moveZ = _upGizmo.Scale(_diff.Dot(_upGizmo)); + if (_snap + && EditSpace == BBMOD_EEditSpace.Local + && GridSize.Z != 0.0) + { + var _moveZLength = _moveZ.Length(); + if (_moveZLength > 0.0) + { + var _s = round(_moveZLength / GridSize.Z) * GridSize.Z; + _moveZ = _moveZ.Normalize().Scale(_s); + } + } + Position = __positionBackup.Add(_moveZ); + } + } + + if (_snap + && (EditSpace == BBMOD_EEditSpace.Global + || EditAxis == BBMOD_EEditAxis.All)) + { + if (GridSize.X != 0.0) + { + Position.X = round(Position.X / GridSize.X) * GridSize.X; + } + + if (GridSize.Y != 0.0) + { + Position.Y = round(Position.Y / GridSize.Y) * GridSize.Y; + } + + if (GridSize.Z != 0.0) + { + Position.Z = round(Position.Z / GridSize.Z) * GridSize.Z; + } + } + } + break; + + case BBMOD_EEditType.Rotation: + var _planeNormal = ((EditAxis == BBMOD_EEditAxis.X) ? _forwardGizmo + : ((EditAxis == BBMOD_EEditAxis.Y) ? _rightGizmo + : _upGizmo)); + + var _mouseWorld = intersect_ray_plane( + global.__bbmodCameraCurrent.Position, + global.__bbmodCameraCurrent.screen_point_to_vec3(new BBMOD_Vec2(_mouseX, _mouseY), global.__bbmodRendererCurrent), + Position, + _planeNormal); + + if (_mouseWorld) + { + if (!__mouseOffset) + { + __mouseOffset = _mouseWorld; + } + + var _v1 = __mouseOffset.Sub(Position); + var _v2 = _mouseWorld.Sub(Position); + var _angle = darctan2(_v2.Cross(_v1).Dot(_planeNormal), _v1.Dot(_v2)); + + switch (EditAxis) + { + case BBMOD_EEditAxis.X: + __rotateBy.X = _angle; + break; + + case BBMOD_EEditAxis.Y: + __rotateBy.Y = _angle; + break; + + case BBMOD_EEditAxis.Z: + __rotateBy.Z = _angle; + break; + } + } + break; + + case BBMOD_EEditType.Scale: + var _planeNormal; + + switch (EditAxis) + { + case BBMOD_EEditAxis.X: + var _dot1 = _rightGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + var _dot2 = _upGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + _planeNormal = (abs(_dot1) > abs(_dot2)) ? _rightGizmo : _upGizmo; + break; + + case BBMOD_EEditAxis.Y: + var _dot1 = _forwardGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + var _dot2 = _upGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + _planeNormal = (abs(_dot1) > abs(_dot2)) ? _forwardGizmo : _upGizmo; + break; + + case BBMOD_EEditAxis.Z: + var _dot1 = _forwardGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + var _dot2 = _rightGizmo.Dot(global.__bbmodCameraCurrent.get_forward()); + _planeNormal = (abs(_dot1) > abs(_dot2)) ? _forwardGizmo : _rightGizmo; + break; + + case BBMOD_EEditAxis.All: + _planeNormal = global.__bbmodCameraCurrent.get_forward(); + break; + } + + var _mouseWorld = intersect_ray_plane( + global.__bbmodCameraCurrent.Position, + global.__bbmodCameraCurrent.screen_point_to_vec3(new BBMOD_Vec2(_mouseX, _mouseY), global.__bbmodRendererCurrent), + Position, + _planeNormal); + + if (_mouseWorld && __mouseOffset) + { + var _mul = (keyboard_check(KeyEditFaster) ? 5.0 + : (keyboard_check(KeyEditSlower) ? 0.1 + : 1.0)); + + var _diff = _mouseWorld.Sub(__mouseOffset).Scale(_mul); + + if (EditAxis == BBMOD_EEditAxis.All) + { + var _diffX = _diff.Mul(_forwardGizmo.Abs()).Dot(_forwardGizmo); + var _diffY = _diff.Mul(_rightGizmo.Abs()).Dot(_rightGizmo); + var _scaleBy = (abs(_diffX) > abs(_diffY)) ? _diffX : _diffY; + __scaleBy.X += _scaleBy; + __scaleBy.Y += _scaleBy; + __scaleBy.Z += _scaleBy; + } + else + { + if (EditAxis & BBMOD_EEditAxis.X) + { + __scaleBy.X += _diff.Mul(_forwardGizmo.Abs()).Dot(_forwardGizmo); + } + + if (EditAxis & BBMOD_EEditAxis.Y) + { + __scaleBy.Y += _diff.Mul(_rightGizmo.Abs()).Dot(_rightGizmo); + } + + if (EditAxis & BBMOD_EEditAxis.Z) + { + __scaleBy.Z += _diff.Mul(_upGizmo.Abs()).Dot(_upGizmo); + } + } + } + + __mouseOffset = _mouseWorld; + break; + } + + //////////////////////////////////////////////////////////////////////// + // Cancel editing? + if (keyboard_check_pressed(KeyCancel)) + { + if (__positionBackup) + { + __positionBackup.Copy(Position); + } + __rotateBy.Set(0.0, 0.0, 0.0); + __scaleBy.Set(0.0, 0.0, 0.0); + IsEditing = false; + } + + //////////////////////////////////////////////////////////////////////// + // Apply to selected instances + var _size = ds_list_size(Selected); + + for (var i = _size - 1; i >= 0; --i) + { + var _instance = Selected[| i]; + + if (!InstanceExists(_instance)) + { + ds_list_delete(Selected, i); + ds_list_delete(__instanceData, i); + --_size; + continue; + } + + var _data = __instanceData[| i]; + var _positionOffset = _data.Offset; + var _rotationStored = _data.Rotation; + var _scaleStored = _data.Scale; + + // Get local basis + var _quaternionInstance = new BBMOD_Quaternion().FromEuler( + GetInstanceRotationX(_instance), + GetInstanceRotationY(_instance), + GetInstanceRotationZ(_instance)); + var _forwardInstance = _quaternionInstance.Rotate(BBMOD_VEC3_FORWARD); + var _rightInstance = _quaternionInstance.Rotate(BBMOD_VEC3_RIGHT); + var _upInstance = _quaternionInstance.Rotate(BBMOD_VEC3_UP); + + // Apply rotation + var _matGlobal = GetInstanceGlobalMatrix(_instance); + var _matGlobalInv = _matGlobal.Inverse(); + var _rotateByX = __rotateBy.X; + var _rotateByY = __rotateBy.Y; + var _rotateByZ = __rotateBy.Z; + + if (EnableAngleSnap + && AngleSnap != 0.0 + && !keyboard_check(KeyIgnoreSnap)) + { + _rotateByX = floor(__rotateBy.X / AngleSnap) * AngleSnap; + _rotateByY = floor(__rotateBy.Y / AngleSnap) * AngleSnap; + _rotateByZ = floor(__rotateBy.Z / AngleSnap) * AngleSnap; + } + + var _temp = new BBMOD_Vec4(_forwardGizmo.X, _forwardGizmo.Y, _forwardGizmo.Z, 0.0).Transform(_matGlobalInv.Raw); + var _forwardGlobal = new BBMOD_Vec3(_temp.X, _temp.Y, _temp.Z); + var _temp = new BBMOD_Vec4(_rightGizmo.X, _rightGizmo.Y, _rightGizmo.Z, 0.0).Transform(_matGlobalInv.Raw); + var _rightGlobal = new BBMOD_Vec3(_temp.X, _temp.Y, _temp.Z); + var _temp = new BBMOD_Vec4(_upGizmo.X, _upGizmo.Y, _upGizmo.Z, 0.0).Transform(_matGlobalInv.Raw); + var _upGlobal = new BBMOD_Vec3(_temp.X, _temp.Y, _temp.Z); + + var _rotMatrix = new BBMOD_Matrix().RotateEuler(_rotationStored); + if (_rotateByX != 0.0) + { + var _quaternionX = new BBMOD_Quaternion().FromAxisAngle(_forwardGlobal, _rotateByX); + _positionOffset = _quaternionX.Rotate(_positionOffset); + _rotMatrix = _rotMatrix.RotateQuat(_quaternionX); + } + if (_rotateByY != 0.0) + { + var _quaternionY = new BBMOD_Quaternion().FromAxisAngle(_rightGlobal, _rotateByY); + _positionOffset = _quaternionY.Rotate(_positionOffset); + _rotMatrix = _rotMatrix.RotateQuat(_quaternionY); + } + if (_rotateByZ != 0.0) + { + var _quaternionZ = new BBMOD_Quaternion().FromAxisAngle(_upGlobal, _rotateByZ); + _positionOffset = _quaternionZ.Rotate(_positionOffset); + _rotMatrix = _rotMatrix.RotateQuat(_quaternionZ); + } + var _rotArray = _rotMatrix.ToEuler(); + SetInstanceRotationX(_instance, _rotArray[0]); + SetInstanceRotationY(_instance, _rotArray[1]); + SetInstanceRotationZ(_instance, _rotArray[2]); + + // Apply scale + var _scaleNew = _scaleStored.Clone(); + var _scaleOld = _scaleNew.Clone(); + + // Scale on X + _scaleNew.X += __scaleBy.X * abs(_forwardGlobal.Dot(_forwardInstance)); + _scaleNew.Y += __scaleBy.X * abs(_forwardGlobal.Dot(_rightInstance)); + _scaleNew.Z += __scaleBy.X * abs(_forwardGlobal.Dot(_upInstance)); + + // Scale on Y + _scaleNew.X += __scaleBy.Y * abs(_rightGlobal.Dot(_forwardInstance)); + _scaleNew.Y += __scaleBy.Y * abs(_rightGlobal.Dot(_rightInstance)); + _scaleNew.Z += __scaleBy.Y * abs(_rightGlobal.Dot(_upInstance)); + + // Scale on Z + _scaleNew.X += __scaleBy.Z * abs(_upGlobal.Dot(_forwardInstance)); + _scaleNew.Y += __scaleBy.Z * abs(_upGlobal.Dot(_rightInstance)); + _scaleNew.Z += __scaleBy.Z * abs(_upGlobal.Dot(_upInstance)); + + // Scale offset + var _vI = matrix_transform_vertex(_matRotInverse, _positionOffset.X, _positionOffset.Y, _positionOffset.Z); + var _vIRot = matrix_transform_vertex( + matrix_build( + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + (1.0 / max(_scaleOld.X, 0.0001)) * (_scaleOld.X + __scaleBy.X), + (1.0 / max(_scaleOld.Y, 0.0001)) * (_scaleOld.Y + __scaleBy.Y), + (1.0 / max(_scaleOld.Z, 0.0001)) * (_scaleOld.Z + __scaleBy.Z)), + _vI[0], _vI[1], _vI[2]); + var _v = matrix_transform_vertex(_matRot, _vIRot[0], _vIRot[1], _vIRot[2]); + + // Apply scale and position + set_instance_scale_vec3(_instance, _scaleNew); + SetInstancePositionX(_instance, Position.X + _v[0]); + SetInstancePositionY(_instance, Position.Y + _v[1]); + SetInstancePositionZ(_instance, Position.Z + _v[2]); + } + + return self; + }; + + /// @func submit([_materials]) + /// + /// @desc Immediately submits the gizmo for rendering. + /// + /// @param {Array} [_materials] Materials to use or + /// `undefined`. + /// + /// @return {Struct.BBMOD_Gizmo} Returns `self`. + /// + /// @note This changes the world matrix based on the gizmo's position and size! + static submit = function (_materials=undefined) { + gml_pragma("forceinline"); + (new BBMOD_Matrix()) + .Scale(new BBMOD_Vec3(Size)) + .RotateEuler(Rotation) + .Translate(Position) + .ApplyWorld(); + Models[EditType].submit(_materials); + return self; + }; + + /// @func render([_materials]) + /// + /// @desc Enqueues the gizmo for rendering. + /// + /// @param {Array} [_materials] Materials to use or + /// `undefined`. + /// + /// @return {Struct.BBMOD_Gizmo} Returns `self`. + /// + /// @note This changes the world matrix based on the gizmo's position and size! + static render = function (_materials=undefined) { + gml_pragma("forceinline"); + new BBMOD_Matrix() + .Scale(new BBMOD_Vec3(Size)) + .RotateEuler(Rotation) + .Translate(Position) + .ApplyWorld(); + Models[EditType].render(_materials); + return self; + }; + + static destroy = function () { + Class_destroy(); + ds_list_destroy(Selected); + ds_list_destroy(__instanceData); + return undefined; + }; +} diff --git a/scripts/BBMOD_Gizmo/BBMOD_Gizmo.yy b/scripts/BBMOD_Gizmo/BBMOD_Gizmo.yy new file mode 100644 index 000000000..d4119fd0c --- /dev/null +++ b/scripts/BBMOD_Gizmo/BBMOD_Gizmo.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Gizmo", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Gizmo", + "path": "folders/_Extensions/BBMOD/Gizmo.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_GravityModule/BBMOD_GravityModule.gml b/scripts/BBMOD_GravityModule/BBMOD_GravityModule.gml new file mode 100644 index 000000000..f99da08b4 --- /dev/null +++ b/scripts/BBMOD_GravityModule/BBMOD_GravityModule.gml @@ -0,0 +1,44 @@ +/// @func BBMOD_GravityModule([_gravity]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that applies gravity force to particles. +/// +/// @param {Struct.BBMOD_Vec3} [_gravity] The gravity vector. Defaults to +/// `(0, 0, -9.8)`. +function BBMOD_GravityModule(_gravity=BBMOD_VEC3_UP.Scale(-9.8)) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_Vec3} The gravity vector. Default value is + /// `(0, 0, -9.8)`. + Gravity = _gravity; + + static on_update = function (_emitter, _deltaTime) { + var _y2 = _emitter.ParticlesAlive - 1; + if (_y2 >= 0) + { + var _particles = _emitter.Particles; + var _gravity = Gravity; + + ds_grid_add_region( + _particles, + BBMOD_EParticle.AccelerationX, 0, + BBMOD_EParticle.AccelerationX, _y2, + _gravity.X); + + ds_grid_add_region( + _particles, + BBMOD_EParticle.AccelerationY, 0, + BBMOD_EParticle.AccelerationY, _y2, + _gravity.Y); + + ds_grid_add_region( + _particles, + BBMOD_EParticle.AccelerationZ, 0, + BBMOD_EParticle.AccelerationZ, _y2, + _gravity.Z); + } + }; +} diff --git a/scripts/BBMOD_GravityModule/BBMOD_GravityModule.yy b/scripts/BBMOD_GravityModule/BBMOD_GravityModule.yy new file mode 100644 index 000000000..f3037c61a --- /dev/null +++ b/scripts/BBMOD_GravityModule/BBMOD_GravityModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_GravityModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Physics", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Physics.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_IEventListener/BBMOD_IEventListener.gml b/scripts/BBMOD_IEventListener/BBMOD_IEventListener.gml new file mode 100644 index 000000000..9ca5fc155 --- /dev/null +++ b/scripts/BBMOD_IEventListener/BBMOD_IEventListener.gml @@ -0,0 +1,153 @@ +/// @func BBMOD_IEventListener() +/// +/// @interface +/// +/// @example +/// ```gml +/// function MyEventListener() : BBMOD_Class() constructor +/// { +/// implement(BBMOD_IEventListener); +/// +/// on_event("test", function () { +/// show_debug_message("It is working!"); +/// }); +/// } +/// +/// new MyEventListener().trigger_event("test"); // Prints "It is working!" +/// ``` +function BBMOD_IEventListener() +{ + /// @var {Id.DsMap} + /// @private + __listeners = undefined; + + /// @func on_event([_event, ]_listener) + /// + /// @desc Adds a listener for a specific event. + /// + /// @param {String} [_event] The event name. If `undefined`, then the + /// listener is executed on every event. + /// @param {Function} _listener A function executed when the event occurs. + /// Should take the event data as the first argument and the event name + /// as the second argument. + /// + /// @return {Struct.BBMOD_IEventListener} Returns `self`. + /// + /// @example + /// ```gml + /// function Button() : BBMOD_Class() constructor + /// { + /// implement(BBMOD_IEventListener); + /// } + /// + /// var _button = new Button(); + /// + /// // This will be always executed, no matter the event type. + /// _button.on_event(function (_data, _eventName) { + /// show_debug_message("Got event " + string(_eventName) + "!"); + /// }); + /// + /// // This will be executed only on event "click". + /// _button.on_event("click", function () { + /// show_debug_message("The button was clicked!"); + /// }); + /// ``` + /// + /// @see BBMOD_IEventListener.off_event + static _onEvent = function (_event, _listener=undefined) { + gml_pragma("forceinline"); + if (is_method(_event)) + { + _listener = _event; + _event = __BBMOD_EV_ALL; + } + __listeners ??= ds_map_create(); + if (!ds_map_exists(__listeners, _event)) + { + __listeners[? _event] = []; + } + array_push(__listeners[? _event], _listener); + return self; + }; + + on_event = _onEvent; + + /// @func off_event([_event]) + /// + /// @desc Removes event listeners. + /// + /// @param {String} [_event] The name of the event for which should be the + /// listener removed. If `undefined`, then listeners for all events are + /// removed. + /// + /// @return {Struct.BBMOD_IEventListener} Returns `self`. + /// + /// @see BBMOD_IEventListener.on_event + static _offEvent = function (_event=undefined) { + gml_pragma("forceinline"); + if (__listeners == undefined) + { + return self; + } + if (_event != undefined) + { + ds_map_delete(__listeners, _event); + } + else + { + ds_map_destroy(__listeners); + } + return self; + }; + + off_event = _offEvent; + + /// @func trigger_event(_event, _data) + /// + /// @desc Triggers an event in the event listener. + /// + /// @param {String} _event The event name. + /// @param {Any} _data The event data. + /// + /// @return {Struct.BBMOD_IEventListener} Returns `self`. + static _triggerEvent = function (_event, _data) { + gml_pragma("forceinline"); + if (__listeners == undefined) + { + return self; + } + + var _events, i; + + if (ds_map_exists(__listeners, _event)) + { + _events = __listeners[? _event]; + i = 0; + repeat (array_length(_events)) + { + _events[i++](_data, _event); + } + } + + if (ds_map_exists(__listeners, __BBMOD_EV_ALL)) + { + _events = __listeners[? __BBMOD_EV_ALL]; + i = 0; + repeat (array_length(_events)) + { + _events[i++](_data, _event); + } + } + + return self; + }; + + trigger_event = _triggerEvent; + + array_push(__destroyActions, function () { + if (__listeners != undefined) + { + ds_map_destroy(__listeners); + } + }); +} diff --git a/scripts/BBMOD_IEventListener/BBMOD_IEventListener.yy b/scripts/BBMOD_IEventListener/BBMOD_IEventListener.yy new file mode 100644 index 000000000..20aa35e4b --- /dev/null +++ b/scripts/BBMOD_IEventListener/BBMOD_IEventListener.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_IEventListener", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Interfaces", + "path": "folders/_Extensions/BBMOD/Core/Interfaces.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_IRenderTarget/BBMOD_IRenderTarget.gml b/scripts/BBMOD_IRenderTarget/BBMOD_IRenderTarget.gml new file mode 100644 index 000000000..2f881f5c8 --- /dev/null +++ b/scripts/BBMOD_IRenderTarget/BBMOD_IRenderTarget.gml @@ -0,0 +1,35 @@ +/// @func BBMOD_IRenderTarget() +/// +/// @interface +/// +/// @desc An interface for structs which can be used as a render target. +function BBMOD_IRenderTarget() +{ + /// @func set_target() + /// + /// @desc Sets the render target. + /// + /// @return {Bool} Returns `true` if the render target was set. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + /// + /// @see BBMOD_IRenderTarget.reset_target + static set_target = function () { + throw new BBMOD_NotImplementedException(); + //return self; + }; + + /// @func reset_target() + /// + /// @desc Resets the render target. + /// + /// @return {Struct.BBMOD_IRenderTarget} Returns `self`. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + /// + /// @see BBMOD_IRenderTarget.set_target + static reset_target = function () { + throw new BBMOD_NotImplementedException(); + //return self; + }; +} diff --git a/scripts/BBMOD_IRenderTarget/BBMOD_IRenderTarget.yy b/scripts/BBMOD_IRenderTarget/BBMOD_IRenderTarget.yy new file mode 100644 index 000000000..b3b24db0d --- /dev/null +++ b/scripts/BBMOD_IRenderTarget/BBMOD_IRenderTarget.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_IRenderTarget", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Interfaces", + "path": "folders/_Extensions/BBMOD/Core/Interfaces.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_IRenderable/BBMOD_IRenderable.gml b/scripts/BBMOD_IRenderable/BBMOD_IRenderable.gml new file mode 100644 index 000000000..f39c681d8 --- /dev/null +++ b/scripts/BBMOD_IRenderable/BBMOD_IRenderable.gml @@ -0,0 +1,50 @@ +/// @func BBMOD_IRenderable() +/// +/// @interface +/// +/// @desc An interface describing renderable objects. Any struct or object that +/// implements this interface can be rendered using a {@link BBMOD_Renderer}. +/// +/// @example +/// A renderable object: +/// ```gml +/// /// @desc Create event +/// render = function () { +/// var _matrix = matrix_build_identity(); +/// _matrix[@ 12] = x; +/// _matrix[@ 13] = y; +/// _matrix[@ 14] = z; +/// matrix_set(matrix_world, _matrix); +/// model.render(); +/// return self; +/// }; +/// ``` +/// A renderable struct: +/// ```gml +/// renderable = { +/// position: new BBMOD_Vec3(), +/// model: /* ... */, +/// render: function () { +/// var _matrix = matrix_build_identity(); +/// position.ToArray(_matrix, 12); +/// matrix_set(matrix_world, _matrix); +/// model.render(); +/// return self; +/// }, +/// }; +/// ``` +/// @see BBMOD_Renderer +function BBMOD_IRenderable() +{ + /// @func render() + /// + /// @desc Enqueues the object for rendering. + /// + /// @return {Struct.BBMOD_IRenderable} Returns `self`. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static render = function () { + throw new BBMOD_NotImplementedException(); + //return self; + }; +} diff --git a/scripts/BBMOD_IRenderable/BBMOD_IRenderable.yy b/scripts/BBMOD_IRenderable/BBMOD_IRenderable.yy new file mode 100644 index 000000000..637793d04 --- /dev/null +++ b/scripts/BBMOD_IRenderable/BBMOD_IRenderable.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_IRenderable", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Interfaces", + "path": "folders/_Extensions/BBMOD/Core/Interfaces.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_ImageBasedLight/BBMOD_ImageBasedLight.gml b/scripts/BBMOD_ImageBasedLight/BBMOD_ImageBasedLight.gml new file mode 100644 index 000000000..f7c6669ba --- /dev/null +++ b/scripts/BBMOD_ImageBasedLight/BBMOD_ImageBasedLight.gml @@ -0,0 +1,55 @@ +/// @var {Struct.BBMOD_ImageBasedLight} +/// @private +global.__bbmodImageBasedLight = undefined; + +/// @func BBMOD_ImageBasedLight(_texture) +/// +/// @extends BBMOD_Light +/// +/// @desc An image based light. +/// +/// @param {Pointer.Texture} _texture A texture containing 8 prefiltered +/// RGBM-encoded octahedrons, where the first 7 are for specular reflections +/// with increasing roughness and the last one is for diffuse lighting. +function BBMOD_ImageBasedLight(_texture) + : BBMOD_Light() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Pointer.Texture} The texture of the IBL. + /// @readonly + Texture = _texture; + + /// @var {Real} The texel height of the texture. + /// @readonly + Texel = texture_get_texel_height(Texture); +} + +/// @func bbmod_ibl_get() +/// +/// @desc Retrieves the image based light passed to shaders. +/// +/// @return {Struct.BBMOD_ImageBasedLight} The image based light or `undefined`. +/// +/// @see bbmod_ibl_set +/// @see BBMOD_ImageBasedLight +function bbmod_ibl_get() +{ + gml_pragma("forceinline"); + return global.__bbmodImageBasedLight; +} + +/// @func bbmod_ibl_set(_ibl) +/// +/// @desc Defines the image based light passed to shaders. +/// +/// @param {Struct.BBMOD_ImageBasedLight} _ibl The new image based light or +/// `undefined`. +/// +/// @see bbmod_ibl_get +/// @see BBMOD_ImageBasedLight +function bbmod_ibl_set(_ibl) +{ + gml_pragma("forceinline"); + global.__bbmodImageBasedLight = _ibl; +} diff --git a/scripts/BBMOD_ImageBasedLight/BBMOD_ImageBasedLight.yy b/scripts/BBMOD_ImageBasedLight/BBMOD_ImageBasedLight.yy new file mode 100644 index 000000000..87091e79a --- /dev/null +++ b/scripts/BBMOD_ImageBasedLight/BBMOD_ImageBasedLight.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_ImageBasedLight", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Lights", + "path": "folders/_Extensions/BBMOD/Core/Lights.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Importer/BBMOD_Importer.gml b/scripts/BBMOD_Importer/BBMOD_Importer.gml new file mode 100644 index 000000000..cdac8e434 --- /dev/null +++ b/scripts/BBMOD_Importer/BBMOD_Importer.gml @@ -0,0 +1,45 @@ +/// @func BBMOD_Importer() +/// +/// @extends BBMOD_Class +/// +/// @desc Base class for model importers. +function BBMOD_Importer() + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Bool} If true then UV texture coordinates of imported models + /// will be flipped horizontally. Defaults to false. + FlipUVHorizontally = false; + + /// @var {Bool} If true then UV texture coordinates of imported models + /// will be flipped vertically. Defaults to false. + FlipUVVertically = false; + + /// @func can_import(_path) + /// + /// @desc Checks whether a file can be imported. + /// + /// @param {String} _path The path to the file to import. + /// + /// @return {Bool} Returns `true` if the importer can import the file. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static can_import = function (_path) { + throw new BBMOD_NotImplementedException(); + }; + + /// @func import(_path) + /// + /// @desc Imports a model from a file. + /// + /// @param {String} _path The path to the file to import. + /// + /// @return {Struct.BBMOD_Model} The imported model. + /// + /// @throws {BBMOD_Exception} If the file could not be imported. + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static import = function (_path) { + throw new BBMOD_NotImplementedException(); + }; +} diff --git a/scripts/BBMOD_Importer/BBMOD_Importer.yy b/scripts/BBMOD_Importer/BBMOD_Importer.yy new file mode 100644 index 000000000..56e46e8f2 --- /dev/null +++ b/scripts/BBMOD_Importer/BBMOD_Importer.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Importer", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Core", + "path": "folders/_Extensions/BBMOD/Core.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Light/BBMOD_Light.gml b/scripts/BBMOD_Light/BBMOD_Light.gml new file mode 100644 index 000000000..af67c90f4 --- /dev/null +++ b/scripts/BBMOD_Light/BBMOD_Light.gml @@ -0,0 +1,50 @@ +/// @func BBMOD_Light() +/// +/// @extends BBMOD_Class +/// +/// @desc Base class for lights. +/// +/// @see BBMOD_DirectionalLight +/// @see BBMOD_ImageBasedLight +/// @see BBMOD_PointLight +/// @see BBMOD_SpotLight +function BBMOD_Light() + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Bool} Use `false` to disable the light. Defaults to `true` (the + /// light is enabled). + Enabled = true; + + /// @var {Struct.BBMOD_Vec3} The position of the light. + Position = new BBMOD_Vec3(); + + /// @var {Bool} If `true` then the light affects also materials with baked + /// lightmaps. Defaults to `true`. + AffectLightmaps = true; + + /// @var {Bool} If `true` then the light should casts shadows. This may + /// not be implemented for all types of lights! Defaults to `false`. + CastShadows = false; + + /// @var {Real} The resolution of the shadowmap surface. Must be power of 2. + /// Defaults to 512. + ShadowmapResolution = 512; + + /// @var {Function} + /// @private + __getZFar = undefined; + + /// @var {Function} + /// @private + __getViewMatrix = undefined; + + /// @var {Function} + /// @private + __getProjMatrix = undefined; + + /// @var {Function} + /// @private + __getShadowmapMatrix = undefined; +} diff --git a/scripts/BBMOD_Light/BBMOD_Light.yy b/scripts/BBMOD_Light/BBMOD_Light.yy new file mode 100644 index 000000000..4faafddf7 --- /dev/null +++ b/scripts/BBMOD_Light/BBMOD_Light.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Light", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Lights", + "path": "folders/_Extensions/BBMOD/Core/Lights.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_LightmapMaterial/BBMOD_LightmapMaterial.yy b/scripts/BBMOD_LightmapMaterial/BBMOD_LightmapMaterial.yy new file mode 100644 index 000000000..8f0c7fea6 --- /dev/null +++ b/scripts/BBMOD_LightmapMaterial/BBMOD_LightmapMaterial.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_LightmapMaterial", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Deprecated", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Deprecated.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_LightmapMaterial/bbmod_lightmapmaterial.gml b/scripts/BBMOD_LightmapMaterial/bbmod_lightmapmaterial.gml new file mode 100644 index 000000000..6f0019110 --- /dev/null +++ b/scripts/BBMOD_LightmapMaterial/bbmod_lightmapmaterial.gml @@ -0,0 +1,10 @@ +/// @func BBMOD_LightmapMaterial([_shader]) +/// +/// @extends BBMOD_DefaultLightmapMaterial +/// +/// @deprecated Please use {@link BBMOD_DefaultLightmapMaterial} instead. +function BBMOD_LightmapMaterial(_shader=undefined) + : BBMOD_DefaultLightmapMaterial(_shader) constructor +{ + BBMOD_CLASS_GENERATED_BODY; +} diff --git a/scripts/BBMOD_LightmapShader/BBMOD_LightmapShader.yy b/scripts/BBMOD_LightmapShader/BBMOD_LightmapShader.yy new file mode 100644 index 000000000..14a024093 --- /dev/null +++ b/scripts/BBMOD_LightmapShader/BBMOD_LightmapShader.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_LightmapShader", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Deprecated", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Deprecated.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_LightmapShader/bbmod_lightmapshader.gml b/scripts/BBMOD_LightmapShader/bbmod_lightmapshader.gml new file mode 100644 index 000000000..a805add8b --- /dev/null +++ b/scripts/BBMOD_LightmapShader/bbmod_lightmapshader.gml @@ -0,0 +1,10 @@ +/// @func BBMOD_LightmapShader(_shader, _vertexFormat) +/// +/// @extends BBMOD_DefaultLightmapShader +/// +/// @deprecated Please use {@link BBMOD_DefaultLightmapShader} instead. +function BBMOD_LightmapShader(_shader, _vertexFormat) + : BBMOD_DefaultLightmapShader(_shader, _vertexFormat) constructor +{ + BBMOD_CLASS_GENERATED_BODY; +} diff --git a/scripts/BBMOD_Material/BBMOD_Material.gml b/scripts/BBMOD_Material/BBMOD_Material.gml new file mode 100644 index 000000000..e769f0953 --- /dev/null +++ b/scripts/BBMOD_Material/BBMOD_Material.gml @@ -0,0 +1,770 @@ +/// @func __bbmod_material_get_map() +/// +/// @desc Retrieves a map of registered materials. +/// +/// @return {Id.DsMap} The map of registered +/// materials. +/// +/// @private +function __bbmod_material_get_map() +{ + static _map = ds_map_create(); + return _map; +} + +/// @func bbmod_material_register(_name, _material) +/// +/// @desc Registers a material. +/// +/// @param {String} _name The name of the material. +/// @param {Struct.BBMOD_Material} _material The material. +function bbmod_material_register(_name, _material) +{ + gml_pragma("forceinline"); + static _map =__bbmod_material_get_map(); + _map[? _name] = _material; + _material.__name = _name; +} + +/// @func bbmod_material_exists(_name) +/// +/// @desc Checks if there is a material registered under the name. +/// +/// @param {String} _name The name of the material. +/// +/// @return {Bool} Returns `true` if there is a material registered under the +/// name. +function bbmod_material_exists(_name) +{ + gml_pragma("forceinline"); + static _map =__bbmod_material_get_map(); + return ds_map_exists(_map, _name); +} + +/// @func bbmod_material_get(_name) +/// +/// @desc Retrieves a material registered under the name. +/// +/// @param {String} _name The name of the material. +/// +/// @return {Struct.BBMOD_Material} The material or `undefined` if no +/// material registered under the given name exists. +function bbmod_material_get(_name) +{ + gml_pragma("forceinline"); + static _map =__bbmod_material_get_map(); + return _map[? _name]; +} + +/// @var {Struct.BBMOD_Material} The currently applied material or `undefined`. +/// @private +global.__bbmodMaterialCurrent = undefined; + +/// @func BBMOD_Material([_shader]) +/// +/// @extends BBMOD_Resource +/// +/// @desc Base class for materials. +/// +/// @param {Struct.BBMOD_Shader} [_shader] A shader that the material uses in +/// the {@link BBMOD_ERenderPass.Forward} pass. Leave `undefined` if you would +/// like to use {@link BBMOD_Material.set_shader} to specify shaders used in +/// specific render passes. +/// +/// @see BBMOD_Shader +function BBMOD_Material(_shader=undefined) + : BBMOD_Resource() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Resource_destroy = destroy; + + /// @var {String} The name under which is this material registered or + /// `undefined`. + /// @private + __name = undefined; + + /// @var {Real} Render passes in which is the material rendered. Defaults + /// to 0 (no passes). + /// @readonly + /// @see BBMOD_ERenderPass + RenderPass = 0; + + /// @var {Array} Shaders used in specific render passes. + /// @private + /// @see BBMOD_Material.set_shader + /// @see BBMOD_Material.get_shader + Shaders = array_create(BBMOD_ERenderPass.SIZE, undefined); + + /// @var {Struct.BBMOD_RenderQueue} The render queue used by this material. + /// Defaults to the default BBMOD render queue. + /// @readonly + /// @see BBMOD_RenderQueue + /// @see bbmod_render_queue_get_default + RenderQueue = bbmod_render_queue_get_default(); + + /// @var {Function} A function that is executed when the shader is applied. + /// Must take the material as the first argument. Use `undefined` if you do + /// not want to execute any function. Defaults to `undefined`. + OnApply = undefined; + + /// @var {Constant.BlendMode} A blend mode. Default value is `bm_normal`. + BlendMode = bm_normal; + + /// @var {Constant.CullMode} A culling mode. Default value is + /// `cull_counterclockwise`. + Culling = cull_counterclockwise; + + /// @var {Bool} If `true` then models using this material should write to + /// the depth buffer. Default value is `true`. + ZWrite = true; + + /// @var {Bool} If `true` then models using this material should be tested + /// against the depth buffer. Defaults value is `true`. + ZTest = true; + + /// @var {Constant.CmpFunc} The function used for depth testing when + /// {@link BBMOD_Material.ZTest} is enabled. Default value is + /// `cmpfunc_lessequal`. + ZFunc = cmpfunc_lessequal; + + /// @var {Real} Discard pixels with alpha less than this value. Use values + /// in range 0..1. + AlphaTest = 1.0; + + /// @var {Bool} Use `true` to enable alpha blending. This can have negative + /// effect on performance, therefore it should be used only when necessary. + /// Default value is `false`. + AlphaBlend = false; + + /// @var {Real} Use one of the `mip_` constants. Default value is `mip_on`. + Mipmapping = mip_on; + + /// @var {Real} Defines a bias for which mip level is used. Can be also + /// negative values to select lower mip levels. E.g. if mip level 2 would be + /// normally selected and bias was -1, then level 1 would be selected instead + /// and if it was 1, then level 3 would be selected instead. Default value is + /// 0. + MipBias = 0; + + /// @var {Real} The mip filter mode used for the material. Use one of the + /// `tf_` constants. Default value is `tf_anisotropic`. + MipFilter = tf_anisotropic; + + /// @var {Real} The minimum mip level used, where 0 is the highest resolution, + /// 1 is the first mipmap, 2 is the second etc. Default value is 0. + MipMin = 0; + + /// @var {Real} The maximum mip level used, where 0 is the highest resolution, + /// 1 is the first mipmap, 2 is the second etc. Default value is 16. + MipMax = 16; + + /// @var {Real} The maximum level of anisotropy when + /// {@link BBMOD_Material.MipFilter} is set to `tf_anisotropic`. Must be in + /// range 1..16. Default value is 16. + Anisotropy = 16; + + /// @var {Bool} Use `false` to disable linear texture filtering for this + /// material. Default value is `true`. + Filtering = true; + + /// @var {Bool} Use `true` to enable texture repeat for this material. + /// Default value is `false`. + Repeat = false; + + /// @var {Pointer.Texture} A texture with a base color in the RGB channels + /// and opacity in the alpha channel. + BaseOpacity = pointer_null; + + __baseOpacitySprite = undefined; + + /// @func copy(_dest) + /// + /// @desc Copies properties of this material into another material. + /// + /// @param {Struct.BBMOD_Material} _dest The destination material. + /// + /// @return {Struct.BBMOD_Material} Returns `self`. + static copy = function (_dest) { + _dest.__name = __name; + _dest.RenderPass = RenderPass; + _dest.Shaders = array_create(BBMOD_ERenderPass.SIZE, undefined); + array_copy(_dest.Shaders, 0, Shaders, 0, BBMOD_ERenderPass.SIZE); + _dest.RenderQueue = RenderQueue; + _dest.OnApply = OnApply; + _dest.BlendMode = BlendMode; + _dest.Culling = Culling; + _dest.ZWrite = ZWrite; + _dest.ZTest = ZTest; + _dest.ZFunc = ZFunc; + _dest.AlphaTest = AlphaTest; + _dest.AlphaBlend = AlphaBlend; + _dest.Mipmapping = Mipmapping; + _dest.MipBias = MipBias; + _dest.MipFilter = MipFilter; + _dest.MipMin = MipMin; + _dest.MipMax = MipMax; + _dest.Anisotropy = Anisotropy; + _dest.Filtering = Filtering; + _dest.Repeat = Repeat; + + if (_dest.__baseOpacitySprite != undefined) + { + sprite_delete(_dest.__baseOpacitySprite); + _dest.__baseOpacitySprite = undefined; + } + + if (__baseOpacitySprite != undefined) + { + _dest.__baseOpacitySprite = sprite_duplicate(__baseOpacitySprite); + _dest.BaseOpacity = sprite_get_texture(_dest.__baseOpacitySprite, 0); + } + else + { + _dest.BaseOpacity = BaseOpacity; + } + + return self; + }; + + /// @func clone() + /// + /// @desc Creates a clone of the material. + /// + /// @return {Struct.BBMOD_Material} The created clone. + static clone = function () { + var _clone = new BBMOD_Material(); + copy(_clone); + return _clone; + }; + + /// @func to_json(_json) + /// + /// @desc Saves material properties to a JSON object. + /// + /// @param {Struct} _json The object to save the properties to. + /// + /// @return {Struct.BBMOD_Material} Returns `self`. + /// + /// @throws {BBMOD_Exception} If an error occurs. + static to_json = function (_json) { + var _shaders = {}; + var _pass = 0; + repeat (BBMOD_ERenderPass.SIZE) + { + var _shader = Shaders[_pass]; + if (_shader != undefined) + { + var _passName = bbmod_render_pass_to_string(_pass); + if (_shader.__name == undefined) + { + throw new BBMOD_Exception( + "Cannot save to JSON, shader for render pass \"" + + _passName + "\" is not registered!"); + } + else + { + _shaders[$ _passName] = _shader.__name; + } + } + ++_pass; + } + _json.Shaders = _shaders; + + if (RenderQueue.Name != undefined) + { + _json.RenderQueue = RenderQueue.Name; + } + + // TODO: Save OnApply + + _json.BlendMode = BlendMode; + _json.Culling = Culling; + _json.ZWrite = ZWrite; + _json.ZTest = ZTest; + _json.ZFunc = ZFunc; + _json.AlphaTest = AlphaTest; + _json.AlphaBlend = AlphaBlend; + _json.Mipmapping = Mipmapping; + _json.MipBias = MipBias; + _json.MipFilter = MipFilter; + _json.MipMin = MipMin; + _json.MipMax = MipMax; + _json.Anisotropy = Anisotropy; + _json.Filtering = Filtering; + _json.Repeat = Repeat; + + // TODO: Save BaseOpacity/__baseOpacitySprite + + return self; + }; + + /// @func from_json(_json) + /// + /// @desc Loads material properties from a JSON object. + /// + /// @param {Struct} _json The object to load the properties from. + /// + /// @return {Struct.BBMOD_Material} Returns `self`. + /// + /// @throws {BBMOD_Exception} If an error occurs. + static from_json = function (_json) { + if (variable_struct_exists(_json, "Shaders")) + { + var _shaders = _json.Shaders; + var _keys = variable_struct_get_names(_shaders); + var _index = 0; + repeat (array_length(_keys)) + { + var _passName = _keys[_index++]; + var _pass = bbmod_render_pass_from_string(_passName); + var _shader = _shaders[$ _passName]; + if (is_string(_shader)) + { + _shader = bbmod_shader_get(_shader); + } + set_shader(_pass, _shader); + } + } + + if (variable_struct_exists(_json, "RenderQueue")) + { + var _renderQueue = _json.RenderQueue; + if (is_string(_renderQueue)) + { + var _renderQueues = bbmod_render_queues_get(); + var _index = 0; + repeat (array_length(_renderQueues)) + { + with (_renderQueues[_index++]) + { + if (Name == _renderQueue) + { + _renderQueue = self; + break; + } + } + } + if (is_string(_renderQueue)) + { + throw new BBMOD_Exception("Invalid render queue \"" + _renderQueue + "\"!"); + } + } + } + + if (variable_struct_exists(_json, "OnApply")) + { + OnApply = _json.OnApply; + } + + if (variable_struct_exists(_json, "BlendMode")) + { + BlendMode = _json.BlendMode; + } + + if (variable_struct_exists(_json, "Culling")) + { + Culling = _json.Culling; + } + + if (variable_struct_exists(_json, "ZWrite")) + { + ZWrite = _json.ZWrite; + } + + if (variable_struct_exists(_json, "ZTest")) + { + ZTest = _json.ZTest; + } + + if (variable_struct_exists(_json, "ZFunc")) + { + ZFunc = _json.ZFunc; + } + + if (variable_struct_exists(_json, "AlphaTest")) + { + AlphaTest = _json.AlphaTest; + } + + if (variable_struct_exists(_json, "AlphaBlend")) + { + AlphaBlend = _json.AlphaBlend; + } + + if (variable_struct_exists(_json, "Mipmapping")) + { + Mipmapping = _json.Mipmapping; + } + + if (variable_struct_exists(_json, "MipBias")) + { + MipBias = _json.MipBias; + } + + if (variable_struct_exists(_json, "MipFilter")) + { + MipFilter = _json.MipFilter; + } + + if (variable_struct_exists(_json, "MipMin")) + { + MipMin = _json.MipMin; + } + + if (variable_struct_exists(_json, "MipMax")) + { + MipMax = _json.MipMax; + } + + if (variable_struct_exists(_json, "Anisotropy")) + { + Anisotropy = _json.Anisotropy; + } + + if (variable_struct_exists(_json, "Filtering")) + { + Filtering = _json.Filtering; + } + + if (variable_struct_exists(_json, "Repeat")) + { + Repeat = _json.Repeat; + } + + if (variable_struct_exists(_json, "BaseOpacity")) + { + if (__baseOpacitySprite != undefined) + { + sprite_delete(__baseOpacitySprite); + __baseOpacitySprite = undefined; + } + + BaseOpacity = _json.BaseOpacity; + } + + return self; + }; + + static to_file = function (_file) { + var _dirname = filename_dir(_file); + if (!directory_exists(_dirname)) + { + directory_create(_dirname); + } + + var _json = {}; + to_json(_json); + + var _jsonFile = file_text_open_write(_file); + file_text_write_string(_jsonFile, json_stringify(_json)); + file_text_close(_jsonFile); + + return self; + }; + + static from_file = function (_file, _sha1=undefined) { + Path = _file; + check_file(_file, _sha1); + from_json(bbmod_json_load(_file)); + IsLoaded = true; + return self; + }; + + static from_file_async = function (_file, _sha1=undefined, _callback=undefined) { + Path = _file; + + if (!check_file(_file, _sha1, _callback ?? bbmod_empty_callback)) + { + return self; + } + + var _json; + + try + { + _json = bbmod_json_load(_file); + } + catch (_err) + { + if (_callback) + { + _callback(_err, self); + } + return self; + } + + from_json(_json); + IsLoaded = true; + + if (_callback != undefined) + { + _callback(undefined, self); + } + + return self; + }; + + static _make_sprite = function (_r, _g, _b, _a) { + gml_pragma("forceinline"); + static _sur = noone; + if (!surface_exists(_sur)) + { + _sur = surface_create(1, 1); + } + surface_set_target(_sur); + draw_clear_alpha(make_color_rgb(_r, _g, _b), _a); + surface_reset_target(); + return sprite_create_from_surface(_sur, 0, 0, 1, 1, false, false, 0, 0); + }; + + /// @func set_base_opacity(_color) + /// + /// @desc Changes the base color and opacity to a uniform value for the + /// entire material. + /// + /// @param {Struct.BBMOD_Color} _color The new base color and opacity. + /// + /// @return {Struct.BBMOD_BaseMaterial} Returns `self`. + static set_base_opacity = function (_color) { + if (__baseOpacitySprite != undefined) + { + sprite_delete(__baseOpacitySprite); + } + var _isReal = is_real(_color); + __baseOpacitySprite = _make_sprite( + _isReal ? color_get_red(_color) : _color.Red, + _isReal ? color_get_green(_color) : _color.Green, + _isReal ? color_get_blue(_color) : _color.Blue, + _isReal ? argument[1] : _color.Alpha + ); + BaseOpacity = sprite_get_texture(__baseOpacitySprite, 0); + return self; + }; + + /// @func apply(_vertexFormat) + /// + /// @desc Makes this material the current one. + /// + /// @param {Struct.BBMOD_VertexFormat} _vertexFormat The vertex format of + /// meshes that we are going to use the material for. + /// + /// @return {Bool} Returns `true` if the material was applied. + /// + /// @see BBMOD_Material.reset + static apply = function (_vertexFormat) { + if ((RenderPass & (1 << bbmod_render_pass_get())) == 0) + { + return false; + } + + var _shader = Shaders[bbmod_render_pass_get()]; + var _shaderRaw = _shader.get_variant(_vertexFormat); + + if (_shaderRaw == undefined) + { + __bbmod_warning( + "Shader variant for vertex format " + + string(_vertexFormat.get_hash()) + + " was not found! Material not applied!"); + return false; + } + + var _shaderChanged = false; + if (BBMOD_SHADER_CURRENT != _shader + || shader_current() != _shaderRaw) + { + if (BBMOD_SHADER_CURRENT != undefined) + { + BBMOD_SHADER_CURRENT.reset(); + } + shader_set(_shaderRaw); + BBMOD_SHADER_CURRENT = _shader; + _shaderChanged = true; + } + + if (global.__bbmodMaterialCurrent != self) + { + // TODO: GPU settings override per render pass! + var _isShadows = (bbmod_render_pass_get() == BBMOD_ERenderPass.Shadows); + + if (global.__bbmodMaterialCurrent != undefined) + { + gpu_pop_state(); + } + + gpu_push_state(); + + if (_shaderChanged) + { + with (_shader) + { + on_set(); + __bbmod_shader_set_globals(_shaderRaw); + } + _shaderChanged = false; + } + + gpu_set_blendmode(_isShadows ? bm_normal : BlendMode); + gpu_set_blendenable(_isShadows ? false : AlphaBlend); + gpu_set_cullmode(Culling); + gpu_set_zwriteenable(_isShadows ? true : ZWrite); + gpu_set_ztestenable(_isShadows ? true : ZTest); + gpu_set_zfunc(ZFunc); + gpu_set_tex_mip_enable(Mipmapping); + gpu_set_tex_mip_bias(MipBias); + gpu_set_tex_mip_filter(MipFilter); + gpu_set_tex_min_mip(MipMin); + gpu_set_tex_max_mip(MipMax); + gpu_set_tex_max_aniso(Anisotropy); + gpu_set_tex_filter(Filtering); + gpu_set_tex_repeat(Repeat); + + _shader.set_material(self); + global.__bbmodMaterialCurrent = self; + } + + if (_shaderChanged) + { + with (_shader) + { + on_set(); + __bbmod_shader_set_globals(_shaderRaw); + } + _shader.set_material(self); + } + + if (OnApply != undefined) + { + OnApply(self); + } + + return true; + }; + + /// @func set_shader(_pass, _shader) + /// + /// @desc Defines a shader used in a specific render pass. + /// + /// @param {Real} _pass The render pass. Use values from {@link BBMOD_ERenderPass}. + /// @param {Struct.BBMOD_Shader} _shader The shader used in the render pass. + /// + /// @return {Struct.BBMOD_Material} Returns `self`. + /// + /// @see BBMOD_Material.get_shader + /// @see bbmod_render_pass_set + static set_shader = function (_pass, _shader) { + gml_pragma("forceinline"); + RenderPass |= (1 << _pass); + Shaders[@ _pass] = _shader; + return self; + }; + + /// @func has_shader(_pass) + /// + /// @desc Checks whether the material has a shader for the render pass. + /// + /// @param {Real} _pass The render pass. Use values from {@link BBMOD_ERenderPass}. + /// + /// @return {Bool} Returns `true` if the material has a shader for the + /// render pass. + static has_shader = function (_pass) { + gml_pragma("forceinline"); + return ((RenderPass & (1 << _pass)) != 0); + }; + + /// @func get_shader(_pass) + /// + /// @desc Retrieves a shader used in a specific render pass. + /// + /// @param {Real} _pass The render pass. Use values from + /// {@link BBMOD_ERenderPass}. + /// + /// @return {Struct.BBMOD_Shader} The shader or `undefined`. + /// + /// @see BBMOD_Material.set_shader + static get_shader = function (_pass) { + gml_pragma("forceinline"); + return Shaders[_pass]; + }; + + /// @func remove_shader(_pass) + /// + /// @desc Removes a shader used in a specific render pass. + /// + /// @param {Real} _pass The render pass. + /// + /// @return {Struct.BBMOD_Material} Returns `self`. + static remove_shader = function (_pass) { + gml_pragma("forceinline"); + RenderPass &= ~(1 << _pass); + Shaders[@ _pass] = undefined; + return self; + }; + + /// @func reset() + /// + /// @desc Resets the current material to `undefined`. + /// + /// @return {Struct.BBMOD_Material} Returns `self`. + /// + /// @see BBMOD_Material.apply + /// @see bbmod_material_reset + static reset = function () { + gml_pragma("forceinline"); + bbmod_material_reset(); + return self; + }; + + static destroy = function () { + Resource_destroy(); + if (__baseOpacitySprite != undefined) + { + sprite_delete(__baseOpacitySprite); + } + return undefined; + }; + + if (_shader != undefined) + { + set_shader(BBMOD_ERenderPass.Forward, _shader); + } +} + +/// @func bbmod_material_reset() +/// +/// @desc Resets the current material to `undefined`. Every block of code +/// rendering models must start and end with this function! +/// +/// @example +/// ```gml +/// bbmod_material_reset(); +/// +/// // Render static batch of trees +/// treeBatch.submit(matTree); +/// +/// // Render characters +/// var _world = matrix_get(matrix_world); +/// with (OCharacter) +/// { +/// matrix_set(matrix_world, matrix_build(x, y, z, 0, 0, direction, 1, 1, 1)); +/// animationPlayer.submit(); +/// } +/// matrix_set(matrix_world, _world); +/// +/// bbmod_material_reset(); +/// ``` +/// @see BBMOD_Material.reset +function bbmod_material_reset() +{ + gml_pragma("forceinline"); + if (global.__bbmodMaterialCurrent != undefined) + { + gpu_pop_state(); + global.__bbmodMaterialCurrent = undefined; + } + if (BBMOD_SHADER_CURRENT != undefined) + { + BBMOD_SHADER_CURRENT.reset(); + } +} diff --git a/scripts/BBMOD_Material/BBMOD_Material.yy b/scripts/BBMOD_Material/BBMOD_Material.yy new file mode 100644 index 000000000..5a03a2ae1 --- /dev/null +++ b/scripts/BBMOD_Material/BBMOD_Material.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Material", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Matrix/BBMOD_Matrix.gml b/scripts/BBMOD_Matrix/BBMOD_Matrix.gml new file mode 100644 index 000000000..ed6b86342 --- /dev/null +++ b/scripts/BBMOD_Matrix/BBMOD_Matrix.gml @@ -0,0 +1,818 @@ +/// @func BBMOD_Matrix([_raw]) +/// +/// @desc A matrix. +/// +/// @param {Array} [_raw] A raw GameMaker matrix. If `undefined`, then an +/// identity matrix is created. +function BBMOD_Matrix(_raw=undefined) constructor +{ + /// @var {Array} A raw GameMaker matrix. + Raw = _raw ?? matrix_build_identity(); + + /// @func Copy(_dest) + /// + /// @desc Copies the matrix to another matrix. + /// + /// @param {Struct.BBMOD_Matrix} _dest The destination matrix. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static Copy = function (_dest) { + gml_pragma("forceinline"); + array_copy(_dest.Raw, 0, Raw, 0, 16); + return self; + }; + + /// @func Clone() + /// + /// @desc Creates a clone of the matrix. + /// + /// @return {Struct.BBMOD_Matrix} The clone of the matrix. + static Clone = function () { + var _clone = new BBMOD_Matrix(); + Copy(_clone); + return _clone; + }; + + /// @func Set(_index, _value) + /// + /// @desc Sets matrix value at specific index. + /// + /// @param {Real} _index The index to change the value at. + /// @param {Real} _value The new value. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static Set = function (_index, _value) { + gml_pragma("forceinline"); + Raw[@ _index] = _value; + return self; + }; + + /// @func FromArray(_array[, _index]) + /// + /// @desc Initializes the matrix from an array. + /// + /// @param {Array} _array The array to read values from. + /// + /// @param {Real} [_index] The index to start reading at. Defaults to 0. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static FromArray = function (_array, _index=0) { + gml_pragma("forceinline"); + array_copy(Raw, 0, _array, _index, 16); + return self; + }; + + /// @func ToArray([_array[, _index]]) + /// + /// @desc Writes the matrix into an array. + /// + /// @param {Array} [_array] The array to write the matrix to. If + /// `undefined`, then a new one is created. + /// @param {Real} [_index] The index to start writing at. Defaults to 0. + /// + /// @return {Array} The destination array. + static ToArray = function (_array=undefined, _index=0) { + gml_pragma("forceinline"); + _array ??= array_create(16, 0.0); + array_copy(_array, _index, Raw, 0, 16); + return _array; + }; + + /// @func FromBuffer(_buffer, _type) + /// + /// @desc Initializes the matrix from a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to read values from. + /// @param {Constant.BufferDataType} _type The type of values. Use one of + /// the `buffer_` constants. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static FromBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + var _index = 0; + repeat (16) + { + Raw[_index++] = buffer_read(_buffer, _type); + } + return self; + }; + + /// @func ToBuffer(_buffer, _type) + /// + /// @desc Writes the matrix into a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to write to. + /// @param {Real} _type The type of values. Use one of the `buffer_` constants. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static ToBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + var _index = 0; + repeat (16) + { + buffer_write(_buffer, _type, Raw[_index++]); + } + return self; + }; + + /// @func FromColumns(_c1, _c2, _c3, _c4) + /// + /// @desc Initializes the matrix from columns. + /// + /// @param {Struct.BBMOD_Vec4} _c1 A vector containing the first column. + /// @param {Struct.BBMOD_Vec4} _c2 A vector containing the second column. + /// @param {Struct.BBMOD_Vec4} _c3 A vector containing the third column. + /// @param {Struct.BBMOD_Vec4} _c4 A vector containing the fourth column. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static FromColumns = function (_c1, _c2, _c3, _c4) { + gml_pragma("forceinline"); + Raw = [ + _c1.X, _c2.X, _c3.X, _c4.X, + _c1.Y, _c2.Y, _c3.Y, _c4.Y, + _c1.Z, _c2.Z, _c3.Z, _c4.Z, + _c1.W, _c2.W, _c3.W, _c4.W, + ]; + return self; + }; + + /// @func FromRows(_r1, _r2, _r3, _r4) + /// + /// @desc Initializes the matrix from rows. + /// + /// @param {Struct.BBMOD_Vec4} _r1 A vector containing the first row. + /// @param {Struct.BBMOD_Vec4} _r2 A vector containing the second row. + /// @param {Struct.BBMOD_Vec4} _r3 A vector containing the third row. + /// @param {Struct.BBMOD_Vec4} _r4 A vector containing the fourth row. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static FromRows = function (_r1, _r2, _r3, _r4) { + gml_pragma("forceinline"); + Raw = [ + _r1.X, _r1.Y, _r1.Z, _r1.W, + _r2.X, _r2.Y, _r2.Z, _r2.W, + _r3.X, _r3.Y, _r3.Z, _r3.W, + _r4.X, _r4.Y, _r4.Z, _r4.W, + ]; + return self; + }; + + /// @func FromLookAt(_from, _to, _up) + /// + /// @desc Initializes a look-at matrix. + /// + /// @param {Struct.BBMOD_Vec3} _from The position of the camera. + /// @param {Struct.BBMOD_Vec3} _to The position where the camera is looking at. + /// @param {Struct.BBMOD_Vec3} _up The direction up. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static FromLookAt = function (_from, _to, _up) { + gml_pragma("forceinline"); + Raw = matrix_build_lookat( + _from.X, _from.Y, _from.Z, + _to.X, _to.Y, _to.Z, + _up.X, _up.Y, _up.Z); + return self; + }; + + /// @func FromWorld() + /// + /// @desc Initializes the matrix using the current world matrix. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static FromWorld = function () { + gml_pragma("forceinline"); + Raw = matrix_get(matrix_world); + return self; + }; + + /// @func FromView() + /// + /// @desc Initializes the matrix using the current view matrix. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static FromView = function () { + gml_pragma("forceinline"); + Raw = matrix_get(matrix_view); + return self; + }; + + /// @func FromProjection() + /// + /// @desc Initializes the matrix using the current projection matrix. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static FromProjection = function () { + gml_pragma("forceinline"); + Raw = matrix_get(matrix_projection); + return self; + }; + + /// @func FromWorldViewProjection() + /// + /// @desc Initializes the matrix using the current `world * view * projection` + /// matrix. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static FromWorldViewProjection = function () { + gml_pragma("forceinline"); + Raw = matrix_multiply( + matrix_multiply(matrix_get(matrix_world), matrix_get(matrix_view)), + matrix_get(matrix_projection)); + return self; + }; + + /// @func ApplyWorld() + /// + /// @desc Changes the current world matrix to this one. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static ApplyWorld = function () { + gml_pragma("forceinline"); + matrix_set(matrix_world, Raw); + return self; + }; + + /// @func ApplyView() + /// + /// @desc Changes the view world matrix to this one. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static ApplyView = function () { + gml_pragma("forceinline"); + matrix_set(matrix_view, Raw); + return self; + }; + + /// @func ApplyProjection() + /// + /// @desc Changes the current projeciton matrix to this one. + /// + /// @return {Struct.BBMOD_Matrix} Returns `self`. + static ApplyProjection = function () { + gml_pragma("forceinline"); + matrix_set(matrix_projection, Raw); + return self; + }; + + /// @func ToEuler([_array[, _index]]) + /// + /// @desc Retrieves euler angles from the matrix. + /// + /// @param {Array} [_array] An array to write the X,Y,Z angles to. + /// If `undefined`, a new one is created. + /// + /// @param {Real} [_index] The index to start writing at. + /// + /// @return {Array} The destination array. + static ToEuler = function (_array=undefined, _index=0) { + gml_pragma("forceinline"); + + _array ??= array_create(3, 0.0); + + var _thetaX, _thetaY, _thetaZ; + var _m = Raw; + var _m6 = _m[6]; + + if (_m6 < 1.0) + { + if (_m6 > -1.0) + { + _thetaX = arcsin(-_m6); + _thetaY = arctan2(_m[2], _m[10]); + _thetaZ = arctan2(_m[4], _m[5]); + } + else + { + _thetaX = pi * 0.5; + _thetaY = -arctan2(-_m[1], _m[0]); + _thetaZ = 0.0; + } + } + else + { + _thetaX = -pi * 0.5; + _thetaY = arctan2(-_m[1], _m[0]); + _thetaZ = 0.0; + } + + _array[@ _index] = (360.0 + radtodeg(_thetaX)) mod 360.0; + _array[@ _index + 1] = (360.0 + radtodeg(_thetaY)) mod 360.0; + _array[@ _index + 2] = (360.0 + radtodeg(_thetaZ)) mod 360.0; + + return _array; + }; + + /// @func Determinant() + /// + /// @desc Computes the determinant of the matrix. + /// + /// @return {Real} The determinant. + static Determinant = function () { + gml_pragma("forceinline"); + var _m = Raw; + var _m0 = _m[ 0]; + var _m1 = _m[ 1]; + var _m2 = _m[ 2]; + var _m3 = _m[ 3]; + var _m4 = _m[ 4]; + var _m5 = _m[ 5]; + var _m6 = _m[ 6]; + var _m7 = _m[ 7]; + var _m8 = _m[ 8]; + var _m9 = _m[ 9]; + var _m10 = _m[10]; + var _m11 = _m[11]; + var _m12 = _m[12]; + var _m13 = _m[13]; + var _m14 = _m[14]; + var _m15 = _m[15]; + return (0.0 + + (_m3 * _m6 * _m9 * _m12) - (_m2 * _m7 * _m9 * _m12) - (_m3 * _m5 * _m10 * _m12) + (_m1 * _m7 * _m10 * _m12) + + (_m2 * _m5 * _m11 * _m12) - (_m1 * _m6 * _m11 * _m12) - (_m3 * _m6 * _m8 * _m13) + (_m2 * _m7 * _m8 * _m13) + + (_m3 * _m4 * _m10 * _m13) - (_m0 * _m7 * _m10 * _m13) - (_m2 * _m4 * _m11 * _m13) + (_m0 * _m6 * _m11 * _m13) + + (_m3 * _m5 * _m8 * _m14) - (_m1 * _m7 * _m8 * _m14) - (_m3 * _m4 * _m9 * _m14) + (_m0 * _m7 * _m9 * _m14) + + (_m1 * _m4 * _m11 * _m14) - (_m0 * _m5 * _m11 * _m14) - (_m2 * _m5 * _m8 * _m15) + (_m1 * _m6 * _m8 * _m15) + + (_m2 * _m4 * _m9 * _m15) - (_m0 * _m6 * _m9 * _m15) - (_m1 * _m4 * _m10 * _m15) + (_m0 * _m5 * _m10 * _m15)); + }; + + /// @func Inverse() + /// + /// @desc Creates a matrix that is inverse to this one. + /// + /// @return {Struct.BBMOD_Matrix} The inverse matrix. + static Inverse = function () { + gml_pragma("forceinline"); + + var _res = new BBMOD_Matrix(); + var _m = Raw; + var _m0 = _m[ 0]; + var _m1 = _m[ 1]; + var _m2 = _m[ 2]; + var _m3 = _m[ 3]; + var _m4 = _m[ 4]; + var _m5 = _m[ 5]; + var _m6 = _m[ 6]; + var _m7 = _m[ 7]; + var _m8 = _m[ 8]; + var _m9 = _m[ 9]; + var _m10 = _m[10]; + var _m11 = _m[11]; + var _m12 = _m[12]; + var _m13 = _m[13]; + var _m14 = _m[14]; + var _m15 = _m[15]; + + var _determinant = (0.0 + + (_m3 * _m6 * _m9 * _m12) - (_m2 * _m7 * _m9 * _m12) - (_m3 * _m5 * _m10 * _m12) + (_m1 * _m7 * _m10 * _m12) + + (_m2 * _m5 * _m11 * _m12) - (_m1 * _m6 * _m11 * _m12) - (_m3 * _m6 * _m8 * _m13) + (_m2 * _m7 * _m8 * _m13) + + (_m3 * _m4 * _m10 * _m13) - (_m0 * _m7 * _m10 * _m13) - (_m2 * _m4 * _m11 * _m13) + (_m0 * _m6 * _m11 * _m13) + + (_m3 * _m5 * _m8 * _m14) - (_m1 * _m7 * _m8 * _m14) - (_m3 * _m4 * _m9 * _m14) + (_m0 * _m7 * _m9 * _m14) + + (_m1 * _m4 * _m11 * _m14) - (_m0 * _m5 * _m11 * _m14) - (_m2 * _m5 * _m8 * _m15) + (_m1 * _m6 * _m8 * _m15) + + (_m2 * _m4 * _m9 * _m15) - (_m0 * _m6 * _m9 * _m15) - (_m1 * _m4 * _m10 * _m15) + (_m0 * _m5 * _m10 * _m15)); + + var _s = 1.0 / _determinant; + + _res.Raw = [ + _s * ((_m6 * _m11 * _m13) - (_m7 * _m10 * _m13) + (_m7 * _m9 * _m14) - (_m5 * _m11 * _m14) - (_m6 * _m9 * _m15) + (_m5 * _m10 * _m15)), + _s * ((_m3 * _m10 * _m13) - (_m2 * _m11 * _m13) - (_m3 * _m9 * _m14) + (_m1 * _m11 * _m14) + (_m2 * _m9 * _m15) - (_m1 * _m10 * _m15)), + _s * ((_m2 * _m7 * _m13) - (_m3 * _m6 * _m13) + (_m3 * _m5 * _m14) - (_m1 * _m7 * _m14) - (_m2 * _m5 * _m15) + (_m1 * _m6 * _m15)), + _s * ((_m3 * _m6 * _m9) - (_m2 * _m7 * _m9) - (_m3 * _m5 * _m10) + (_m1 * _m7 * _m10) + (_m2 * _m5 * _m11) - (_m1 * _m6 * _m11)), + _s * ((_m7 * _m10 * _m12) - (_m6 * _m11 * _m12) - (_m7 * _m8 * _m14) + (_m4 * _m11 * _m14) + (_m6 * _m8 * _m15) - (_m4 * _m10 * _m15)), + _s * ((_m2 * _m11 * _m12) - (_m3 * _m10 * _m12) + (_m3 * _m8 * _m14) - (_m0 * _m11 * _m14) - (_m2 * _m8 * _m15) + (_m0 * _m10 * _m15)), + _s * ((_m3 * _m6 * _m12) - (_m2 * _m7 * _m12) - (_m3 * _m4 * _m14) + (_m0 * _m7 * _m14) + (_m2 * _m4 * _m15) - (_m0 * _m6 * _m15)), + _s * ((_m2 * _m7 * _m8) - (_m3 * _m6 * _m8) + (_m3 * _m4 * _m10) - (_m0 * _m7 * _m10) - (_m2 * _m4 * _m11) + (_m0 * _m6 * _m11)), + _s * ((_m5 * _m11 * _m12) - (_m7 * _m9 * _m12) + (_m7 * _m8 * _m13) - (_m4 * _m11 * _m13) - (_m5 * _m8 * _m15) + (_m4 * _m9 * _m15)), + _s * ((_m3 * _m9 * _m12) - (_m1 * _m11 * _m12) - (_m3 * _m8 * _m13) + (_m0 * _m11 * _m13) + (_m1 * _m8 * _m15) - (_m0 * _m9 * _m15)), + _s * ((_m1 * _m7 * _m12) - (_m3 * _m5 * _m12) + (_m3 * _m4 * _m13) - (_m0 * _m7 * _m13) - (_m1 * _m4 * _m15) + (_m0 * _m5 * _m15)), + _s * ((_m3 * _m5 * _m8) - (_m1 * _m7 * _m8) - (_m3 * _m4 * _m9) + (_m0 * _m7 * _m9) + (_m1 * _m4 * _m11) - (_m0 * _m5 * _m11)), + _s * ((_m6 * _m9 * _m12) - (_m5 * _m10 * _m12) - (_m6 * _m8 * _m13) + (_m4 * _m10 * _m13) + (_m5 * _m8 * _m14) - (_m4 * _m9 * _m14)), + _s * ((_m1 * _m10 * _m12) - (_m2 * _m9 * _m12) + (_m2 * _m8 * _m13) - (_m0 * _m10 * _m13) - (_m1 * _m8 * _m14) + (_m0 * _m9 * _m14)), + _s * ((_m2 * _m5 * _m12) - (_m1 * _m6 * _m12) - (_m2 * _m4 * _m13) + (_m0 * _m6 * _m13) + (_m1 * _m4 * _m14) - (_m0 * _m5 * _m14)), + _s * ((_m1 * _m6 * _m8) - (_m2 * _m5 * _m8) + (_m2 * _m4 * _m9) - (_m0 * _m6 * _m9) - (_m1 * _m4 * _m10) + (_m0 * _m5 * _m10)), + ]; + + return _res; + }; + + /// @func Mul(_matrix, ...) + /// + /// @desc Multiplies matrices. + /// + /// @param {Struct.BBMOD_Matrix} _matrix The first matrix to multiply with. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + /// + /// @example + /// ```gml + /// var _world = new BBMOD_Matrix().FromWorld(); + /// var _view = new BBMOD_Matrix().FromView(); + /// var _projection = new BBMOD_Matrix().FromProjection(); + /// var _worldViewProjection = _world.Mul(_view, _projection); + /// ``` + /// Please note that this example only shows that you can pass multiple + /// matrices to this method. If you would actually like to get the + /// `world * view * projection` matrix, you can simply call + /// {@link BBMOD_Matrix.FromWorldViewProjection}. + static Mul = function (_matrix) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + var _raw = matrix_multiply(Raw, _matrix.Raw); + var _index = 1; + repeat (argument_count - 1) + { + _raw = matrix_multiply(_raw, argument[_index++]); + } + _res.Raw = _raw; + return _res; + }; + + /// @func MulComponentwise(_matrix) + /// + /// @desc Multiplies each component of the matrix with corresponding + /// component of other matrix. + /// + /// @param {Struct.BBMOD_Matrix} _matrix The matrix to multiply componentwise + /// with. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static MulComponentwise = function (_matrix) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + var _selfRaw = Raw; + var _otherRaw = _matrix.Raw; + var _index = 0; + repeat (16) + { + _res.Raw[@ _index] = _selfRaw[_index] * _otherRaw[_index]; + ++_index; + } + return _res; + }; + + /// @func AddComponentwise(_matrix) + /// + /// @desc Adds each component of the matrix to corresponding component of + /// other matrix. + /// + /// @param {Struct.BBMOD_Matrix} _matrix The matrix to add componentwise with. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static AddComponentwise = function (_matrix) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + var _selfRaw = Raw; + var _otherRaw = _matrix.Raw; + var _index = 0; + repeat (16) + { + _res.Raw[@ _index] = _selfRaw[_index] + _otherRaw[_index]; + ++_index; + } + return _res; + }; + + /// @func SubComponentwise(_matrix) + /// + /// @desc Subtracts each component of a matrix from corresponding component + /// of this matrix. + /// + /// @param {Struct.BBMOD_Matrix} _matrix The matrix that subtracts + /// componentwise from this one. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static SubComponentwise = function (_matrix) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + var _selfRaw = Raw; + var _otherRaw = _matrix.Raw; + var _index = 0; + repeat (16) + { + _res.Raw[@ _index] = _selfRaw[_index] - _otherRaw[_index]; + ++_index; + } + return _res; + }; + + /// @func Transform(_vector) + /// + /// @desc Transforms a vector by the matrix and returns the result as a new + /// vector. + /// + /// @param {Struct.BBMOD_Vec4} _vector The vector to transform. + /// + /// @return {Struct.BBMOD_Vec4} The tranformed vector. + static Transform = function (_vector) { + gml_pragma("forceinline"); + return _vector.Transform(Raw); + }; + + /// @func Transpose() + /// + /// @desc Creates a transpose of this matrix. + /// + /// @return {Struct.BBMOD_Matrix} The transposed matrix. + static Transpose = function () { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + var _m = Raw; + _res.Raw = [ + _m[0], _m[4], _m[ 8], _m[12], + _m[1], _m[5], _m[ 9], _m[13], + _m[2], _m[6], _m[10], _m[14], + _m[3], _m[7], _m[11], _m[15], + ]; + return _res; + }; + + /// @func Translate(_x[, _y, _z]) + /// + /// @desc Translates the matrix. + /// + /// @param {Real, Struct.BBMOD_Vec3} _x Translation on the X axis or a + /// vector with translations on all axes. + /// @param {Real} [_y] Translation on the Y axis. Use `undefined` if `_x` is + /// a vector. Defaults to `undefined`. + /// @param {Real} [_z] Translation on the Z axis. Use `undefined` if `_x` is + /// a vector. Defaults to `undefined`. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static Translate = function (_x, _y=undefined, _z=undefined) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + is_struct(_x) + ? matrix_build(_x.X, _x.Y, _x.Z, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0) + : matrix_build(_x, _y, _z, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0)); + return _res; + }; + + /// @func TranslateX(_x) + /// + /// @desc Translates the matrix on the X axis. + /// + /// @param {Real} _x Translation on the X axis. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static TranslateX = function (_x) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + matrix_build(_x, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0)); + return _res; + }; + + /// @func TranslateY(_y) + /// + /// @desc Translates the matrix on the Y axis. + /// + /// @param {Real} _y Translation on the Y axis. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static TranslateY = function (_y) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + matrix_build(0.0, _y, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0)); + return _res; + }; + + /// @func TranslateZ(_z) + /// + /// @desc Translates the matrix on the Z axis. + /// + /// @param {Real} _z Translation on the Z axis. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static TranslateZ = function (_z) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + matrix_build(0.0, 0.0, _z, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0)); + return _res; + }; + + /// @func RotateEuler(_x[, _y, _z]) + /// + /// @desc Rotates the matrix using euler angles. + /// + /// @param {Real, Struct.BBMOD_Vec3} _x Rotation on the X axis or a vector + /// with rotations on all axes. + /// @param {Real} [_y] Rotation on the Y axis. Use `undefined` if `_x` is a + /// vector. Defaults to `undefined`. + /// @param {Real} [_z] Rotation on the Z axis. Use `undefined` if `_x` is a + /// vector. Defaults to `undefined`. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + /// + /// @note The order of rotations is YXZ, same as in `matrix_build`. + static RotateEuler = function (_x, _y=undefined, _z=undefined) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + is_struct(_x) + ? matrix_build(0.0, 0.0, 0.0, _x.X, _x.Y, _x.Z, 1.0, 1.0, 1.0) + : matrix_build(0.0, 0.0, 0.0, _x, _y, _z, 1.0, 1.0, 1.0)); + return _res; + }; + + /// @func RotateQuat(_quat) + /// + /// @desc Rotates the matrix using a quaternion + /// + /// @param {Struct.BBMOD_Quaternion} _quat The quaternion to rotate the + /// matrix with. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static RotateQuat = function (_quat) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, _quat.ToMatrix()); + return _res; + }; + + /// @func RotateX(_x) + /// + /// @desc Rotates the matrix on the X axis. + /// + /// @param {Real} _x Rotation on the X axis. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static RotateX = function (_x) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + matrix_build(0.0, 0.0, 0.0, _x, 0.0, 0.0, 1.0, 1.0, 1.0)); + return _res; + }; + + /// @func RotateY(_y) + /// + /// @desc Rotates the matrix on the Y axis. + /// + /// @param {Real} _y Rotation on the Y axis. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static RotateY = function (_y) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + matrix_build(0.0, 0.0, 0.0, 0.0, _y, 0.0, 1.0, 1.0, 1.0)); + return _res; + }; + + /// @func RotateZ(_z) + /// + /// @desc Rotates the matrix on the Z axis. + /// + /// @param {Real} _z Rotation on the Z axis. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static RotateZ = function (_z) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + matrix_build(0.0, 0.0, 0.0, 0.0, 0.0, _z, 1.0, 1.0, 1.0)); + return _res; + }; + + /// @func Scale(_x[, _y, _z]) + /// + /// @desc Scales the matrix. + /// + /// @param {Real, Struct.BBMOD_Vec3} _x Scale on the X axis or a vector with + /// scale on all axes. + /// @param {Real} [_y] Scale on the Y axis. Use `undefined` if `_x` is a + /// vector. Defaults to `undefined`. + /// @param {Real} [_z] Scale on the Z axis. Use `undefined` if `_x` is a + /// vector. Defaults to `undefined`. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static Scale = function (_x, _y=undefined, _z=undefined) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + is_struct(_x) + ? matrix_build(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, _x.X, _x.Y, _x.Z) + : matrix_build(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, _x, _y, _z)); + return _res; + }; + + /// @func ScaleComponentwise(_s) + /// + /// @desc Scales each component of the matrix. + /// + /// @param {Real} _s The value to scale the components with. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static ScaleComponentwise = function (_s) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + var _selfRaw = Raw; + var _index = 0; + repeat (16) + { + _res.Raw[@ _index] = _selfRaw[_index] * _s; + ++_index; + } + return _res; + }; + + /// @func ScaleX(_x) + /// + /// @desc Scales the matrix on the X axis. + /// + /// @param {Real} _x Scale on the X axis. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static ScaleX = function (_x) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + matrix_build(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, _x, 1.0, 1.0)); + return _res; + }; + + /// @func ScaleY(_y) + /// + /// @desc Scales the matrix on the Y axis. + /// + /// @param {Real} _y Scale on the Y axis. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static ScaleY = function (_y) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + matrix_build(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, _y, 1.0)); + return _res; + }; + + /// @func ScaleZ(_z) + /// + /// @desc Scales the matrix on the Z axis. + /// + /// @param {Real} _z Scale on the Z axis. + /// + /// @return {Struct.BBMOD_Matrix} The resulting matrix. + static ScaleZ = function (_z) { + gml_pragma("forceinline"); + var _res = new BBMOD_Matrix(); + _res.Raw = matrix_multiply(Raw, + matrix_build(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, _z)); + return _res; + }; +} + +/// @func bbmod_matrix_build_normalmatrix(_m[, _dest[, _index]]) +/// +/// @desc Creates a matrix using which you can safely transform normal vectors. +/// +/// @param {Array} _m A matrix to build the normal matrix from. +/// @param {Array} [_dest] An array to store the resulting matrix to. Defaults +/// to a new array empty array. +/// @param {Real} [_index] An index to start writing the result at. Defaults to +/// 0. +/// +/// @return {Array} The destination array. +function bbmod_matrix_build_normalmatrix(_m, _dest=[], _index=0) +{ + gml_pragma("forceinline"); + + var _m0 = _m[ 0]; + var _m1 = _m[ 1]; + var _m2 = _m[ 2]; + var _m4 = _m[ 4]; + var _m5 = _m[ 5]; + var _m6 = _m[ 6]; + var _m8 = _m[ 8]; + var _m9 = _m[ 9]; + var _m10 = _m[10]; + + var _determinant = (0.0 + + _m0 * ((_m5 * _m10) - (_m6 * _m9)) + + _m4 * ((_m9 * _m2) - (_m1 * _m10)) + + _m8 * ((_m1 * _m6) - (_m5 * _m2))); + + var _s = 1.0 / _determinant; + + _dest[@ _index + 0] = _s * ((_m5 * _m10) - (_m6 * _m9)); + _dest[@ _index + 1] = _s * ((_m8 * _m6) - (_m4 * _m10)); + _dest[@ _index + 2] = _s * ((_m4 * _m9) - (_m8 * _m5)); + _dest[@ _index + 3] = 0.0; + _dest[@ _index + 4] = _s * ((_m9 * _m2) - (_m1 * _m10)); + _dest[@ _index + 5] = _s * ((_m0 * _m10) - (_m8 * _m2)); + _dest[@ _index + 6] = _s * ((_m1 * _m8) - (_m0 * _m9)); + _dest[@ _index + 7] = 0.0; + _dest[@ _index + 8] = _s * ((_m1 * _m6) - (_m2 * _m5)); + _dest[@ _index + 9] = _s * ((_m2 * _m4) - (_m0 * _m6)); + _dest[@ _index + 10] = _s * ((_m0 * _m5) - (_m1 * _m4)); + _dest[@ _index + 11] = 0.0; + _dest[@ _index + 12] = 0.0; + _dest[@ _index + 13] = 0.0; + _dest[@ _index + 14] = 0.0; + _dest[@ _index + 15] = 1.0; + + return _dest; +} diff --git a/scripts/BBMOD_Matrix/BBMOD_Matrix.yy b/scripts/BBMOD_Matrix/BBMOD_Matrix.yy new file mode 100644 index 000000000..205175624 --- /dev/null +++ b/scripts/BBMOD_Matrix/BBMOD_Matrix.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Matrix", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Math", + "path": "folders/_Extensions/BBMOD/Core/Math.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Mesh/BBMOD_Mesh.gml b/scripts/BBMOD_Mesh/BBMOD_Mesh.gml new file mode 100644 index 000000000..8589f3219 --- /dev/null +++ b/scripts/BBMOD_Mesh/BBMOD_Mesh.gml @@ -0,0 +1,510 @@ +/// @func BBMOD_Mesh(_vertexFormat[, _model]) +/// +/// @extends BBMOD_Class +/// +/// @desc A mesh struct. +/// +/// @param {Struct.BBMOD_VertexFormat} _vertexFormat The vertex format of the +/// or `undefined`. +/// @param {Struct.BBMOD_Model} [_model] The model to which the mesh belongs or +/// `undefined`. +/// +/// @see BBMOD_Model +/// @see BBMOD_VertexFormat +function BBMOD_Mesh(_vertexFormat, _model=undefined) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Struct.BBMOD_Model} The model to which the mesh belongs or + /// `undefined`. + /// @readonly + Model = _model; + + /// @var {Real} An index of a material that the mesh uses. + /// @see BBMOD_Model.MaterialCount + /// @see BBMOD_Model.MaterialNames + /// @readonly + MaterialIndex = 0; + + /// @var {Struct.BBMOD_Vec3} The minimum coordinate of the mesh's bounding + /// box. Available since model version 3.1. Can be `undefined`. + BboxMin = undefined; + + /// @var {Struct.BBMOD_Vec3} The maximum coordinate of the mesh's bounding + /// box. Available since model version 3.1. Can be `undefined`. + BboxMax = undefined; + + /// @var {Id.VertexBuffer} A vertex buffer. + /// @readonly + VertexBuffer = undefined; + + /// @var {Struct.BBMOD_VertexFormat} The vertex format of the mesh. + /// @readonly + VertexFormat = _vertexFormat; + + /// @var {Constant.PrimitiveType} The primitive type of the mesh. + /// @readonly + PrimitiveType = pr_trianglelist; + + /// @var {Bool} If `true` then the mesh is frozen. + /// @readonly + Frozen = false; + + /// @func copy(_dest) + /// + /// @desc Copies mesh data into another mesh. + /// + /// @param {Struct.BBMOD_Mesh} _dest The mesh to copy data to. + /// + /// @return {Struct.BBMOD_Mesh} Returns `self`. + static copy = function (_dest) { + _dest.Model = Model; + _dest.MaterialIndex = MaterialIndex; + _dest.BboxMin = (BboxMin != undefined) ? BboxMin.Clone() : undefined; + _dest.BboxMax = (BboxMax != undefined) ? BboxMax.Clone() : undefined; + + if (_dest.VertexBuffer != undefined) + { + vertex_delete_buffer(_dest.VertexBuffer); + } + + if (VertexBuffer) + { + var _buffer = buffer_create_from_vertex_buffer(VertexBuffer, buffer_fixed, 1); + _dest.VertexBuffer = vertex_create_buffer_from_buffer(_buffer, + (VertexFormat != undefined) ? VertexFormat.Raw : Model.VertexFormat.Raw); + buffer_delete(_buffer); + } + else + { + _dest.VertexBuffer = undefined; + } + + _dest.VertexFormat = VertexFormat; + _dest.PrimitiveType = PrimitiveType; + + return self; + }; + + /// @func clone() + /// + /// @desc Creates a clone of the mesh. + /// + /// @return {Struct.BBMOD_Mesh} The created clone. + static clone = function () { + var _clone = new BBMOD_Mesh(VertexFormat, Model); + copy(_clone); + return _clone; + }; + + /// @func from_buffer(_buffer) + /// + /// @desc Loads mesh data from a bufffer. + /// + /// @param {Id.Buffer} _buffer The buffer to load the data from. + /// + /// @return {Struct.BBMOD_Mesh} Returns `self`. + static from_buffer = function (_buffer) { + MaterialIndex = buffer_read(_buffer, buffer_u32); + + if (Model.VersionMinor >= 1) + { + BboxMin = new BBMOD_Vec3().FromBuffer(_buffer, buffer_f32); + BboxMax = new BBMOD_Vec3().FromBuffer(_buffer, buffer_f32); + } + + if (Model.VersionMinor >= 2) + { + VertexFormat = __bbmod_vertex_format_load(_buffer, Model.VersionMinor); + PrimitiveType = buffer_read(_buffer, buffer_u32); + } + + var _vertexCount = buffer_read(_buffer, buffer_u32); + if (_vertexCount > 0) + { + var _size = _vertexCount * VertexFormat.get_byte_size(); + if (_size > 0) + { + VertexBuffer = vertex_create_buffer_from_buffer_ext( + _buffer, VertexFormat.Raw, buffer_tell(_buffer), _vertexCount); + buffer_seek(_buffer, buffer_seek_relative, _size); + } + } + + return self; + }; + + /// @func to_buffer(_buffer) + /// + /// @desc Writes mesh data to a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to write the data to. + /// + /// @return {Struct.BBMOD_Mesh} Returns `self`. + static to_buffer = function (_buffer) { + buffer_write(_buffer, buffer_u32, MaterialIndex); + + var _versionMinor = Model.VersionMinor; + + if (_versionMinor >= 1) + { + BboxMin.ToBuffer(_buffer, buffer_f32); + BboxMax.ToBuffer(_buffer, buffer_f32); + } + + if (_versionMinor >= 2) + { + __bbmod_vertex_format_save(VertexFormat, _buffer, _versionMinor); + buffer_write(_buffer, buffer_u32, PrimitiveType); + } + + var _bufferVertices = buffer_create_from_vertex_buffer(VertexBuffer, buffer_fixed, 1); + var _bufferVerticesSize = buffer_get_size(_bufferVertices); + var _vertexCount = _bufferVerticesSize / VertexFormat.get_byte_size(); + + buffer_write(_buffer, buffer_u32, _vertexCount); + buffer_copy(_bufferVertices, 0, _bufferVerticesSize, _buffer, buffer_tell(_buffer)); + buffer_seek(_buffer, buffer_seek_relative, _bufferVerticesSize); + buffer_delete(_bufferVertices); + + return self; + }; + + /// @func freeze() + /// + /// @desc Freezes the mesh. This makes it render faster. + /// + /// @return {Struct.BBMOD_Mesh} Returns `self`. + static freeze = function () { + gml_pragma("forceinline"); + if (!Frozen) + { + vertex_freeze(VertexBuffer); + Frozen = true; + } + return self; + }; + + /// @func submit(_material, _transform, _batchData) + /// + /// @desc Immediately submits the mesh for rendering. + /// + /// @param {Struct.BBMOD_BaseMaterial} _material The material to use. + /// @param {Array} _transform An array of bone transform or `undefined`. + /// @param {Array, Array>} _batchData Data for dynamic + /// batching or `undefined`. + /// + /// @return {Struct.BBMOD_Mesh} Returns `self`. + static submit = function (_material, _transform, _batchData) { + if (!_material.apply(VertexFormat)) + { + return self; + } + + var _vertexBuffer = VertexBuffer; + var _primitiveType = PrimitiveType; + var _baseOpacity = _material.BaseOpacity; + + with (BBMOD_SHADER_CURRENT) + { + set_instance_id(); + set_material_index(other.MaterialIndex); + + if (_transform != undefined) + { + set_bones(_transform); + } + + if (_batchData != undefined) + { + if (is_array(_batchData[0])) + { + var _dataIndex = 0; + repeat (array_length(_batchData)) + { + set_batch_data(_batchData[_dataIndex++]); + vertex_submit(_vertexBuffer, _primitiveType, _baseOpacity); + } + } + else + { + set_batch_data(_batchData); + vertex_submit(_vertexBuffer, _primitiveType, _baseOpacity); + } + } + else + { + vertex_submit(_vertexBuffer, _primitiveType, _baseOpacity); + } + } + + return self; + }; + + /// @func render(_material, _transform, _batchData, _matrix) + /// + /// @desc Enqueues the mesh for rendering. + /// + /// @param {Struct.BBMOD_BaseMaterial} _material The material to use. + /// @param {Array} _transform An array of bone transforms or `undefined`. + /// @param {Array, Array>} _batchData Data for dynamic + /// batching or `undefined`. + /// @param {Array} _matrix The current world matrix. + /// + /// @return {Struct.BBMOD_Mesh} Returns `self`. + static render = function (_material, _transform, _batchData, _matrix) { + gml_pragma("forceinline"); + if (_batchData != undefined) + { + _material.RenderQueue.draw_mesh_batched( + VertexBuffer, VertexFormat, PrimitiveType, MaterialIndex, _material, _matrix, _batchData); + } + else if (_transform != undefined) + { + _material.RenderQueue.draw_mesh_animated( + VertexBuffer, VertexFormat, PrimitiveType, MaterialIndex, _material, _matrix, _transform); + } + else + { + _material.RenderQueue.draw_mesh( + VertexBuffer, VertexFormat, PrimitiveType, MaterialIndex, _material, _matrix); + } + return self; + }; + + /// @func __to_dynamic_batch(_dynamicBatch) + /// + /// @param {Struct.BBMOD_DynamicBatch} _dynamicBatch + /// + /// @return {Struct.BBMOD_Mesh} Returns `self`. + /// + /// @throws {BBMOD_Exception} When adding the mesh into a batch with a + /// different primitive type. + /// + /// @private + static __to_dynamic_batch = function (_dynamicBatch) { + if (_dynamicBatch.PrimitiveType != undefined + && _dynamicBatch.PrimitiveType != PrimitiveType) + { + throw new BBMOD_Exception( + "Cannot add a mesh to a dynamic batch with a different primitive type!"); + } + _dynamicBatch.PrimitiveType = PrimitiveType; + var _vertexBuffer = _dynamicBatch.VertexBuffer; + var _vertexFormat = VertexFormat; + var _hasVertices = _vertexFormat.Vertices; + var _hasNormals = _vertexFormat.Normals; + var _hasUvs = _vertexFormat.TextureCoords; + var _hasUvs2 = _vertexFormat.TextureCoords2; + var _hasColors = _vertexFormat.Colors; + var _hasTangentW = _vertexFormat.TangentW; + var _hasBones = _vertexFormat.Bones; + var _hasIds = _vertexFormat.Ids; + var _meshVertexBuffer = VertexBuffer; + var _vertexCount = vertex_get_number(_meshVertexBuffer); + var _buffer = buffer_create_from_vertex_buffer( + _meshVertexBuffer, buffer_fixed, 1); + var _id = 0; + + repeat (_dynamicBatch.Size) + { + buffer_seek(_buffer, buffer_seek_start, 0); + + repeat (_vertexCount) + { + if (_hasVertices) + { + var _x = buffer_read(_buffer, buffer_f32); + var _y = buffer_read(_buffer, buffer_f32); + var _z = buffer_read(_buffer, buffer_f32); + + vertex_position_3d(_vertexBuffer, _x, _y, _z); + } + + if (_hasNormals) + { + var _x = buffer_read(_buffer, buffer_f32); + var _y = buffer_read(_buffer, buffer_f32); + var _z = buffer_read(_buffer, buffer_f32); + + vertex_normal(_vertexBuffer, _x, _y, _z); + } + + if (_hasUvs) + { + var _u = buffer_read(_buffer, buffer_f32); + var _v = buffer_read(_buffer, buffer_f32); + + vertex_texcoord(_vertexBuffer, _u, _v); + } + + if (_hasUvs2) + { + buffer_read(_buffer, buffer_f32); + buffer_read(_buffer, buffer_f32); + } + + if (_hasColors) + { + var _a = buffer_read(_buffer, buffer_u8); + var _b = buffer_read(_buffer, buffer_u8); + var _g = buffer_read(_buffer, buffer_u8); + var _r = buffer_read(_buffer, buffer_u8); + + vertex_color(_vertexBuffer, make_color_rgb(_r, _g, _b), _a); + } + + if (_hasTangentW) + { + var _x = buffer_read(_buffer, buffer_f32); + var _y = buffer_read(_buffer, buffer_f32); + var _z = buffer_read(_buffer, buffer_f32); + var _w = buffer_read(_buffer, buffer_f32); + + vertex_float4(_vertexBuffer, _x, _y, _z, _w); + } + + if (_hasBones) + { + repeat (8) + { + buffer_read(_buffer, buffer_f32); + } + } + + if (_hasIds) + { + buffer_read(_buffer, buffer_f32); + } + + vertex_float1(_vertexBuffer, _id); + } + + ++_id; + } + + buffer_delete(_buffer); + + return self; + }; + + /// @func __to_static_batch(_model, _staticBatch, _transform) + /// + /// @param {Struct.BBMOD_Model} _model + /// @param {Struct.BBMOD_StaticBatch} _staticBatch + /// @param {Array} _transform + /// + /// @return {Struct.BBMOD_Mesh} Returns `self`. + /// + /// @throws {BBMOD_Exception} When adding the mesh into a batch with a + /// different primitive type. + /// + /// @private + static __to_static_batch = function (_model, _staticBatch, _transform) { + if (_staticBatch.PrimitiveType != undefined + && _staticBatch.PrimitiveType != PrimitiveType) + { + throw new BBMOD_Exception( + "Cannot add a mesh to a static batch with a different primitive type!"); + } + _staticBatch.PrimitiveType = PrimitiveType; + var _vertexBuffer = _staticBatch.VertexBuffer; + var _vertexFormat = _model.VertexFormat; + var _hasVertices = _vertexFormat.Vertices; + var _hasNormals = _vertexFormat.Normals; + var _hasUvs = _vertexFormat.TextureCoords; + var _hasUvs2 = _vertexFormat.TextureCoords2; + var _hasColors = _vertexFormat.Colors; + var _hasTangentW = _vertexFormat.TangentW; + var _hasBones = _vertexFormat.Bones; + var _hasIds = _vertexFormat.Ids; + var _meshVertexBuffer = VertexBuffer; + var _buffer = buffer_create_from_vertex_buffer( + _meshVertexBuffer, buffer_fixed, 1); + + buffer_seek(_buffer, buffer_seek_start, 0); + + repeat (vertex_get_number(_meshVertexBuffer)) + { + if (_hasVertices) + { + var _x = buffer_read(_buffer, buffer_f32); + var _y = buffer_read(_buffer, buffer_f32); + var _z = buffer_read(_buffer, buffer_f32); + var _vec = new BBMOD_Vec3(_x, _y, _z).Transform(_transform); + + vertex_position_3d(_vertexBuffer, _vec.X, _vec.Y, _vec.Z); + } + + if (_hasNormals) + { + var _x = buffer_read(_buffer, buffer_f32); + var _y = buffer_read(_buffer, buffer_f32); + var _z = buffer_read(_buffer, buffer_f32); + var _vec = new BBMOD_Vec4(_x, _y, _z, 0.0).Transform(_transform); + + vertex_normal(_vertexBuffer, _vec.X, _vec.Y, _vec.Z); + } + + if (_hasUvs) + { + var _u = buffer_read(_buffer, buffer_f32); + var _v = buffer_read(_buffer, buffer_f32); + + vertex_texcoord(_vertexBuffer, _u, _v); + } + + if (_hasUvs2) + { + buffer_read(_buffer, buffer_f32); + buffer_read(_buffer, buffer_f32); + } + + if (_hasColors) + { + var _a = buffer_read(_buffer, buffer_u8); + var _b = buffer_read(_buffer, buffer_u8); + var _g = buffer_read(_buffer, buffer_u8); + var _r = buffer_read(_buffer, buffer_u8); + + vertex_color(_vertexBuffer, make_color_rgb(_r, _g, _b), _a); + } + + if (_hasTangentW) + { + var _x = buffer_read(_buffer, buffer_f32); + var _y = buffer_read(_buffer, buffer_f32); + var _z = buffer_read(_buffer, buffer_f32); + var _w = buffer_read(_buffer, buffer_f32); + + vertex_float4(_vertexBuffer, _x, _y, _z, _w); + } + + if (_hasBones) + { + repeat (8) + { + buffer_read(_buffer, buffer_f32); + } + } + + if (_hasIds) + { + buffer_read(_buffer, buffer_f32); + } + } + + buffer_delete(_buffer); + + return self; + }; + + static destroy = function () { + Class_destroy(); + vertex_delete_buffer(VertexBuffer); + return undefined; + }; +} diff --git a/scripts/BBMOD_Mesh/BBMOD_Mesh.yy b/scripts/BBMOD_Mesh/BBMOD_Mesh.yy new file mode 100644 index 000000000..5d2fe669f --- /dev/null +++ b/scripts/BBMOD_Mesh/BBMOD_Mesh.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Mesh", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Model", + "path": "folders/_Extensions/BBMOD/Core/Model.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MeshBuilder/BBMOD_MeshBuilder.gml b/scripts/BBMOD_MeshBuilder/BBMOD_MeshBuilder.gml new file mode 100644 index 000000000..d56fa37f3 --- /dev/null +++ b/scripts/BBMOD_MeshBuilder/BBMOD_MeshBuilder.gml @@ -0,0 +1,283 @@ +/// @func BBMOD_MeshBuilder([_primitiveType]) +/// +/// @extends BBMOD_Class +/// +/// @desc Allows you to build meshes through code. +/// +/// @param {Constant.PrimitiveType} [_primitiveType] The primitive type of built +/// meshes. Defaults to `pr_trianglelist`. +/// +/// @example +/// Following code shows how you can create a plane mesh using the mesh builder. +/// ```gml +/// var _meshBuilder = new BBMOD_MeshBuilder(); +/// var _vertexFormat = new BBMOD_VertexFormat(true); +/// var _v1 = new BBMOD_Vertex(_vertexFormat); +/// _v1.Position = new BBMOD_Vec3(0.0, 0.0, 0.0); +/// var _v1Ind = _meshBuilder.add_vertex(v1); +/// var _v2 = new BBMOD_Vertex(_vertexFormat); +/// _v2.Position = new BBMOD_Vec3(1.0, 0.0, 0.0); +/// var _v2Ind = _meshBuilder.add_vertex(v2); +/// var _v3 = new BBMOD_Vertex(_vertexFormat); +/// _v3.Position = new BBMOD_Vec3(1.0, 1.0, 0.0); +/// var _v3Ind = _meshBuilder.add_vertex(v3); +/// var _v4 = new BBMOD_Vertex(_vertexFormat); +/// _v4.Position = new BBMOD_Vec3(0.0, 1.0, 0.0); +/// var _v4Ind = _meshBuilder.add_vertex(v4); +/// _meshBuilder.add_face(_v1, _v2, _v4); +/// _meshBuilder.add_face(_v2, _v3, _v4); +/// var _mesh = _meshBuilder.build(); +/// _meshBuilder = _meshBuilder.destroy(); +/// ``` +/// +/// @see BBMOD_Mesh +/// @see BBMOD_Vertex +/// @see BBMOD_VertexFormat +function BBMOD_MeshBuilder(_primitiveType=pr_trianglelist) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Constant.PrimitiveType} The primitive type of built meshes. + /// @readonly + PrimitiveType = _primitiveType; + + /// @var {Id.DsList} List of mesh vertices. + /// @readonly + Vertices = ds_list_create(); + + /// @var {Id.DsList} List of vertex indices that make up a face. First + /// three indices are the first face, next three indices are the second face + /// etc. + /// @readonly + Faces = ds_list_create(); + + /// @func add_vertex(_vertex) + /// + /// @desc Adds a vertex to the mesh. + /// + /// @param {Struct.BBMOD_Vertex} _vertex The vertex to add. + /// + /// @return {Real} Returns the index of the vertex. + /// + /// @see BBMOD_Vertex + static add_vertex = function (_vertex) { + gml_pragma("forceinline"); + var _ind = ds_list_size(Vertices); + ds_list_add(Vertices, _vertex); + return _ind; + }; + + /// @func add_face(_index...) + /// + /// @desc Adds a face to the mesh. + /// + /// @param {Real} _index The index of the first vertex of the face. + /// + /// @return {Real} Returns the index within the list of faces where + /// the first vertex was stored. + /// + /// @see BBMOD_MeshBuilder.Faces + static add_face = function (_index) { + gml_pragma("forceinline"); + var _ind = ds_list_size(Faces); + var i = 0; + repeat (argument_count) + { + ds_list_add(Faces, argument[i++]); + } + return _ind; + }; + + /// @func make_tangents() + /// + /// @desc Makes tangent and bitangent vectors for added vertices. + /// + /// @return {Struct.BBMOD_MeshBuilder} Returns `self`. + /// + /// @throws {BBMOD_Exception} If an error occurs during the process. + /// + /// @note This works only for the `pr_trianglelist` primitive type! + /// + /// @source https://gamedev.stackexchange.com/a/68617 + static make_tangents = function () { + if (PrimitiveType != pr_trianglelist) + { + throw new BBMOD_Exception( + "Cannot build tangents and bitangents for meshes with primitive" + + " type other than pr_trianglelist!"); + } + + var _faceCount = ds_list_size(Faces); + var _vertexCount = ds_list_size(Vertices); + var _tan2 = array_create(_vertexCount); + + for (var i = 0; i < _vertexCount; ++i) + { + Vertices[| i].TangentW = new BBMOD_Vec4(0.0); + _tan2[@ i] = new BBMOD_Vec3(0.0); + } + + for (var i = 0; i < _faceCount; i += 3) + { + var _i1 = Faces[| i]; + var _i2 = Faces[| i + 1]; + var _i3 = Faces[| i + 2]; + + var _f1 = Vertices[| _i1]; + var _f2 = Vertices[| _i2]; + var _f3 = Vertices[| _i3]; + + // Check if their vertex formats have the required data + if (!(_f1.VertexFormat.Vertices && _f1.VertexFormat.Normals && _f1.VertexFormat.TextureCoords + && _f2.VertexFormat.Vertices && _f2.VertexFormat.Normals && _f2.VertexFormat.TextureCoords + && _f3.VertexFormat.Vertices && _f3.VertexFormat.Normals && _f3.VertexFormat.TextureCoords)) + { + throw new BBMOD_Exception( + "Vertices, normals and texture coords are required to build tangents!"); + } + + var _v1 = _f1.Position; + var _v2 = _f2.Position; + var _v3 = _f3.Position; + + var _w1 = _f1.TextureCoord; + var _w2 = _f2.TextureCoord; + var _w3 = _f3.TextureCoord; + + var _x1 = _v2.X - _v1.X; + var _x2 = _v3.X - _v1.X; + var _y1 = _v2.Y - _v1.Y; + var _y2 = _v3.Y - _v1.Y; + var _z1 = _v2.Z - _v1.Z; + var _z2 = _v3.Z - _v1.Z; + + var _s1 = _w2.X - _w1.X; + var _s2 = _w3.X - _w1.X; + var _t1 = _w2.Y - _w1.Y; + var _t2 = _w3.Y - _w1.Y; + + if (_s1 * _t2 == _t1 * _s2) + { + _s1 = 1.0; + _t1 = 0.0; + _s2 = 0.0; + _t2 = 1.0; + } + + var _r = 1.0 / ((_s1 * _t2) - (_s2 * _t1)); + var _temp; + + var _sdirX = ((_t2 * _x1) - (_t1 * _x2)) * _r; + var _sdirY = ((_t2 * _y1) - (_t1 * _y2)) * _r; + var _sdirZ = ((_t2 * _z1) - (_t1 * _z2)) * _r; + + _temp = _f1.TangentW; + _temp.X += _sdirX; + _temp.Y += _sdirY; + _temp.Z += _sdirZ; + + _temp = _f2.TangentW; + _temp.X += _sdirX; + _temp.Y += _sdirY; + _temp.Z += _sdirZ; + + _temp = _f3.TangentW; + _temp.X += _sdirX; + _temp.Y += _sdirY; + _temp.Z += _sdirZ; + + var _tdirX = ((_s1 * _x2) - (_s2 * _x1)) * _r; + var _tdirY = ((_s1 * _y2) - (_s2 * _y1)) * _r; + var _tdirZ = ((_s1 * _z2) - (_s2 * _z1)) * _r; + + _temp = _tan2[_i1]; + _temp.X += _tdirX; + _temp.Y += _tdirY; + _temp.Z += _tdirZ; + + _temp = _tan2[_i2]; + _temp.X += _tdirX; + _temp.Y += _tdirY; + _temp.Z += _tdirZ; + + _temp = _tan2[_i3]; + _temp.X += _tdirX; + _temp.Y += _tdirY; + _temp.Z += _tdirZ; + } + + for (var i = 0; i < _vertexCount; ++i) + { + var _v = Vertices[| i]; + var _n = _v.Normal; + var _t = new BBMOD_Vec3( + _v.TangentW.X, + _v.TangentW.Y, + _v.TangentW.Z + ); + + // Gram-Schmidt orthogonalize + var _tNew = _t.Sub(_n.Scale(_n.Dot(_t))).Normalize().Copy(_v.TangentW); + + // Calculate handedness + var _dot = _n.Cross(_tNew).Dot(_tan2[i]); + _v.TangentW.W = (_dot < 0.0) ? -1.0 : 1.0; + } + + return self; + }; + + /// @func build([_vertexFormat]) + /// + /// @desc Builds a mesh from the added vertices and faces. + /// + /// @param {Struct.BBMOD_VertexFormat} [_vertexFormat] The vertex format of + /// the mesh. This must be compatible with the format of the added vertices. + /// If `undefined`, then the format of the first added vertex is used. + /// + /// @return {Struct.BBMOD_Mesh} The created mesh. + /// + /// @throws {BBMOD_Exception} If an error occurs during the mesh building + /// process. + /// + /// @see BBMOD_Mesh + /// @see BBMOD_VertexFormat + static build = function (_vertexFormat=undefined) { + _vertexFormat ??= Vertices[| 0].VertexFormat; + + var _vbuffer = vertex_create_buffer(); + var _faceCount = ds_list_size(Faces); + + vertex_begin(_vbuffer, _vertexFormat.Raw); + for (var i = 0; i < _faceCount; ++i) + { + var _ind = Faces[| i]; + var _vertex = Vertices[| _ind]; + try + { + _vertex.to_vertex_buffer(_vbuffer, _vertexFormat); + } + catch (_err) + { + vertex_delete_buffer(_vbuffer); + throw _err; + } + } + vertex_end(_vbuffer); + + var _mesh = new BBMOD_Mesh(_vertexFormat); + _mesh.VertexBuffer = _vbuffer; + _mesh.PrimitiveType = PrimitiveType; + return _mesh; + }; + + static destroy = function () { + Class_destroy(); + ds_list_destroy(Vertices); + ds_list_destroy(Faces); + return undefined; + }; +} diff --git a/scripts/BBMOD_MeshBuilder/BBMOD_MeshBuilder.yy b/scripts/BBMOD_MeshBuilder/BBMOD_MeshBuilder.yy new file mode 100644 index 000000000..8d697f4df --- /dev/null +++ b/scripts/BBMOD_MeshBuilder/BBMOD_MeshBuilder.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MeshBuilder", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Model", + "path": "folders/_Extensions/BBMOD/Core/Model.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixColorFromHealthModule/BBMOD_MixColorFromHealthModule.yy b/scripts/BBMOD_MixColorFromHealthModule/BBMOD_MixColorFromHealthModule.yy new file mode 100644 index 000000000..ef269c0c8 --- /dev/null +++ b/scripts/BBMOD_MixColorFromHealthModule/BBMOD_MixColorFromHealthModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixColorFromHealthModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromHealth", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromHealth.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixColorFromHealthModule/bbmod_mixcolorfromhealthmodule.gml b/scripts/BBMOD_MixColorFromHealthModule/bbmod_mixcolorfromhealthmodule.gml new file mode 100644 index 000000000..e1e23010e --- /dev/null +++ b/scripts/BBMOD_MixColorFromHealthModule/bbmod_mixcolorfromhealthmodule.gml @@ -0,0 +1,67 @@ +/// @func BBMOD_MixColorFromHealthModule([_property[, _from[, _to]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes particles' color property +/// between two values based on their remaining health. +/// +/// @param {Real} [_property] The first of the four consecutive properties that +/// together form a color. Use values from {@link BBMOD_EParticle}. Defaults to +/// `undefined`. +/// @param {Struct.BBMOD_Color} [_from] The color when the particle has full +/// health. Defaults to {@link BBMOD_C_WHITE}. +/// @param {Struct.BBMOD_Color} [_to] The color when the particle has no health +/// left. Defaults to `_from`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixColorFromHealthModule( + _property=undefined, + _from=BBMOD_C_WHITE, + _to=_from.Clone() +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties that together + /// form a color. Use values from {@link BBMOD_EParticle}. Default value is + /// `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Color} The color when the particle has full + /// health. Default value is {@link BBMOD_C_WHITE}. + From = _from; + + /// @var {Struct.BBMOD_Color} The color when the particle has no health + /// left. Default value is the same as {@link BBMOD_MixColorFromHealthModule.From}. + To = _to; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _particles = _emitter.Particles; + var _from = From; + var _fromR = _from.Red; + var _fromG = _from.Green; + var _fromB = _from.Blue; + var _fromA = _from.Alpha; + var _to = To; + var _toR = _to.Red; + var _toG = _to.Green; + var _toB = _to.Blue; + var _toA = _to.Alpha; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.HealthLeft, _particleIndex] + / _particles[# BBMOD_EParticle.Health, _particleIndex], 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_toR, _fromR, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_toG, _fromG, _factor); + _particles[# _property + 2, _particleIndex] = lerp(_toB, _fromB, _factor); + _particles[# _property + 3, _particleIndex] = lerp(_toA, _fromA, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixColorFromSpeedModule/BBMOD_MixColorFromSpeedModule.yy b/scripts/BBMOD_MixColorFromSpeedModule/BBMOD_MixColorFromSpeedModule.yy new file mode 100644 index 000000000..56afd1e67 --- /dev/null +++ b/scripts/BBMOD_MixColorFromSpeedModule/BBMOD_MixColorFromSpeedModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixColorFromSpeedModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromSpeed", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromSpeed.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixColorFromSpeedModule/bbmod_mixcolorfromspeedmodule.gml b/scripts/BBMOD_MixColorFromSpeedModule/bbmod_mixcolorfromspeedmodule.gml new file mode 100644 index 000000000..54f706c31 --- /dev/null +++ b/scripts/BBMOD_MixColorFromSpeedModule/bbmod_mixcolorfromspeedmodule.gml @@ -0,0 +1,87 @@ +/// @func BBMOD_MixColorFromSpeedModule([_property[, _from[, _to[, _min[, _max]]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes particles' color property +/// between two values based on the magnitude of their velocity vector. +/// +/// @param {Real} [_property] The first of the four consecutive properties that +/// together form a color. Use values from {@link BBMOD_EParticle}. Defaults to +/// `undefined`. +/// @param {Struct.BBMOD_Color} [_from] The color when the particle has full +/// health. Defaults to {@link BBMOD_C_WHITE}. +/// @param {Struct.BBMOD_Color} [_to] The color when the particle has no health +/// left. Defaults to `_from`. +/// @param {Real} [_min] If the particles' speed is less than this, then the +/// property is equal to `_from`. Defaults to 0.0. +/// @param {Real} [_max] If the particles' speed is greater than this, then the +/// property is equal to `_to`. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixColorFromSpeedModule( + _property=undefined, + _from=BBMOD_C_WHITE, + _to=_from.Clone(), + _min=0.0, + _max=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties that together + /// form a color. Use values from {@link BBMOD_EParticle}. Default value is + /// `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Color} The color when the particle has full + /// health. Default value is {@link BBMOD_C_WHITE}. + From = _from; + + /// @var {Struct.BBMOD_Color} The color when the particle has no health + /// left. Default value is the same as {@link BBMOD_MixColorFromSpeedModule.From}. + To = _to; + + /// @var {Real} If the particles' speed is less than this, then the property + /// is equal to {@link BBMOD_ColorFromSpeedModule.From}. Default value is 0.0. + Min = _min; + + /// @var {Real} If the particles' speed is greater than this, then the property + /// is equal to {@link BBMOD_ColorFromSpeedModule.To}. Default value is 1.0. + Max = _max; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _particles = _emitter.Particles; + var _from = From; + var _fromR = _from.Red; + var _fromG = _from.Green; + var _fromB = _from.Blue; + var _fromA = _from.Alpha; + var _to = To; + var _toR = _to.Red; + var _toG = _to.Green; + var _toB = _to.Blue; + var _toA = _to.Alpha; + var _min = Min; + var _max = Max; + var _div = _max - _min; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _velX = _particles[# BBMOD_EParticle.VelocityX, _particleIndex]; + var _velY = _particles[# BBMOD_EParticle.VelocityY, _particleIndex]; + var _velZ = _particles[# BBMOD_EParticle.VelocityZ, _particleIndex]; + var _speed = sqrt((_velX * _velX) + (_velY + _velY) + (_velZ * _velZ)); + var _factor = clamp((_speed - _min) / _div, 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_toR, _fromR, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_toG, _fromG, _factor); + _particles[# _property + 2, _particleIndex] = lerp(_toB, _fromB, _factor); + _particles[# _property + 3, _particleIndex] = lerp(_toA, _fromA, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixColorModule/BBMOD_MixColorModule.yy b/scripts/BBMOD_MixColorModule/BBMOD_MixColorModule.yy new file mode 100644 index 000000000..bae2ac646 --- /dev/null +++ b/scripts/BBMOD_MixColorModule/BBMOD_MixColorModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixColorModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixColorModule/bbmod_mixcolormodule.gml b/scripts/BBMOD_MixColorModule/bbmod_mixcolormodule.gml new file mode 100644 index 000000000..72296d132 --- /dev/null +++ b/scripts/BBMOD_MixColorModule/bbmod_mixcolormodule.gml @@ -0,0 +1,49 @@ +/// @func BBMOD_MixColorModule([_property[, _from[, _to]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that randomly mixes particles' color +/// property when they are spawned. +/// +/// @param {Real} [_property] The first of the four consecutive properties that +/// together form a color. Use values from {@link BBMOD_EParticle}. Defaults to +/// `undefined`. +/// @param {Struct.BBMOD_Color} [_from] The color to mix from. Defaults to +/// {@link BBMOD_C_WHITE}. +/// @param {Struct.BBMOD_Color} [_to] The value to mix to. Defaults to `_from`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixColorModule( + _property=undefined, + _from=BBMOD_C_WHITE, + _to=_from.Clone() +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties that together + /// form a color. Use values from {@link BBMOD_EParticle}. Default value is + /// `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Color} The color to mix from. Default value is + /// {@link BBMOD_C_WHITE}. + From = _from; + + /// @var {Struct.BBMOD_Color} The color to mix to. Default value is the + /// same as {@link BBMOD_MixColorModule.From}. + To = _to; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + var _from = From; + var _to = To; + var _factor = random(1.0); + _emitter.Particles[# Property, _particleIndex] = lerp(_from.Red, _to.Red, _factor); + _emitter.Particles[# Property + 1, _particleIndex] = lerp(_from.Green, _to.Green, _factor); + _emitter.Particles[# Property + 2, _particleIndex] = lerp(_from.Blue, _to.Blue, _factor); + _emitter.Particles[# Property + 3, _particleIndex] = lerp(_from.Alpha, _to.Alpha, _factor); + } + }; +} diff --git a/scripts/BBMOD_MixColorOverTimeModule/BBMOD_MixColorOverTimeModule.yy b/scripts/BBMOD_MixColorOverTimeModule/BBMOD_MixColorOverTimeModule.yy new file mode 100644 index 000000000..b0a376cf7 --- /dev/null +++ b/scripts/BBMOD_MixColorOverTimeModule/BBMOD_MixColorOverTimeModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixColorOverTimeModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyOverTime", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyOverTime.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixColorOverTimeModule/bbmod_mixcolorovertimemodule.gml b/scripts/BBMOD_MixColorOverTimeModule/bbmod_mixcolorovertimemodule.gml new file mode 100644 index 000000000..ac08d0823 --- /dev/null +++ b/scripts/BBMOD_MixColorOverTimeModule/bbmod_mixcolorovertimemodule.gml @@ -0,0 +1,75 @@ +/// @func BBMOD_MixColorOverTimeModule([_property[, _from[, _to[, _duration]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes particles' color property +/// between two values based on their time alive. +/// +/// @param {Real} [_property] The first of the four consecutive properties that +/// together form a color. Use values from {@link BBMOD_EParticle}. Defaults to +/// `undefined`. +/// @param {Struct.BBMOD_Color} [_from] The color when the particle has full +/// health. Defaults to {@link BBMOD_C_WHITE}. +/// @param {Struct.BBMOD_Color} [_to] The color when the particle has no health +/// left. Defaults to `_from`. +/// @param {Real} [_duration] How long in seconds it takes to mix between the +/// two values. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixColorOverTimeModule( + _property=undefined, + _from=BBMOD_C_WHITE, + _to=_from.Clone(), + _duration=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties that together + /// form a color. Use values from {@link BBMOD_EParticle}. Default value is + /// `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Color} The color when the particle has full + /// health. Default value is {@link BBMOD_C_WHITE}. + From = _from; + + /// @var {Struct.BBMOD_Color} The color when the particle has no health + /// left. Default value is the same as {@link BBMOD_MixColorOverTimeModule.From}. + To = _to; + + /// @var {Real} How long in seconds it takes to mix between the two values. + /// Default value is 1.0. + Duration = _duration; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _particles = _emitter.Particles; + var _from = From; + var _fromR = _from.Red; + var _fromG = _from.Green; + var _fromB = _from.Blue; + var _fromA = _from.Alpha; + var _to = To; + var _toR = _to.Red; + var _toG = _to.Green; + var _toB = _to.Blue; + var _toA = _to.Alpha; + var _duration = Duration; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.TimeAlive, _particleIndex] + / _duration, 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_fromR, _toR, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_fromG, _toG, _factor); + _particles[# _property + 2, _particleIndex] = lerp(_fromB, _toB, _factor); + _particles[# _property + 3, _particleIndex] = lerp(_fromA, _toA, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixEmissionModule/BBMOD_MixEmissionModule.yy b/scripts/BBMOD_MixEmissionModule/BBMOD_MixEmissionModule.yy new file mode 100644 index 000000000..fa9010d92 --- /dev/null +++ b/scripts/BBMOD_MixEmissionModule/BBMOD_MixEmissionModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixEmissionModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Emission", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Emission.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixEmissionModule/bbmod_mixemissionmodule.gml b/scripts/BBMOD_MixEmissionModule/bbmod_mixemissionmodule.gml new file mode 100644 index 000000000..5e11bf1a5 --- /dev/null +++ b/scripts/BBMOD_MixEmissionModule/bbmod_mixemissionmodule.gml @@ -0,0 +1,28 @@ +/// @func BBMOD_MixEmissionModule([_from[, _to]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that spawns random number of particles at the start +/// of a particle emitter's life. +/// +/// @param {Real} [_from] The minimum number of particles to spawn. Defaults to 1. +/// @param {Real} [_to] The maxmimum particles to spawn. Defaults to `_from`. +function BBMOD_MixEmissionModule(_from=1, _to=_from) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The minimum number of particles to spawn. Default value is 1. + From = _from; + + /// @var {Real} The maximum particles to spawn. Default value is the same as + /// {@link BBMOD_MixEmissionModule.From}. + To = _to; + + static on_start = function (_emitter) { + repeat (irandom_range(From, To)) + { + _emitter.spawn_particle(); + } + }; +} diff --git a/scripts/BBMOD_MixQuaternionFromHealthModule/BBMOD_MixQuaternionFromHealthModule.yy b/scripts/BBMOD_MixQuaternionFromHealthModule/BBMOD_MixQuaternionFromHealthModule.yy new file mode 100644 index 000000000..52c96c465 --- /dev/null +++ b/scripts/BBMOD_MixQuaternionFromHealthModule/BBMOD_MixQuaternionFromHealthModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixQuaternionFromHealthModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromHealth", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromHealth.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixQuaternionFromHealthModule/bbmod_mixquaternionfromhealthmodule.gml b/scripts/BBMOD_MixQuaternionFromHealthModule/bbmod_mixquaternionfromhealthmodule.gml new file mode 100644 index 000000000..5a2849429 --- /dev/null +++ b/scripts/BBMOD_MixQuaternionFromHealthModule/bbmod_mixquaternionfromhealthmodule.gml @@ -0,0 +1,61 @@ +/// @func BBMOD_MixQuaternionFromHealthModule([_property[, _from[, _to]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes particles' quaternion +/// property between two values based on their remaining health. +/// +/// @param {Real} [_property] The first of the four consecutive properties that +/// together form a quaternion. Use values from {@link BBMOD_EParticle}. +/// Defaults to `undefined`. +/// @param {Struct.BBMOD_Quaternion} [_from] The quaternion when the particle has +/// a full health. Defaults to an identity quaternion. +/// @param {Struct.BBMOD_Quaternion} [_to] The quaternion when the particle has +/// no health left. Defaults to `_from`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixQuaternionFromHealthModule( + _property=undefined, + _from=new BBMOD_Quaternion(), + _to=_from.Clone() +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties that together + /// form a quaternion. Use values from {@link BBMOD_EParticle}. Default + /// value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Quaternion} The quaternion when the particle has full + /// health. Default value is an identity quaternion. + From = _from; + + /// @var {Struct.BBMOD_Quaternion} The quaternion when the particle has no + /// health left. Default value is the same as + /// {@link BBMOD_MixQuaternionFromHealthModule.From}. + To = _to; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _from = From; + var _particles = _emitter.Particles; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.HealthLeft, _particleIndex] + / _particles[# BBMOD_EParticle.Health, _particleIndex], 0.0, 1.0); + var _quat = _to.Slerp(_from, _factor); + _particles[# _property, _particleIndex] = _quat.X; + _particles[# _property + 1, _particleIndex] = _quat.Y; + _particles[# _property + 2, _particleIndex] = _quat.Z; + _particles[# _property + 3, _particleIndex] = _quat.W; + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixQuaternionFromSpeedModule/BBMOD_MixQuaternionFromSpeedModule.yy b/scripts/BBMOD_MixQuaternionFromSpeedModule/BBMOD_MixQuaternionFromSpeedModule.yy new file mode 100644 index 000000000..391b34b74 --- /dev/null +++ b/scripts/BBMOD_MixQuaternionFromSpeedModule/BBMOD_MixQuaternionFromSpeedModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixQuaternionFromSpeedModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromSpeed", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromSpeed.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixQuaternionFromSpeedModule/bbmod_mixquaternionfromspeedmodule.gml b/scripts/BBMOD_MixQuaternionFromSpeedModule/bbmod_mixquaternionfromspeedmodule.gml new file mode 100644 index 000000000..400d41ce7 --- /dev/null +++ b/scripts/BBMOD_MixQuaternionFromSpeedModule/bbmod_mixquaternionfromspeedmodule.gml @@ -0,0 +1,81 @@ +/// @func BBMOD_MixQuaternionFromSpeedModule([_property[, _from[, _to[, _min[, _max]]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes particles' quaternion +/// property between two values based on the magnitude of their velocity vector. +/// +/// @param {Real} [_property] The first of the four consecutive properties that +/// together form a quaternion. Use values from {@link BBMOD_EParticle}. +/// Defaults to `undefined`. +/// @param {Struct.BBMOD_Quaternion} [_from] The quaternion when the particle has +/// a full health. Defaults to an identity quaternion. +/// @param {Struct.BBMOD_Quaternion} [_to] The quaternion when the particle has +/// no health left. Defaults to `_from`. +/// @param {Real} [_min] If the particles' speed is less than this, then the +/// property is equal to `_from`. Defaults to 0.0. +/// @param {Real} [_max] If the particles' speed is greater than this, then the +/// property is equal to `_to`. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixQuaternionFromSpeedModule( + _property=undefined, + _from=new BBMOD_Quaternion(), + _to=_from.Clone(), + _min=0.0, + _max=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties that together + /// form a quaternion. Use values from {@link BBMOD_EParticle}. Default + /// value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Quaternion} The quaternion when the particle has full + /// health. Default value is an identity quaternion. + From = _from; + + /// @var {Struct.BBMOD_Quaternion} The quaternion when the particle has no + /// health left. Default value is the same as + /// {@link BBMOD_MixQuaternionFromSpeedModule.From}. + To = _to; + + /// @var {Real} If the particles' speed is less than this, then the property + /// is equal to {@link BBMOD_MixQuaternionFromSpeedModule.From}. Default value is 0.0. + Min = _min; + + /// @var {Real} If the particles' speed is greater than this, then the property + /// is equal to {@link BBMOD_MixQuaternionFromSpeedModule.To}. Default value is 1.0. + Max = _max; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _from = From; + var _particles = _emitter.Particles; + var _min = Min; + var _max = Max; + var _div = _max - _min; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _velX = _particles[# BBMOD_EParticle.VelocityX, _particleIndex]; + var _velY = _particles[# BBMOD_EParticle.VelocityY, _particleIndex]; + var _velZ = _particles[# BBMOD_EParticle.VelocityZ, _particleIndex]; + var _speed = sqrt((_velX * _velX) + (_velY + _velY) + (_velZ * _velZ)); + var _factor = clamp((_speed - _min) / _div, 0.0, 1.0); + var _quat = _to.Slerp(_from, _factor); + _particles[# _property, _particleIndex] = _quat.X; + _particles[# _property + 1, _particleIndex] = _quat.Y; + _particles[# _property + 2, _particleIndex] = _quat.Z; + _particles[# _property + 3, _particleIndex] = _quat.W; + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixQuaternionModule/BBMOD_MixQuaternionModule.yy b/scripts/BBMOD_MixQuaternionModule/BBMOD_MixQuaternionModule.yy new file mode 100644 index 000000000..ae250cf96 --- /dev/null +++ b/scripts/BBMOD_MixQuaternionModule/BBMOD_MixQuaternionModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixQuaternionModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixQuaternionModule/bbmod_mixquaternionmodule.gml b/scripts/BBMOD_MixQuaternionModule/bbmod_mixquaternionmodule.gml new file mode 100644 index 000000000..7053ec889 --- /dev/null +++ b/scripts/BBMOD_MixQuaternionModule/bbmod_mixquaternionmodule.gml @@ -0,0 +1,47 @@ +/// @func BBMOD_MixQuaternionModule([_property[, _from[, _to]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that randomly mixes particles' quaternion +/// property when they are spawned. +/// +/// @param {Real} [_property] The first of the four consecutive properties that +/// together form a quaternion. Use values from {@link BBMOD_EParticle}. Defaults +/// to `undefined`. +/// @param {Struct.BBMOD_Quaternion} [_from] The quaternion to mix from. Defaults to +/// an identity quaternion. +/// @param {Struct.BBMOD_Quaternion} [_to] The quaternion to mix to. Defaults to `_from`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixQuaternionModule( + _property=undefined, + _from=new BBMOD_Quaternion(), + _to=_from.Clone() +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties that together + /// form a quaternion. Use values from {@link BBMOD_EParticle}. Default value + /// is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Quaternion} The quaternion to mix from. Default value is + /// an identity quaternion. + From = _from; + + /// @var {Struct.BBMOD_Quaternion} The quaternion to mix to. Default value is the + /// same as {@link BBMOD_MixQuaternionModule.From}. + To = _to; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + var _quat = From.Slerp(To, random(1.0)); + _emitter.Particles[# Property, _particleIndex] = _quat.X; + _emitter.Particles[# Property + 1, _particleIndex] = _quat.Y; + _emitter.Particles[# Property + 2, _particleIndex] = _quat.Z; + _emitter.Particles[# Property + 3, _particleIndex] = _quat.W; + } + }; +} diff --git a/scripts/BBMOD_MixQuaternionOverTimeModule/BBMOD_MixQuaternionOverTimeModule.yy b/scripts/BBMOD_MixQuaternionOverTimeModule/BBMOD_MixQuaternionOverTimeModule.yy new file mode 100644 index 000000000..02ee6906a --- /dev/null +++ b/scripts/BBMOD_MixQuaternionOverTimeModule/BBMOD_MixQuaternionOverTimeModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixQuaternionOverTimeModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyOverTime", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyOverTime.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixQuaternionOverTimeModule/bbmod_mixquaternionovertimemodule.gml b/scripts/BBMOD_MixQuaternionOverTimeModule/bbmod_mixquaternionovertimemodule.gml new file mode 100644 index 000000000..ca7a05c93 --- /dev/null +++ b/scripts/BBMOD_MixQuaternionOverTimeModule/bbmod_mixquaternionovertimemodule.gml @@ -0,0 +1,68 @@ +/// @func BBMOD_MixQuaternionOverTimeModule([_property[, _from[, _to[, _duration]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes particles' quaternion +/// property between two values based on their time alive. +/// +/// @param {Real} [_property] The first of the four consecutive properties that +/// together form a quaternion. Use values from {@link BBMOD_EParticle}. Defaults +/// to `undefined`. +/// @param {Struct.BBMOD_Quaternion} [_from] The quaternion when the particle has +/// a full health. Defaults to an identity quaternion. +/// @param {Struct.BBMOD_Quaternion} [_to] The quaternion when the particle has +/// no health left. Defaults to `_from`. +/// @param {Real} [_duration] How long in seconds it takes to mix between the +/// two values. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixQuaternionOverTimeModule( + _property=undefined, + _from=new BBMOD_Quaternion(), + _to=_from.Clone(), + _duration=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties that together + /// form a quaternion. Use values from {@link BBMOD_EParticle}. Default value + /// is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Quaternion} The quaternion when the particle has full + /// health. Default value is an identity quaternion. + From = _from; + + /// @var {Struct.BBMOD_Quaternion} The quaternion when the particle has no + /// health left. Default value is the same as + /// {@link BBMOD_MixQuaternionOverTimeModule.From}. + To = _to; + + /// @var {Real} How long in seconds it takes to mix between the two values. + /// Default value is 1.0. + Duration = _duration; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _from = From; + var _particles = _emitter.Particles; + var _duration = Duration; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.TimeAlive, _particleIndex] / _duration, 0.0, 1.0); + var _quat = _to.Slerp(_from, _factor); + _particles[# _property, _particleIndex] = _quat.X; + _particles[# _property + 1, _particleIndex] = _quat.Y; + _particles[# _property + 2, _particleIndex] = _quat.Z; + _particles[# _property + 3, _particleIndex] = _quat.W; + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixRealFromHealthModule/BBMOD_MixRealFromHealthModule.yy b/scripts/BBMOD_MixRealFromHealthModule/BBMOD_MixRealFromHealthModule.yy new file mode 100644 index 000000000..ac635d056 --- /dev/null +++ b/scripts/BBMOD_MixRealFromHealthModule/BBMOD_MixRealFromHealthModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixRealFromHealthModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromHealth", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromHealth.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixRealFromHealthModule/bbmod_mixrealfromhealthmodule.gml b/scripts/BBMOD_MixRealFromHealthModule/bbmod_mixrealfromhealthmodule.gml new file mode 100644 index 000000000..6a8543ecb --- /dev/null +++ b/scripts/BBMOD_MixRealFromHealthModule/bbmod_mixrealfromhealthmodule.gml @@ -0,0 +1,54 @@ +/// @func BBMOD_MixRealFromHealthModule([_property[, _from[, _to]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes value of particles' +/// property between two values based on their remaining health. +/// +/// @param {Real} [_property] The property to set initial value of. Use values +/// from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Real} [_from] The value when the particle has full health. +/// Defaults to 0.0. +/// @param {Real} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixRealFromHealthModule( + _property=undefined, + _from=0.0, + _to=_from +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The property to set initial value of. Use values from + /// {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Real} The value when the particle has full health. Default value + /// is 0.0. + From = _from; + + /// @var {Real} The value when the particle has no health left. Default value + /// is the same as {@link BBMOD_MixRealFromHealthModule.From}. + To = _to; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _from = From; + var _particles = _emitter.Particles; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.HealthLeft, _particleIndex] + / _particles[# BBMOD_EParticle.Health, _particleIndex], 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_to, _from, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixRealFromSpeedModule/BBMOD_MixRealFromSpeedModule.yy b/scripts/BBMOD_MixRealFromSpeedModule/BBMOD_MixRealFromSpeedModule.yy new file mode 100644 index 000000000..82697786a --- /dev/null +++ b/scripts/BBMOD_MixRealFromSpeedModule/BBMOD_MixRealFromSpeedModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixRealFromSpeedModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromSpeed", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromSpeed.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixRealFromSpeedModule/bbmod_mixrealfromspeedmodule.gml b/scripts/BBMOD_MixRealFromSpeedModule/bbmod_mixrealfromspeedmodule.gml new file mode 100644 index 000000000..915cf2210 --- /dev/null +++ b/scripts/BBMOD_MixRealFromSpeedModule/bbmod_mixrealfromspeedmodule.gml @@ -0,0 +1,74 @@ +/// @func BBMOD_MixRealFromSpeedModule([_property[, _from[, _to[, _min[, _max]]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes value of particles' +/// property between two values based on the magnitude of their velocity vector. +/// +/// @param {Real} [_property] The property to set initial value of. Use values +/// from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Real} [_from] The value when the particle has full health. +/// Defaults to 0.0. +/// @param {Real} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// @param {Real} [_min] If the particles' speed is less than this, then the +/// property is equal to `_from`. Defaults to 0.0. +/// @param {Real} [_max] If the particles' speed is greater than this, then the +/// property is equal to `_to`. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixRealFromSpeedModule( + _property=undefined, + _from=0.0, + _to=_from, + _min=0.0, + _max=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The property to set initial value of. Use values from + /// {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Real} The value when the particle has full health. Default value + /// is 0.0. + From = _from; + + /// @var {Real} The value when the particle has no health left. Default value + /// is the same as {@link BBMOD_MixRealFromSpeedModule.From}. + To = _to; + + /// @var {Real} If the particles' speed is less than this, then the property + /// is equal to {@link BBMOD_MixRealFromSpeedModule.From}. Default value is 0.0. + Min = _min; + + /// @var {Real} If the particles' speed is greater than this, then the property + /// is equal to {@link BBMOD_MixRealFromSpeedModule.To}. Default value is 1.0. + Max = _max; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _from = From; + var _particles = _emitter.Particles; + var _min = Min; + var _max = Max; + var _div = _max - _min; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _velX = _particles[# BBMOD_EParticle.VelocityX, _particleIndex]; + var _velY = _particles[# BBMOD_EParticle.VelocityY, _particleIndex]; + var _velZ = _particles[# BBMOD_EParticle.VelocityZ, _particleIndex]; + var _speed = sqrt((_velX * _velX) + (_velY + _velY) + (_velZ * _velZ)); + var _factor = clamp((_speed - _min) / _div, 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_to, _from, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixRealModule/BBMOD_MixRealModule.yy b/scripts/BBMOD_MixRealModule/BBMOD_MixRealModule.yy new file mode 100644 index 000000000..fae90dcb6 --- /dev/null +++ b/scripts/BBMOD_MixRealModule/BBMOD_MixRealModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixRealModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixRealModule/bbmod_mixrealmodule.gml b/scripts/BBMOD_MixRealModule/bbmod_mixrealmodule.gml new file mode 100644 index 000000000..bb1bc7187 --- /dev/null +++ b/scripts/BBMOD_MixRealModule/bbmod_mixrealmodule.gml @@ -0,0 +1,36 @@ +/// @func BBMOD_MixRealModule([_property[, _from[, _to]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that randomly mixes initial value of +/// particles' property between two values when they are spawned. +/// +/// @param {Real} [_property] The property to set initial value of. Use values +/// from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Real} [_from] The value to mix from. Defaults to 0.0. +/// @param {Real} [_to] The value to mix to. Defaults to `_from`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixRealModule(_property=undefined, _from=0.0, _to=_from) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The property to set initial value of. Use values from + /// {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Real} The initial value to mix from. Default value is 0.0. + From = _from; + + /// @var {Real} The initial value to mix to. Default value is the same as + /// {@link BBMOD_MixRealModule.From}. + To = _to; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + _emitter.Particles[# Property, _particleIndex] = lerp(From, To, random(1.0)); + } + }; +} diff --git a/scripts/BBMOD_MixRealOverTimeModule/BBMOD_MixRealOverTimeModule.yy b/scripts/BBMOD_MixRealOverTimeModule/BBMOD_MixRealOverTimeModule.yy new file mode 100644 index 000000000..67c39d366 --- /dev/null +++ b/scripts/BBMOD_MixRealOverTimeModule/BBMOD_MixRealOverTimeModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixRealOverTimeModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyOverTime", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyOverTime.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixRealOverTimeModule/bbmod_mixrealovertimemodule.gml b/scripts/BBMOD_MixRealOverTimeModule/bbmod_mixrealovertimemodule.gml new file mode 100644 index 000000000..8c386cfd4 --- /dev/null +++ b/scripts/BBMOD_MixRealOverTimeModule/bbmod_mixrealovertimemodule.gml @@ -0,0 +1,61 @@ +/// @func BBMOD_MixRealOverTimeModule([_property[, _from[, _to[, _duration]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes value of particles' +/// property between two values based on their time alive. +/// +/// @param {Real} [_property] The property to set initial value of. Use values +/// from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Real} [_from] The value when the particle has full health. +/// Defaults to 0.0. +/// @param {Real} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// @param {Real} [_duration] How long in seconds it takes to mix between the +/// two values. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixRealOverTimeModule( + _property=undefined, + _from=0.0, + _to=_from, + _duration=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The property to set initial value of. Use values from + /// {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Real} The value when the particle has full health. Default value + /// is 0.0. + From = _from; + + /// @var {Real} The value when the particle has no health left. Default value + /// is the same as {@link BBMOD_MixRealOverTimeModule.From}. + To = _to; + + /// @var {Real} How long in seconds it takes to mix between the two values. + /// Default value is 1.0. + Duration = _duration; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _from = From; + var _particles = _emitter.Particles; + var _duration = Duration; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.TimeAlive, _particleIndex] / _duration, 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_to, _from, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixSpeedModule/BBMOD_MixSpeedModule.yy b/scripts/BBMOD_MixSpeedModule/BBMOD_MixSpeedModule.yy new file mode 100644 index 000000000..210ad1641 --- /dev/null +++ b/scripts/BBMOD_MixSpeedModule/BBMOD_MixSpeedModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixSpeedModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Velocity", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Velocity.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixSpeedModule/bbmod_mixspeedmodule.gml b/scripts/BBMOD_MixSpeedModule/bbmod_mixspeedmodule.gml new file mode 100644 index 000000000..ac94b695b --- /dev/null +++ b/scripts/BBMOD_MixSpeedModule/bbmod_mixspeedmodule.gml @@ -0,0 +1,37 @@ +/// @func BBMOD_MixSpeedModule([_from[, _to]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that randomly sets initial magnitude of particles' +/// velocity vector. +/// +/// @param {Real} [_from] The minimum velocity vector magnitude. Defaults to 1.0. +/// @param {Real} [_to] The maximum velocity vector magnitude. Defaults to `_from`. +/// +/// @see BBMOD_EParticle.VelocityX +/// @see BBMOD_EParticle.VelocityY +/// @see BBMOD_EParticle.VelocityZ +function BBMOD_MixSpeedModule(_from=1.0, _to=_from) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The minimum velocity vector magnitude. Default value is 1.0. + From = _from; + + /// @var {Real} The maximum velocity vector magnitude. Default value is the + /// same as {@link BBMOD_MixSpeedModule.From}. + To = _to; + + static on_particle_start = function (_emitter, _particleIndex) { + var _particles = _emitter.Particles; + var _velocityX = _particles[# BBMOD_EParticle.VelocityX, _particleIndex]; + var _velocityY = _particles[# BBMOD_EParticle.VelocityY, _particleIndex]; + var _velocityZ = _particles[# BBMOD_EParticle.VelocityZ, _particleIndex]; + var _norm = sqrt((_velocityX * _velocityX) + (_velocityY * _velocityY) + (_velocityZ * _velocityZ)); + var _factor = lerp(From, To, random(1.0)) / max(_norm, 0.001); + _particles[# BBMOD_EParticle.VelocityX, _particleIndex] = _velocityX * _factor; + _particles[# BBMOD_EParticle.VelocityY, _particleIndex] = _velocityY * _factor; + _particles[# BBMOD_EParticle.VelocityZ, _particleIndex] = _velocityZ * _factor; + }; +} diff --git a/scripts/BBMOD_MixVec2FromHealthModule/BBMOD_MixVec2FromHealthModule.yy b/scripts/BBMOD_MixVec2FromHealthModule/BBMOD_MixVec2FromHealthModule.yy new file mode 100644 index 000000000..39d75178e --- /dev/null +++ b/scripts/BBMOD_MixVec2FromHealthModule/BBMOD_MixVec2FromHealthModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec2FromHealthModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromHealth", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromHealth.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec2FromHealthModule/bbmod_mixvec2fromhealthmodule.gml b/scripts/BBMOD_MixVec2FromHealthModule/bbmod_mixvec2fromhealthmodule.gml new file mode 100644 index 000000000..66b105d93 --- /dev/null +++ b/scripts/BBMOD_MixVec2FromHealthModule/bbmod_mixvec2fromhealthmodule.gml @@ -0,0 +1,59 @@ +/// @func BBMOD_MixVec2FromHealthModule([_property[, _from[, _to]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes values of particles' two +/// consecutive properties between two values based on their remaining health. +/// +/// @param {Real} [_property] The first of the two consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec2} [_from] The value when the particle has full health. +/// Defaults to `(0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec2} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec2FromHealthModule( + _property=undefined, + _from=new BBMOD_Vec2(), + _to=_from.Clone() +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the two consecutive properties. Use values from + /// {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec2} The value when the particle has full health. + /// Default value is `(0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec2} The value when the particle has no health left. + /// Default value is the same as {@link BBMOD_MixVec2FromHealthModule.From}. + To = _to; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _toX = _to.X; + var _toY = _to.Y; + var _from = From; + var _fromX = _from.X; + var _fromY = _from.Y; + var _particles = _emitter.Particles; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.HealthLeft, _particleIndex] + / _particles[# BBMOD_EParticle.Health, _particleIndex], 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_toX, _fromX, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_toY, _fromY, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixVec2FromSpeedModule/BBMOD_MixVec2FromSpeedModule.yy b/scripts/BBMOD_MixVec2FromSpeedModule/BBMOD_MixVec2FromSpeedModule.yy new file mode 100644 index 000000000..8b0bd051d --- /dev/null +++ b/scripts/BBMOD_MixVec2FromSpeedModule/BBMOD_MixVec2FromSpeedModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec2FromSpeedModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromSpeed", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromSpeed.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec2FromSpeedModule/bbmod_mixvec2fromspeedmodule.gml b/scripts/BBMOD_MixVec2FromSpeedModule/bbmod_mixvec2fromspeedmodule.gml new file mode 100644 index 000000000..80aeb7f16 --- /dev/null +++ b/scripts/BBMOD_MixVec2FromSpeedModule/bbmod_mixvec2fromspeedmodule.gml @@ -0,0 +1,80 @@ +/// @func BBMOD_MixVec2FromSpeedModule([_property[, _from[, _to[, _min[, _max]]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes values of particles' two +/// consecutive properties between two values based on the magnitude of their +/// velocity vector. +/// +/// @param {Real} [_property] The first of the two consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec2} [_from] The value when the particle has full health. +/// Defaults to `(0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec2} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// @param {Real} [_min] If the particles' speed is less than this, then the +/// property is equal to `_from`. Defaults to 0.0. +/// @param {Real} [_max] If the particles' speed is greater than this, then the +/// property is equal to `_to`. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec2FromSpeedModule( + _property=undefined, + _from=new BBMOD_Vec2(), + _to=_from.Clone(), + _min=0.0, + _max=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the two consecutive properties. Use values from + /// {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec2} The value when the particle has full health. + /// Default value is `(0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec2} The value when the particle has no health left. + /// Default value is the same as {@link BBMOD_MixVec2FromSpeedModule.From}. + To = _to; + + /// @var {Real} If the particles' speed is less than this, then the property + /// is equal to {@link BBMOD_MixVec2FromSpeedModule.From}. Default value is 0.0. + Min = _min; + + /// @var {Real} If the particles' speed is greater than this, then the property + /// is equal to {@link BBMOD_MixVec2FromSpeedModule.To}. Default value is 1.0. + Max = _max; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _toX = _to.X; + var _toY = _to.Y; + var _from = From; + var _fromX = _from.X; + var _fromY = _from.Y; + var _particles = _emitter.Particles; + var _min = Min; + var _max = Max; + var _div = _max - _min; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _velX = _particles[# BBMOD_EParticle.VelocityX, _particleIndex]; + var _velY = _particles[# BBMOD_EParticle.VelocityY, _particleIndex]; + var _velZ = _particles[# BBMOD_EParticle.VelocityZ, _particleIndex]; + var _speed = sqrt((_velX * _velX) + (_velY + _velY) + (_velZ * _velZ)); + var _factor = clamp((_speed - _min) / _div, 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_toX, _fromX, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_toY, _fromY, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixVec2Module/BBMOD_MixVec2Module.yy b/scripts/BBMOD_MixVec2Module/BBMOD_MixVec2Module.yy new file mode 100644 index 000000000..0b1e5141b --- /dev/null +++ b/scripts/BBMOD_MixVec2Module/BBMOD_MixVec2Module.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec2Module", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec2Module/bbmod_mixvec2module.gml b/scripts/BBMOD_MixVec2Module/bbmod_mixvec2module.gml new file mode 100644 index 000000000..ea9629f82 --- /dev/null +++ b/scripts/BBMOD_MixVec2Module/bbmod_mixvec2module.gml @@ -0,0 +1,53 @@ +/// @func BBMOD_MixVec2Module([_property[, _from[, _to[, _separate]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that randomly mixes initial values of +/// particles' two consecutive properties between two values when they are +/// spawned. +/// +/// @param {Real} [_property] The first of the two consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec2} [_from] The value to mix from. Defaults to +/// `(0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec2} [_to] The value to mix to. Defaults to `_from`. +/// @param {Bool} [_separate] If `true`, then each component is mixed independently +/// on other components, otherwise all components are mixed using the same factor. +/// Defaults to `true`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec2Module( + _property=undefined, + _from=new BBMOD_Vec2(), + _to=_from.Clone(), + _separate=true +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the two consecutive properties. Use values from + /// {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec2} The initial value to mix from. Default value is + /// `(0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec2} The initial value to mix to. Default value is the + /// same as {@link BBMOD_MixVec2Module.From}. + To = _to; + + /// @var {Bool} If `true`, then each component is mixed independently on other + /// components. Default value is `true`. + Separate = _separate; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + var _separate = Separate; + var _factor = random(1.0); + _emitter.Particles[# Property, _particleIndex] = lerp(From.X, To.X, _factor); + _emitter.Particles[# Property + 1, _particleIndex] = lerp(From.Y, To.Y, _separate ? random(1.0) : _factor); + } + }; +} diff --git a/scripts/BBMOD_MixVec2OverTimeModule/BBMOD_MixVec2OverTimeModule.yy b/scripts/BBMOD_MixVec2OverTimeModule/BBMOD_MixVec2OverTimeModule.yy new file mode 100644 index 000000000..73699e8ea --- /dev/null +++ b/scripts/BBMOD_MixVec2OverTimeModule/BBMOD_MixVec2OverTimeModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec2OverTimeModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyOverTime", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyOverTime.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec2OverTimeModule/bbmod_mixvec2overtimemodule.gml b/scripts/BBMOD_MixVec2OverTimeModule/bbmod_mixvec2overtimemodule.gml new file mode 100644 index 000000000..a65e6c974 --- /dev/null +++ b/scripts/BBMOD_MixVec2OverTimeModule/bbmod_mixvec2overtimemodule.gml @@ -0,0 +1,66 @@ +/// @func BBMOD_MixVec2OverTimeModule([_property[, _from[, _to[, _duration]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes values of particles' two +/// consecutive properties between two values based on their time alive. +/// +/// @param {Real} [_property] The first of the two consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec2} [_from] The value when the particle has full health. +/// Defaults to `(0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec2} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// @param {Real} [_duration] How long in seconds it takes to mix between the +/// two values. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec2OverTimeModule( + _property=undefined, + _from=new BBMOD_Vec2(), + _to=_from.Clone(), + _duration=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the two consecutive properties. Use values from + /// {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec2} The value when the particle has full health. + /// Default value is `(0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec2} The value when the particle has no health left. + /// Default value is the same as {@link BBMOD_MixVec2OverTimeModule.From}. + To = _to; + + /// @var {Real} How long in seconds it takes to mix between the two values. + /// Default value is 1.0. + Duration = _duration; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _toX = _to.X; + var _toY = _to.Y; + var _from = From; + var _fromX = _from.X; + var _fromY = _from.Y; + var _particles = _emitter.Particles; + var _duration = Duration; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.TimeAlive, _particleIndex] / _duration, 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_toX, _fromX, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_toY, _fromY, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixVec3FromHealthModule/BBMOD_MixVec3FromHealthModule.yy b/scripts/BBMOD_MixVec3FromHealthModule/BBMOD_MixVec3FromHealthModule.yy new file mode 100644 index 000000000..2c77091b2 --- /dev/null +++ b/scripts/BBMOD_MixVec3FromHealthModule/BBMOD_MixVec3FromHealthModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec3FromHealthModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromHealth", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromHealth.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec3FromHealthModule/bbmod_mixvec3fromhealthmodule.gml b/scripts/BBMOD_MixVec3FromHealthModule/bbmod_mixvec3fromhealthmodule.gml new file mode 100644 index 000000000..a9f97876c --- /dev/null +++ b/scripts/BBMOD_MixVec3FromHealthModule/bbmod_mixvec3fromhealthmodule.gml @@ -0,0 +1,62 @@ +/// @func BBMOD_MixVec3FromHealthModule([_property[, _from[, _to]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes values of particles' three +/// consecutive properties between two values based on their remaining health. +/// +/// @param {Real} [_property] The first of the three consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec3} [_from] The value when the particle has full health. +/// Defaults to `(0.0, 0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec3} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec3FromHealthModule( + _property=undefined, + _from=new BBMOD_Vec3(), + _to=_from.Clone() +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {REal} The first of the three consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec3} The value when the particle has full health. Default + /// value is `(0.0, 0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec3} The value when the particle has no health left. Default + /// value is the same as {@link BBMOD_MixVec3FromHealthModule.From}. + To = _to; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _toX = _to.X; + var _toY = _to.Y; + var _toZ = _to.Z; + var _from = From; + var _fromX = _from.X; + var _fromY = _from.Y; + var _fromZ = _from.Z; + var _particles = _emitter.Particles; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.HealthLeft, _particleIndex] + / _particles[# BBMOD_EParticle.Health, _particleIndex], 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_toX, _fromX, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_toY, _fromY, _factor); + _particles[# _property + 2, _particleIndex] = lerp(_toZ, _fromZ, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixVec3FromSpeedModule/BBMOD_MixVec3FromSpeedModule.yy b/scripts/BBMOD_MixVec3FromSpeedModule/BBMOD_MixVec3FromSpeedModule.yy new file mode 100644 index 000000000..c5e3e41b1 --- /dev/null +++ b/scripts/BBMOD_MixVec3FromSpeedModule/BBMOD_MixVec3FromSpeedModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec3FromSpeedModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromSpeed", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromSpeed.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec3FromSpeedModule/bbmod_mixvec3fromspeedmodule.gml b/scripts/BBMOD_MixVec3FromSpeedModule/bbmod_mixvec3fromspeedmodule.gml new file mode 100644 index 000000000..b988b1061 --- /dev/null +++ b/scripts/BBMOD_MixVec3FromSpeedModule/bbmod_mixvec3fromspeedmodule.gml @@ -0,0 +1,83 @@ +/// @func BBMOD_MixVec3FromSpeedModule([_property[, _from[, _to[, _min[, _max]]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes values of particles' three +/// consecutive properties between two values based on the magnitude of their +/// velocity vector. +/// +/// @param {Real} [_property] The first of the three consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec3} [_from] The value when the particle has full health. +/// Defaults to `(0.0, 0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec3} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// @param {Real} [_min] If the particles' speed is less than this, then the +/// property is equal to `_from`. Defaults to 0.0. +/// @param {Real} [_max] If the particles' speed is greater than this, then the +/// property is equal to `_to`. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec3FromSpeedModule( + _property=undefined, + _from=new BBMOD_Vec3(), + _to=_from.Clone(), + _min=0.0, + _max=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the three consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec3} The value when the particle has full health. Default + /// value is `(0.0, 0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec3} The value when the particle has no health left. Default + /// value is the same as {@link BBMOD_MixVec3FromSpeedModule.From}. + To = _to; + + /// @var {Real} If the particles' speed is less than this, then the property + /// is equal to {@link BBMOD_MixVec3FromSpeedModule.From}. Default value is 0.0. + Min = _min; + + /// @var {Real} If the particles' speed is greater than this, then the property + /// is equal to {@link BBMOD_MixVec3FromSpeedModule.To}. Default value is 1.0. + Max = _max; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _toX = _to.X; + var _toY = _to.Y; + var _toZ = _to.Z; + var _from = From; + var _fromX = _from.X; + var _fromY = _from.Y; + var _fromZ = _from.Z; + var _particles = _emitter.Particles; + var _min = Min; + var _max = Max; + var _div = _max - _min; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _velX = _particles[# BBMOD_EParticle.VelocityX, _particleIndex]; + var _velY = _particles[# BBMOD_EParticle.VelocityY, _particleIndex]; + var _velZ = _particles[# BBMOD_EParticle.VelocityZ, _particleIndex]; + var _speed = sqrt((_velX * _velX) + (_velY + _velY) + (_velZ * _velZ)); + var _factor = clamp((_speed - _min) / _div, 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_toX, _fromX, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_toY, _fromY, _factor); + _particles[# _property + 2, _particleIndex] = lerp(_toZ, _fromZ, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixVec3Module/BBMOD_MixVec3Module.yy b/scripts/BBMOD_MixVec3Module/BBMOD_MixVec3Module.yy new file mode 100644 index 000000000..d9c18605d --- /dev/null +++ b/scripts/BBMOD_MixVec3Module/BBMOD_MixVec3Module.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec3Module", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec3Module/bbmod_mixvec3module.gml b/scripts/BBMOD_MixVec3Module/bbmod_mixvec3module.gml new file mode 100644 index 000000000..474a5cfb2 --- /dev/null +++ b/scripts/BBMOD_MixVec3Module/bbmod_mixvec3module.gml @@ -0,0 +1,57 @@ +/// @func BBMOD_MixVec3Module([_property[, _from[, _to[, _separate]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that randomly mixes initial values of +/// particles' three consecutive properties between two values when they are +/// spawned. +/// +/// @param {Real} [_property] The first of the three consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec3} [_from] The value to mix from. Defaults to +/// `(0.0, 0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec3} [_to] The value to mix to. Defaults to `_from`. +/// @param {Bool} [_separate] If `true`, then each component is mixed +/// independently on other components, otherwise all components are mixed using +/// the same factor. Defaults to `true`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec3Module( + _property=undefined, + _from=new BBMOD_Vec3(), + _to=_from.Clone(), + _separate=true +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the three consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec3} The initial value to mix from. Default value is + /// `(0.0, 0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec3} The initial value to mix to. Default value is + /// the same as {@link BBMOD_MixVec3Module.From}. + To = _to; + + /// @var {Bool} If `true`, then each component is mixed independently on + /// other components. Default value is `true`. + Separate = _separate; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + var _separate = Separate; + var _factor = random(1.0); + _emitter.Particles[# Property, _particleIndex] = + lerp(From.X, To.X, _factor); + _emitter.Particles[# Property + 1, _particleIndex] = + lerp(From.Y, To.Y, _separate ? random(1.0) : _factor); + _emitter.Particles[# Property + 2, _particleIndex] = + lerp(From.Z, To.Z, _separate ? random(1.0) : _factor); + } + }; +} diff --git a/scripts/BBMOD_MixVec3OverTimeModule/BBMOD_MixVec3OverTimeModule.yy b/scripts/BBMOD_MixVec3OverTimeModule/BBMOD_MixVec3OverTimeModule.yy new file mode 100644 index 000000000..24e4ab98c --- /dev/null +++ b/scripts/BBMOD_MixVec3OverTimeModule/BBMOD_MixVec3OverTimeModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec3OverTimeModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyOverTime", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyOverTime.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec3OverTimeModule/bbmod_mixvec3overtimemodule.gml b/scripts/BBMOD_MixVec3OverTimeModule/bbmod_mixvec3overtimemodule.gml new file mode 100644 index 000000000..d4aea9502 --- /dev/null +++ b/scripts/BBMOD_MixVec3OverTimeModule/bbmod_mixvec3overtimemodule.gml @@ -0,0 +1,69 @@ +/// @func BBMOD_MixVec3OverTimeModule([_property[, _from[, _to[, _duration]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes values of particles' three +/// consecutive properties between two values based on their time alive. +/// +/// @param {Real} [_property] The first of the three consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec3} [_from] The value when the particle has full health. +/// Defaults to `(0.0, 0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec3} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// @param {Real} [_duration] How long in seconds it takes to mix between the +/// two values. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec3OverTimeModule( + _property=undefined, + _from=new BBMOD_Vec3(), + _to=_from.Clone(), + _duration=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the three consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec3} The value when the particle has full health. Default + /// value is `(0.0, 0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec3} The value when the particle has no health left. Default + /// value is the same as {@link BBMOD_MixVec3OverTimeModule.From}. + To = _to; + + /// @var {Real} How long in seconds it takes to mix between the two values. + /// Default value is 1.0. + Duration = _duration; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _toX = _to.X; + var _toY = _to.Y; + var _toZ = _to.Z; + var _from = From; + var _fromX = _from.X; + var _fromY = _from.Y; + var _fromZ = _from.Z; + var _particles = _emitter.Particles; + var _duration = Duration; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.TimeAlive, _particleIndex] / _duration, 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_toX, _fromX, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_toY, _fromY, _factor); + _particles[# _property + 2, _particleIndex] = lerp(_toZ, _fromZ, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixVec4FromHealthModule/BBMOD_MixVec4FromHealthModule.yy b/scripts/BBMOD_MixVec4FromHealthModule/BBMOD_MixVec4FromHealthModule.yy new file mode 100644 index 000000000..56c34ab6f --- /dev/null +++ b/scripts/BBMOD_MixVec4FromHealthModule/BBMOD_MixVec4FromHealthModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec4FromHealthModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromHealth", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromHealth.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec4FromHealthModule/bbmod_mixvec4fromhealthmodule.gml b/scripts/BBMOD_MixVec4FromHealthModule/bbmod_mixvec4fromhealthmodule.gml new file mode 100644 index 000000000..e004aeffc --- /dev/null +++ b/scripts/BBMOD_MixVec4FromHealthModule/bbmod_mixvec4fromhealthmodule.gml @@ -0,0 +1,65 @@ +/// @func BBMOD_MixVec4FromHealthModule([_property[, _from[, _to]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes values of particles' four +/// consecutive properties between two values based on their remaining health. +/// +/// @param {Real} [_property] The first of the four consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec4} [_from] The value when the particle has full health. +/// Defaults to `(0.0, 0.0, 0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec4} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec4FromHealthModule( + _property=undefined, + _from=new BBMOD_Vec4(), + _to=_from.Clone() +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec4} The value when the particle has full health. + /// Default value is `(0.0, 0.0, 0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec4} The value when the particle has no health left. + /// Default value is the same as {@link BBMOD_MixVec4FromHealthModule.From}. + To = _to; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _toX = _to.X; + var _toY = _to.Y; + var _toZ = _to.Z; + var _toW = _to.W; + var _from = From; + var _fromX = _from.X; + var _fromY = _from.Y; + var _fromZ = _from.Z; + var _fromW = _from.W; + var _particles = _emitter.Particles; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.HealthLeft, _particleIndex] + / _particles[# BBMOD_EParticle.Health, _particleIndex], 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_toX, _fromX, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_toY, _fromY, _factor); + _particles[# _property + 2, _particleIndex] = lerp(_toZ, _fromZ, _factor); + _particles[# _property + 3, _particleIndex] = lerp(_toW, _fromW, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixVec4FromSpeedModule/BBMOD_MixVec4FromSpeedModule.yy b/scripts/BBMOD_MixVec4FromSpeedModule/BBMOD_MixVec4FromSpeedModule.yy new file mode 100644 index 000000000..003ff7fb8 --- /dev/null +++ b/scripts/BBMOD_MixVec4FromSpeedModule/BBMOD_MixVec4FromSpeedModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec4FromSpeedModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyFromSpeed", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyFromSpeed.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec4FromSpeedModule/bbmod_mixvec4fromspeedmodule.gml b/scripts/BBMOD_MixVec4FromSpeedModule/bbmod_mixvec4fromspeedmodule.gml new file mode 100644 index 000000000..41fd56272 --- /dev/null +++ b/scripts/BBMOD_MixVec4FromSpeedModule/bbmod_mixvec4fromspeedmodule.gml @@ -0,0 +1,86 @@ +/// @func BBMOD_MixVec4FromSpeedModule([_property[, _from[, _to[, _min[, _max]]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes values of particles' four +/// consecutive properties between two values based on the magnitude of their +/// velocity vector. +/// +/// @param {Real} [_property] The first of the four consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec4} [_from] The value when the particle has full health. +/// Defaults to `(0.0, 0.0, 0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec4} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// @param {Real} [_min] If the particles' speed is less than this, then the +/// property is equal to `_from`. Defaults to 0.0. +/// @param {Real} [_max] If the particles' speed is greater than this, then the +/// property is equal to `_to`. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec4FromSpeedModule( + _property=undefined, + _from=new BBMOD_Vec4(), + _to=_from.Clone(), + _min=0.0, + _max=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec4} The value when the particle has full health. + /// Default value is `(0.0, 0.0, 0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec4} The value when the particle has no health left. + /// Default value is the same as {@link BBMOD_MixVec4FromSpeedModule.From}. + To = _to; + + /// @var {Real} If the particles' speed is less than this, then the property + /// is equal to {@link BBMOD_MixVec4FromSpeedModule.From}. Default value is 0.0. + Min = _min; + + /// @var {Real} If the particles' speed is greater than this, then the property + /// is equal to {@link BBMOD_MixVec4FromSpeedModule.To}. Default value is 1.0. + Max = _max; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _toX = _to.X; + var _toY = _to.Y; + var _toZ = _to.Z; + var _toW = _to.W; + var _from = From; + var _fromX = _from.X; + var _fromY = _from.Y; + var _fromZ = _from.Z; + var _fromW = _from.W; + var _particles = _emitter.Particles; + var _min = Min; + var _max = Max; + var _div = _max - _min; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _velX = _particles[# BBMOD_EParticle.VelocityX, _particleIndex]; + var _velY = _particles[# BBMOD_EParticle.VelocityY, _particleIndex]; + var _velZ = _particles[# BBMOD_EParticle.VelocityZ, _particleIndex]; + var _speed = sqrt((_velX * _velX) + (_velY + _velY) + (_velZ * _velZ)); + var _factor = clamp((_speed - _min) / _div, 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_toX, _fromX, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_toY, _fromY, _factor); + _particles[# _property + 2, _particleIndex] = lerp(_toZ, _fromZ, _factor); + _particles[# _property + 3, _particleIndex] = lerp(_toW, _fromW, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_MixVec4Module/BBMOD_MixVec4Module.yy b/scripts/BBMOD_MixVec4Module/BBMOD_MixVec4Module.yy new file mode 100644 index 000000000..9179c8eee --- /dev/null +++ b/scripts/BBMOD_MixVec4Module/BBMOD_MixVec4Module.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec4Module", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec4Module/bbmod_mixvec4module.gml b/scripts/BBMOD_MixVec4Module/bbmod_mixvec4module.gml new file mode 100644 index 000000000..668e8e31f --- /dev/null +++ b/scripts/BBMOD_MixVec4Module/bbmod_mixvec4module.gml @@ -0,0 +1,55 @@ +/// @func BBMOD_MixVec4Module([_property[, _from[, _to[, _separate]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that randomly mixes initial values of +/// particles' four consecutive properties between two values when they are +/// spawned. +/// +/// @param {Real} [_property] The first of the four consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec4} [_from] The value to mix from. Defaults to +/// `(0.0, 0.0, 0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec4} [_to] The value to mix to. Defaults to `_from`. +/// @param {Bool} [_separate] If `true`, then each component is mixed independently +/// on other components, otherwise all components are mixed using the same factor. +/// Defaults to `true`. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec4Module( + _property=undefined, + _from=new BBMOD_Vec4(), + _to=_from.Clone(), + _separate=true +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec4} The initial value to mix from. Default value is + /// `(0.0, 0.0, 0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec4} The initial value to mix to. Default value is the + /// same as {@link BBMOD_MixVec4Module.From}. + To = _to; + + /// @var {Bool} If `true`, then each component is mixed independently on other + /// components. Default value is `true`. + Separate = _separate; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + var _separate = Separate; + var _factor = random(1.0); + _emitter.Particles[# Property, _particleIndex] = lerp(From.X, To.X, _factor); + _emitter.Particles[# Property + 1, _particleIndex] = lerp(From.Y, To.Y, _separate ? random(1.0) : _factor); + _emitter.Particles[# Property + 2, _particleIndex] = lerp(From.Z, To.Z, _separate ? random(1.0) : _factor); + _emitter.Particles[# Property + 3, _particleIndex] = lerp(From.W, To.W, _separate ? random(1.0) : _factor); + } + }; +} diff --git a/scripts/BBMOD_MixVec4OverTimeModule/BBMOD_MixVec4OverTimeModule.yy b/scripts/BBMOD_MixVec4OverTimeModule/BBMOD_MixVec4OverTimeModule.yy new file mode 100644 index 000000000..1f2ca4fa0 --- /dev/null +++ b/scripts/BBMOD_MixVec4OverTimeModule/BBMOD_MixVec4OverTimeModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_MixVec4OverTimeModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "MixPropertyOverTime", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/MixPropertyOverTime.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_MixVec4OverTimeModule/bbmod_mixvec4overtimemodule.gml b/scripts/BBMOD_MixVec4OverTimeModule/bbmod_mixvec4overtimemodule.gml new file mode 100644 index 000000000..7fe863ba1 --- /dev/null +++ b/scripts/BBMOD_MixVec4OverTimeModule/bbmod_mixvec4overtimemodule.gml @@ -0,0 +1,72 @@ +/// @func BBMOD_MixVec4OverTimeModule([_property[, _from[, _to[, _duration]]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that mixes values of particles' four +/// consecutive properties between two values based on their time alive. +/// +/// @param {Real} [_property] The first of the four consecutive properties. Use +/// values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec4} [_from] The value when the particle has full health. +/// Defaults to `(0.0, 0.0, 0.0, 0.0)`. +/// @param {Struct.BBMOD_Vec4} [_to] The value when the particle has no health left. +/// Defaults to `_from`. +/// @param {Real} [_duration] How long in seconds it takes to mix between the +/// two values. Defaults to 1.0. +/// +/// @see BBMOD_EParticle +function BBMOD_MixVec4OverTimeModule( + _property=undefined, + _from=new BBMOD_Vec4(), + _to=_from.Clone(), + _duration=1.0 +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four consecutive properties. Use values + /// from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec4} The value when the particle has full health. + /// Default value is `(0.0, 0.0, 0.0, 0.0)`. + From = _from; + + /// @var {Struct.BBMOD_Vec4} The value when the particle has no health left. + /// Default value is the same as {@link BBMOD_MixVec4OverTimeModule.From}. + To = _to; + + /// @var {Real} How long in seconds it takes to mix between the two values. + /// Default value is 1.0. + Duration = _duration; + + static on_update = function (_emitter, _deltaTime) { + var _property = Property; + if (_property != undefined) + { + var _to = To; + var _toX = _to.X; + var _toY = _to.Y; + var _toZ = _to.Z; + var _toW = _to.W; + var _from = From; + var _fromX = _from.X; + var _fromY = _from.Y; + var _fromZ = _from.Z; + var _fromW = _from.W; + var _particles = _emitter.Particles; + var _duration = Duration; + + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _factor = clamp(_particles[# BBMOD_EParticle.TimeAlive, _particleIndex] / _duration, 0.0, 1.0); + _particles[# _property, _particleIndex] = lerp(_toX, _fromX, _factor); + _particles[# _property + 1, _particleIndex] = lerp(_toY, _fromY, _factor); + _particles[# _property + 2, _particleIndex] = lerp(_toZ, _fromZ, _factor); + _particles[# _property + 3, _particleIndex] = lerp(_toW, _fromW, _factor); + ++_particleIndex; + } + } + }; +} diff --git a/scripts/BBMOD_Model/BBMOD_Model.gml b/scripts/BBMOD_Model/BBMOD_Model.gml new file mode 100644 index 000000000..9b01fb708 --- /dev/null +++ b/scripts/BBMOD_Model/BBMOD_Model.gml @@ -0,0 +1,645 @@ +/// @func BBMOD_Model([_file[, _sha1]]) +/// +/// @extends BBMOD_Resource +/// +/// @implements {BBMOD_IRenderable} +/// +/// @desc A model. +/// +/// @param {String} [_file] The "*.bbmod" model file to load or `undefined`. +/// Defaults to `undefined`. +/// @param {String} [_sha1] Expected SHA1 of the file. If the actual +/// one does not match with this, then the model will not be loaded. Use +/// `undefined` if you do not want to check the SHA1 of the file. Defaults to +/// `undefined`. +/// +/// @example +/// ```gml +/// try +/// { +/// modCharacter = new BBMOD_Model("Character.bbmod"); +/// } +/// catch (_error) +/// { +/// // The model failed to load! +/// } +/// ``` +/// +/// @throws {BBMOD_Exception} When the model fails to load. +function BBMOD_Model(_file=undefined, _sha1=undefined) + : BBMOD_Resource() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + implement(BBMOD_IRenderable); + + static Resource_destroy = destroy; + + /// @var {Real} The major version of the model file. + VersionMajor = BBMOD_VERSION_MAJOR; + + /// @var {Real} The minor version of the model file. + VersionMinor = BBMOD_VERSION_MINOR; + + /// @var {Struct.BBMOD_VertexFormat} The vertex format of the model. + /// @see BBMOD_VertexFormat + /// @obsolete Since version 3.2 of the BBMOD file format each mesh has + /// its own vertex format! + /// @see BBMOD_Mesh.VertexFormat + /// @readonly + VertexFormat = undefined; + + /// @var {Array} Array of meshes. + /// @readonly + Meshes = []; + + /// @var {Real} Number of nodes. + /// @readonly + NodeCount = 0; + + /// @var {Struct.BBMOD_Node} The root node. + /// @see BBMOD_Node + /// @readonly + RootNode = undefined; + + /// @var {Real} Number of bones. + /// @readonly + BoneCount = 0; + + /// @var {Array} An array of bone offset dual quaternions. + /// @private + __offsetArray = []; + + /// @var {Real} Number of materials that the model uses. + /// @see BBMOD_BaseMaterial + /// @readonly + MaterialCount = 0; + + /// @var {Array} An array of material names. + /// @see BBMOD_Model.Materials + /// @see BBMOD_Model.get_material + /// @see BBMOD_Model.set_material + /// @readonly + MaterialNames = []; + + /// @var {Array} An array of materials. Each entry + /// defaults to {@link BBMOD_MATERIAL_DEFAULT}. + /// @see BBMOD_Model.MaterialNames + /// @see BBMOD_Model.get_material + /// @see BBMOD_Model.set_material + /// @see BBMOD_BaseMaterial + Materials = []; + + /// @var {Bool} If `true` then the model is frozen. + /// @readonly + /// @see BBMOD_Model.freeze + Frozen = false; + + /// @func copy(_dest) + /// + /// @desc Copies model data into another model. + /// + /// @param {Struct.BBMOD_Model} _dest The model to copy data to. + /// + /// @return {Struct.BBMOD_Model} Returns `self`. + static copy = function (_dest) { + _dest.IsLoaded = IsLoaded; + _dest.Path = Path; + + _dest.VersionMajor = VersionMajor; + _dest.VersionMinor = VersionMinor; + _dest.VertexFormat = VertexFormat; + + for (var i = array_length(_dest.Meshes) - 1; i >= 0; --i) + { + _dest.Meshes[i].destroy(); + } + + var _meshCount = array_length(Meshes); + _dest.Meshes = array_create(_meshCount); + + for (var i = 0; i < _meshCount; ++i) + { + var _meshClone = Meshes[i].clone(); + _meshClone.Model = _dest; + _dest.Meshes[@ i] = _meshClone; + } + + _dest.NodeCount = NodeCount; + + if (_dest.RootNode) + { + _dest.RootNode.destroy(); + } + + if (RootNode) + { + _dest.RootNode = RootNode.clone(); + _dest.__pass_self_to_nodes(); + } + else + { + _dest.RootNode = undefined; + } + + _dest.BoneCount = BoneCount; + _dest.__offsetArray = bbmod_array_clone(__offsetArray); + _dest.MaterialCount = MaterialCount; + _dest.MaterialNames = bbmod_array_clone(MaterialNames); + _dest.Materials = bbmod_array_clone(Materials); + _dest.Frozen = Frozen; + + return self; + }; + + /// @func clone() + /// + /// @desc Creates a clone of the model. + /// + /// @return {Struct.BBMOD_Model} The created clone. + static clone = function () { + var _clone = new BBMOD_Model(); + copy(_clone); + return _clone; + }; + + /// @func __pass_self_to_nodes([_node]) + /// + /// @desc + /// + /// @param {Struct.BBMOD_Node} [_node] + /// + /// @private + static __pass_self_to_nodes = function (_node=undefined) { + _node ??= RootNode; + _node.Model = self; + for (var i = array_length(_node.Children) - 1; i >= 0; --i) + { + __pass_self_to_nodes(_node.Children[i]); + } + return self; + }; + + /// @func from_buffer(_buffer) + /// + /// @desc Loads model data from a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to load the data from. + /// + /// @return {Struct.BBMOD_Model} Returns `self`. + /// + /// @throws {BBMOD_Exception} If loading fails. + static from_buffer = function (_buffer) { + var _hasMinorVersion = false; + + var _type = buffer_read(_buffer, buffer_string); + if (_type == "bbmod") + { + } + else if (_type == "BBMOD") + { + _hasMinorVersion = true; + } + else + { + throw new BBMOD_Exception("Buffer does not contain a BBMOD!"); + } + + VersionMajor = buffer_read(_buffer, buffer_u8); + if (VersionMajor != BBMOD_VERSION_MAJOR) + { + throw new BBMOD_Exception( + "Invalid BBMOD major version " + string(VersionMajor) + "!"); + } + + if (_hasMinorVersion) + { + VersionMinor = buffer_read(_buffer, buffer_u8); + if (VersionMinor > BBMOD_VERSION_MINOR) + { + throw new BBMOD_Exception( + "Invalid BBMOD minor version " + string(VersionMinor) + "!"); + } + } + else + { + VersionMinor = 0; + } + + // Vertex format + if (VersionMinor < 2) + { + VertexFormat = __bbmod_vertex_format_load(_buffer, VersionMinor); + } + + // Meshes + var _meshCount = buffer_read(_buffer, buffer_u32); + Meshes = array_create(_meshCount, undefined); + + var i = 0; + repeat (_meshCount) + { + Meshes[@ i++] = new BBMOD_Mesh(VertexFormat, self).from_buffer(_buffer); + } + + // Node count and root node + NodeCount = buffer_read(_buffer, buffer_u32); + RootNode = new BBMOD_Node(self).from_buffer(_buffer); + + // Bone offsets + BoneCount = buffer_read(_buffer, buffer_u32); + + if (BoneCount > 0) + { + __offsetArray = array_create(BoneCount * 8, 0); + + repeat (BoneCount) + { + var _index = buffer_read(_buffer, buffer_f32) * 8; // Bone index + __offsetArray[@ _index + 0] = buffer_read(_buffer, buffer_f32); + __offsetArray[@ _index + 1] = buffer_read(_buffer, buffer_f32); + __offsetArray[@ _index + 2] = buffer_read(_buffer, buffer_f32); + __offsetArray[@ _index + 3] = buffer_read(_buffer, buffer_f32); + __offsetArray[@ _index + 4] = buffer_read(_buffer, buffer_f32); + __offsetArray[@ _index + 5] = buffer_read(_buffer, buffer_f32); + __offsetArray[@ _index + 6] = buffer_read(_buffer, buffer_f32); + __offsetArray[@ _index + 7] = buffer_read(_buffer, buffer_f32); + } + } + + // Materials + MaterialCount = buffer_read(_buffer, buffer_u32); + + if (MaterialCount > 0) + { + Materials = array_create(MaterialCount, BBMOD_MATERIAL_DEFAULT); + + var _materialNames = array_create(MaterialCount, undefined); + + i = 0; + repeat (MaterialCount) + { + _materialNames[@ i++] = buffer_read(_buffer, buffer_string); + } + + MaterialNames = _materialNames; + } + + IsLoaded = true; + + return self; + }; + + /// @func to_buffer(_buffer) + /// + /// @desc Writes model data to a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to write the data to. + /// + /// @return {Struct.BBMOD_Model} Returns `self`. + static to_buffer = function (_buffer) { + buffer_write(_buffer, buffer_string, "BBMOD"); + buffer_write(_buffer, buffer_u8, VersionMajor); + buffer_write(_buffer, buffer_u8, VersionMinor); + + // Vertex format + if (VersionMinor < 2) + { + __bbmod_vertex_format_save(VertexFormat, _buffer, VersionMinor); + } + + // Meshes + var _meshCount = array_length(Meshes); + buffer_write(_buffer, buffer_u32, _meshCount) + + var i = 0; + repeat (_meshCount) + { + Meshes[i++].to_buffer(_buffer); + } + + // Node count and root node + buffer_write(_buffer, buffer_u32, NodeCount); + RootNode.to_buffer(_buffer); + + // Bone offsets + buffer_write(_buffer, buffer_u32, BoneCount); + + if (BoneCount > 0) + { + var _index = 0; + repeat (BoneCount) + { + buffer_write(_buffer, buffer_f32, _index / 8); // Bone index + buffer_write(_buffer, buffer_f32, __offsetArray[_index + 0]); + buffer_write(_buffer, buffer_f32, __offsetArray[_index + 1]); + buffer_write(_buffer, buffer_f32, __offsetArray[_index + 2]); + buffer_write(_buffer, buffer_f32, __offsetArray[_index + 3]); + buffer_write(_buffer, buffer_f32, __offsetArray[_index + 4]); + buffer_write(_buffer, buffer_f32, __offsetArray[_index + 5]); + buffer_write(_buffer, buffer_f32, __offsetArray[_index + 6]); + buffer_write(_buffer, buffer_f32, __offsetArray[_index + 7]); + _index += 8; + } + } + + // Materials + buffer_write(_buffer, buffer_u32, MaterialCount); + + i = 0; + repeat (MaterialCount) + { + buffer_write(_buffer, buffer_string, MaterialNames[i++]); + } + + return self; + }; + + /// @func freeze() + /// + /// @desc Freezes all vertex buffers used by the model. This should make its + /// rendering faster, but it disables creating new batches of the model. + /// + /// @return {Struct.BBMOD_Model} Returns `self`. + static freeze = function () { + gml_pragma("forceinline"); + if (!Frozen) + { + var i = 0; + repeat (array_length(Meshes)) + { + Meshes[i++].freeze(); + } + Frozen = true; + } + return self; + }; + + /// @func find_node(_idOrName[, _node]) + /// + /// @desc Finds a node by its name or id. + /// + /// @param {Real, String} _idOrName The id (real) or the name (string) of + /// the node. + /// @param {Struct.BBMOD_Node} [_node] The node to start searching from. + /// Defaults to the root node. + /// + /// @return {Struct.BBMOD_Node} Returns the found node or `undefined`. + static find_node = function (_idOrName, _node=RootNode) { + var _isName = is_string(_idOrName); + if (_isName && _node.Name == _idOrName) + { + return _node; + } + if (!_isName && _node.Index == _idOrName) + { + return _node; + } + var _children = _node.Children; + var i = 0; + repeat (array_length(_children)) + { + var _found = find_node(_idOrName, _children[i++]); + if (_found != undefined) + { + return _found; + } + } + return undefined; + }; + + /// @func find_node_id(_nodeName) + /// + /// @desc Finds id of the model's node by its name. + /// + /// @param {String} _nodeName The name of the node. + /// + /// @return {Real} The id of the node or `undefined` when it is not found. + /// + /// @note It is not recommended to use this method in release builds, because + /// having many of these lookups can slow down your game! You should instead + /// use the ids available from the `_log.txt` files, which are created during + /// model conversion. + static find_node_id = function (_nodeName) { + gml_pragma("forceinline"); + var _node = find_node(_nodeName); + if (_node != undefined) + { + return _node.Index; + } + return undefined; + }; + + /// @func get_material(_name) + /// + /// @desc Retrieves a material by its name. + /// + /// @param {String} _name The name of the material. + /// + /// @return {Struct.BBMOD_BaseMaterial} The material. + /// + /// @throws {BBMOD_Exception} If the model does not have a material with + /// given name. + /// + /// @see BBMOD_Model.Materials + /// @see BBMOD_Model.MaterialNames + /// @see BBMOD_Model.set_material + /// @see BBMOD_BaseMaterial + static get_material = function (_name) { + var i = 0; + repeat (MaterialCount) + { + if (MaterialNames[i] == _name) + { + return Materials[i]; + } + ++i; + } + throw new BBMOD_Exception("No such material found!"); + }; + + /// @func set_material(_name, _material) + /// + /// @desc Sets a material. + /// + /// @param {String} _name The name of the material slot. + /// @param {Struct.BBMOD_BaseMaterial} _material The material. + /// + /// @return {Struct.BBMOD_Model} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the model does not have a material with + /// given name. + /// + /// @see BBMOD_Model.Materials + /// @see BBMOD_Model.MaterialNames + /// @see BBMOD_Model.get_material + /// @see BBMOD_BaseMaterial + static set_material = function (_name, _material) { + var i = 0; + repeat (MaterialCount) + { + if (MaterialNames[i] == _name) + { + Materials[@ i] = _material; + return self; + } + ++i; + } + throw new BBMOD_Exception("No such material found!"); + }; + + /// @func get_vertex_format([_bones[, _ids]]) + /// + /// @desc Used to retrieve or create a vertex format compatible with the model. + /// + /// @param {Bool} [_bones] Use `true` to include bone data in the vertex + /// format. Defaults to `true`. + /// @param {Bool} [_ids] Use `true` to include model instance ids in the + /// vertex format. Defaults to `false`. + /// + /// @deprecated Each {@link BBMOD_Mesh} now has its own vertex format! + static get_vertex_format = function (_bones=true, _ids=false) { + gml_pragma("forceinline"); + var _vertexFormat = VertexFormat ? VertexFormat : Meshes[0].VertexFormat; + return new BBMOD_VertexFormat( + _vertexFormat.Vertices, + _vertexFormat.Normals, + _vertexFormat.TextureCoords, + _vertexFormat.Colors, + _vertexFormat.TangentW, + _bones ? _vertexFormat.Bones : false, + _ids); + }; + + /// @func submit([_materials[, _transform[, _batchData]]]) + /// + /// @desc Immediately submits the model for rendering. + /// + /// @param {Array} [_materials] An array of + /// materials, one for each material slot of the model. If not specified, + /// then {@link BBMOD_Model.Materials} is used. Defaults to `undefined`. + /// @param {Array} [_transform] An array of dual quaternions for + /// transforming animated models or `undefined`. + /// @param {Array, Array>} [_batchData] Data for dynamic + /// batching or `undefined`. + /// + /// @return {Struct.BBMOD_Model} Returns `self`. + /// + /// @example + /// ```gml + /// bbmod_material_reset(); + /// // Render a terrain model (does not have animation data) + /// modTerrain.submit([mat_grass]); + /// // Render a character model (animated by animationPlayer) + /// modCharacter.submit([mat_head, mat_body], animationPlayer.get_transform()); + /// bbmod_material_reset(); + /// ``` + /// + /// @note Only parts of the model that use materials compatible with the + /// current render pass are submitted! + /// + /// This method does not do anything if the model has not been loaded yet. + /// + /// @see BBMOD_Resource.IsLoaded + /// @see BBMOD_BaseMaterial + /// @see BBMOD_AnimationPlayer.get_transform + /// @see bbmod_material_reset + /// @see BBMOD_ERenderPass + static submit = function (_materials=undefined, _transform=undefined, _batchData=undefined) { + gml_pragma("forceinline"); + if (RootNode != undefined) + { + _materials ??= Materials; + RootNode.submit(_materials, _transform, _batchData); + } + return self; + }; + + /// @func render([_materials[, _transform[, _batchData[, _matrix]]]]) + /// + /// @desc Enqueues the model for rendering. + /// + /// @param {Array} [_materials] An array of + /// materials, one for each material slot of the model. If not specified, + /// then {@link BBMOD_Model.Materials} is used. Defaults to `undefined`. + /// @param {Array} [_transform] An array of dual quaternions for + /// transforming animated models or `undefined`. + /// @param {Array, Array>} [_batchData] Data for dynamic + /// batching or `undefined`. + /// @param {Array} [_matrix] The world matrix. Defaults to + /// `matrix_get(matrix_world)`. + /// + /// @return {Struct.BBMOD_Model} Returns `self`. + /// + /// @note This method does not do anything if the model has not been loaded + /// yet. + /// + /// @see BBMOD_Resource.IsLoaded + /// @see BBMOD_BaseMaterial + /// @see BBMOD_AnimationPlayer.get_transform + /// @see bbmod_material_reset + static render = function (_materials=undefined, _transform=undefined, _batchData=undefined, _matrix=undefined) { + gml_pragma("forceinline"); + if (RootNode != undefined) + { + _materials ??= Materials; + if (_matrix == undefined) + { + _matrix = matrix_get(matrix_world); + } + RootNode.render(_materials, _transform, _batchData, _matrix); + } + return self; + }; + + /// @func __to_dynamic_batch(_dynamicBatch) + /// + /// @param {Struct.BBMOD_DynamicBatch} _dynamicBatch + /// + /// @return {Struct.BBMOD_Model} Returns `self`. + /// + /// @private + static __to_dynamic_batch = function (_dynamicBatch) { + gml_pragma("forceinline"); + var i = 0; + repeat (array_length(Meshes)) + { + Meshes[i++].__to_dynamic_batch(_dynamicBatch); + } + return self; + }; + + /// @func __to_static_batch(_staticBatch, _transform) + /// + /// @param {Struct.BBMOD_StaticBatch} _staticBatch + /// @param {Array} _transform + /// + /// @return {Struct.BBMOD_Model} Returns `self`. + /// + /// @private + static __to_static_batch = function (_staticBatch, _transform) { + gml_pragma("forceinline"); + var i = 0; + repeat (array_length(Meshes)) + { + Meshes[i++].__to_static_batch(self, _staticBatch, _transform); + } + return self; + }; + + static destroy = function () { + Resource_destroy(); + var i = 0; + repeat (array_length(Meshes)) + { + Meshes[i++].destroy(); + } + Meshes = []; + return undefined; + }; + + if (_file != undefined) + { + from_file(_file, _sha1); + } +} diff --git a/scripts/BBMOD_Model/BBMOD_Model.yy b/scripts/BBMOD_Model/BBMOD_Model.yy new file mode 100644 index 000000000..d56961815 --- /dev/null +++ b/scripts/BBMOD_Model/BBMOD_Model.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Model", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Model", + "path": "folders/_Extensions/BBMOD/Core/Model.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Node/BBMOD_Node.gml b/scripts/BBMOD_Node/BBMOD_Node.gml new file mode 100644 index 000000000..441e854c8 --- /dev/null +++ b/scripts/BBMOD_Node/BBMOD_Node.gml @@ -0,0 +1,383 @@ +/// @var {Id.DsStack} A stack used when rendering nodes to avoid recursion. +/// @private +global.__bbmodRenderStack = ds_stack_create(); + +/// @func BBMOD_Node(_model) +/// +/// @extends BBMOD_Class +/// +/// @implements {BBMOD_IRenderable} +/// +/// @desc A node struct. +/// +/// @param {Struct.BBMOD_Model} _model The model which contains this node. +/// +/// @see BBMOD_Model.RootNode +function BBMOD_Node(_model) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_Model} The model which contains this node. + /// @readonly + Model = _model; + + /// @var {String} The name of the node. + /// @readonly + Name = ""; + + /// @var {Real} The node index. + /// @readonly + Index = 0; + + /// @var {Struct.BBMOD_Node} The parent of this node or `undefined` if it is + /// the root node. + /// @readonly + Parent = undefined; + + /// @var {Bool} If `true` then the node is a bone. + /// @readonly + IsBone = false; + + /// @var {Bool} Set to `false` to disable rendering of the node and its + /// child nodes. + Visible = true; + + /// @var {Struct.BBMOD_DualQuaternion} The transformation of the node. + /// @readonly + Transform = new BBMOD_DualQuaternion(); + + /// @var {Array} An array of mesh indices. + /// @readonly + Meshes = []; + + /// @var {Bool} If true then the node or a node down the chain has a mesh. + /// @readonly + IsRenderable = false; + + /// @var {Array} An array of child nodes. + /// @see BBMOD_Node + /// @readonly + Children = []; + + /// @func copy(_dest) + /// + /// @desc Copies node data into another node. + /// + /// @param {Struct.BBMOD_Node} _dest The node to copy data to. + /// + /// @return {Struct.BBMOD_Node} Returns `self`. + static copy = function (_dest) { + _dest.Model = Model; + _dest.Name = Name; + _dest.Index = Index; + _dest.Parent = Parent; + _dest.IsBone = IsBone; + _dest.Visible = Visible; + _dest.Transform = Transform.Clone(); + _dest.Meshes = bbmod_array_clone(Meshes); + _dest.IsRenderable = IsRenderable; + + for (var i = array_length(_dest.Children) - 1; i >= 0; --i) + { + _dest.Children[i].destroy(); + } + + _dest.Children = []; + + var i = 0; + repeat (array_length(Children)) + { + _dest.add_child(Children[i++].clone()); + } + + return self; + }; + + /// @func clone() + /// + /// @desc Creates a clone of the node. + /// + /// @return {Struct.BBMOD_Node} The created clone. + static clone = function () { + var _clone = new BBMOD_Node(Model); + copy(_clone); + return _clone; + }; + + /// @func add_child(_node) + /// + /// @desc Adds a child node. + /// + /// @param {Struct.BBMOD_Node} _node The child node to add. + /// + /// @return {Struct.BBMOD_Node} Returns `self`. + static add_child = function (_node) { + gml_pragma("forceinline"); + array_push(Children, _node); + _node.Parent = self; + return self; + }; + + /// @func set_renderable() + /// + /// @desc Marks the node and nodes up the chain as renderable. + /// + /// @return {Struct.BBMOD_Node} Returns `self`. + static set_renderable = function () { + gml_pragma("forceinline"); + var _current = self; + while (_current != undefined) + { + //if (_current.IsRenderable) + //{ + // break; + //} + _current.IsRenderable = true; + _current = _current.Parent; + } + return self; + }; + + /// @func from_buffer(_buffer) + /// + /// @desc Loads node data from a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to load the data from. + /// + /// @return {Struct.BBMOD_Node} Returns `self`. + static from_buffer = function (_buffer) { + var i; + + Name = buffer_read(_buffer, buffer_string); + Index = buffer_read(_buffer, buffer_f32); + IsBone = buffer_read(_buffer, buffer_bool); + Visible = true; + Transform = Transform.FromBuffer(_buffer, buffer_f32); + + // Meshes + var _meshCount = buffer_read(_buffer, buffer_u32); + var _meshes = array_create(_meshCount, undefined); + Meshes = _meshes; + + if (_meshCount > 0) + { + set_renderable(); + } + + i = 0; + repeat (_meshCount) + { + _meshes[@ i++] = buffer_read(_buffer, buffer_u32); + } + + // Child nodes + var _childCount = buffer_read(_buffer, buffer_u32); + Children = []; + + repeat (_childCount) + { + var _child = new BBMOD_Node(Model); + add_child(_child); + _child.from_buffer(_buffer); + } + + return self; + }; + + /// @func to_buffer(_buffer) + /// + /// @desc Writes node data to a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to write the data to. + /// + /// @return {Struct.BBMOD_Node} Returns `self`. + static to_buffer = function (_buffer) { + var i; + + buffer_write(_buffer, buffer_string, Name); + buffer_write(_buffer, buffer_f32, Index); + buffer_write(_buffer, buffer_bool, IsBone); + Transform.ToBuffer(_buffer, buffer_f32); + + // Meshes + var _meshCount = array_length(Meshes); + buffer_write(_buffer, buffer_u32, _meshCount) + + i = 0; + repeat (_meshCount) + { + buffer_write(_buffer, buffer_u32, Meshes[i++]); + } + + // Child nodes + var _childCount = array_length(Children); + buffer_write(_buffer, buffer_u32, _childCount); + + i = 0; + repeat (_childCount) + { + Children[i++].to_buffer(_buffer); + } + + return self; + }; + + /// @func submit(_materials, _transform, _batchData) + /// + /// @desc Immediately submits the node for rendering. + /// + /// @param {Array} _materials An array of materials, + /// one for each material slot of the model. + /// @param {Array} _transform An array of dual quaternions for + /// transforming animated models or `undefined`. + /// @param {Array, Array>} _batchData Data for dynamic + /// batching or `undefined`. + /// + /// @return {Struct.BBMOD_Node} Returns `self`. + static submit = function (_materials, _transform, _batchData) { + var _meshes = Model.Meshes; + var _renderStack = global.__bbmodRenderStack; + var _node = self; + + var _matrix = matrix_get(matrix_world); + ds_stack_push(_renderStack, _node); + + while (!ds_stack_empty(_renderStack)) + { + _node = ds_stack_pop(_renderStack); + + if (!_node.IsRenderable || !_node.Visible) + { + continue; + } + + var _nodeTransform = undefined; + var _nodeMatrix = undefined; + + var _meshIndices = _node.Meshes; + var _children = _node.Children; + var i = 0; + + repeat (array_length(_meshIndices)) + { + var _mesh = _meshes[_meshIndices[i++]]; + var _material = _materials[_mesh.MaterialIndex]; + + if (_mesh.VertexFormat.Bones) + { + matrix_set(matrix_world, _matrix); + } + else + { + if (!_nodeTransform) + { + if (_transform == undefined) + { + _nodeTransform = _node.Transform; + } + else + { + _nodeTransform = new BBMOD_DualQuaternion() + .FromArray(_transform, _node.Index * 8); + } + _nodeMatrix = matrix_multiply(_nodeTransform.ToMatrix(), _matrix); + } + + matrix_set(matrix_world, _nodeMatrix); + } + + _mesh.submit(_material, _transform, _batchData); + } + + i = 0; + repeat (array_length(_children)) + { + ds_stack_push(_renderStack, _children[i++]); + } + } + + matrix_set(matrix_world, _matrix); + + return self; + }; + + /// @func render(_materials, _transform, _batchData, _matrix) + /// + /// @desc Enqueues the node for rendering. + /// + /// @param {Array} _materials An array of materials, + /// one for each material slot of the model. + /// @param {Array} _transform An array of dual quaternions for + /// transforming animated models or `undefined`. + /// @param {Array, Array>} _batchData Data for dynamic + /// batching or `undefined`. + /// @param {Array} _matrix The current world matrix. + /// + /// @return {Struct.BBMOD_Node} Returns `self`. + static render = function (_materials, _transform, _batchData, _matrix) { + var _meshes = Model.Meshes; + var _renderStack = global.__bbmodRenderStack; + var _node = self; + + ds_stack_push(_renderStack, _node); + + while (!ds_stack_empty(_renderStack)) + { + _node = ds_stack_pop(_renderStack); + + if (!_node.IsRenderable || !_node.Visible) + { + continue; + } + + var _nodeTransform = undefined; + var _nodeMatrix = undefined; + + var _meshIndices = _node.Meshes; + var _children = _node.Children; + var i = 0; + + repeat (array_length(_meshIndices)) + { + var _mesh = _meshes[_meshIndices[i++]]; + var _material = _materials[_mesh.MaterialIndex]; + + var _meshMatrix; + + if (_mesh.VertexFormat.Bones) + { + _meshMatrix = _matrix; + } + else + { + if (!_nodeTransform) + { + if (_transform == undefined) + { + _nodeTransform = _node.Transform; + } + else + { + _nodeTransform = new BBMOD_DualQuaternion() + .FromArray(_transform, _node.Index * 8); + } + _nodeMatrix = matrix_multiply(_nodeTransform.ToMatrix(), _matrix); + } + + _meshMatrix = _nodeMatrix; + } + + _mesh.render(_material, _transform, _batchData, _meshMatrix); + } + + i = 0; + repeat (array_length(_children)) + { + ds_stack_push(_renderStack, _children[i++]); + } + } + + return self; + }; +} diff --git a/scripts/BBMOD_Node/BBMOD_Node.yy b/scripts/BBMOD_Node/BBMOD_Node.yy new file mode 100644 index 000000000..7d8857296 --- /dev/null +++ b/scripts/BBMOD_Node/BBMOD_Node.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Node", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Model", + "path": "folders/_Extensions/BBMOD/Core/Model.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_NotImplementedException/BBMOD_NotImplementedException.gml b/scripts/BBMOD_NotImplementedException/BBMOD_NotImplementedException.gml new file mode 100644 index 000000000..a428cab00 --- /dev/null +++ b/scripts/BBMOD_NotImplementedException/BBMOD_NotImplementedException.gml @@ -0,0 +1,10 @@ +/// @func BBMOD_NotImplementedException() +/// +/// @extends BBMOD_Exception +/// +/// @desc An exception thrown when called method is not implemented. +function BBMOD_NotImplementedException() + : BBMOD_Exception("Method not implemented!") constructor +{ + BBMOD_CLASS_GENERATED_BODY; +} diff --git a/scripts/BBMOD_NotImplementedException/BBMOD_NotImplementedException.yy b/scripts/BBMOD_NotImplementedException/BBMOD_NotImplementedException.yy new file mode 100644 index 000000000..4ed004d87 --- /dev/null +++ b/scripts/BBMOD_NotImplementedException/BBMOD_NotImplementedException.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_NotImplementedException", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Exceptions", + "path": "folders/_Extensions/BBMOD/Core/Exceptions.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_OBJImporter/BBMOD_OBJImporter.gml b/scripts/BBMOD_OBJImporter/BBMOD_OBJImporter.gml new file mode 100644 index 000000000..04ba5da89 --- /dev/null +++ b/scripts/BBMOD_OBJImporter/BBMOD_OBJImporter.gml @@ -0,0 +1,296 @@ +/// @func BBMOD_OBJImporter() +/// +/// @extends BBMOD_Importer +/// +/// @desc An `*.obj` model importer. +/// +/// @example +/// ```gml +/// var _objImporter = new BBMOD_OBJImporter(); +/// modHouse = _objImporter.import("Data/Assets/House.obj"); +/// modTree = _objImporter.import("Data/Assets/Tree.obj"); +/// modFence = _objImporter.import("Data/Assets/Fence.obj"); +/// _objImporter = _objImporter.destroy(); +/// ``` +function BBMOD_OBJImporter() + : BBMOD_Importer() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Importer_destroy = destroy; + + /// @var {Bool} If `true`, then Y and Z axes are switched in imported + /// models. Default value is `false.` + ConvertYToZUp = false; + + /// @var {Bool} If `true`, then the winding order of vertices is inverted. + /// Default value is `false`. + InvertWinding = false; + + __vertices = ds_list_create(); + + __normals = ds_list_create(); + + __textureCoords = ds_list_create(); + + static can_import = function (_path) { + return (filename_ext(_path) == ".obj"); + }; + + static import = function (_path) { + ds_list_clear(__vertices); + ds_list_clear(__normals); + ds_list_clear(__textureCoords); + + var _file = file_text_open_read(_path); + if (_file == -1) + { + throw new BBMOD_Exception("Could not open file \"" + _path + "\"!"); + } + + var _vformat = new BBMOD_VertexFormat(true, true, true, false, true); + + var _model = new BBMOD_Model(); + _model.IsLoaded = true; + _model.VertexFormat = _vformat; + _model.Meshes = []; + _model.NodeCount = 1; + + var _root = new BBMOD_Node(_model); + _root.Name = "Node0"; + _model.RootNode = _root; + + var _meshBuilder = undefined; + var _split = array_create(2); + var _face = array_create(3); + var _vertexInd = array_create(3); + + var _node = _root; + var _material = undefined; + + while (true) + { + var _line = file_text_read_string(_file); + bbmod_string_split_on_first(_line, " ", _split); + var _keyword = _split[0]; + _line = _split[1]; + + // Build mesh + // TODO: Deduplicate code + if (_keyword != "f" && _meshBuilder != undefined) + { + _meshBuilder.make_tangents(); + var _mesh = _meshBuilder.build(); + _mesh.Model = _model; + + var _meshIndex = array_length(_model.Meshes); + array_push(_model.Meshes, _mesh); + array_push(_node.Meshes, _meshIndex); + _node.set_renderable(); + + if (_material == undefined) + { + _material = 0; + _model.MaterialCount = 1; + array_push(_model.Materials, BBMOD_MATERIAL_DEFAULT); + array_push(_model.MaterialNames, "Material"); + } + _mesh.MaterialIndex = _material; + + _meshBuilder = _meshBuilder.destroy(); + } + + switch (_keyword) + { + // Object + case "o": + _node = new BBMOD_Node(_model); + _root.add_child(_node); + _node.Index = array_length(_root.Children); + _node.Name = "Node" + string(_node.Index); + ++_model.NodeCount; + break; + + // Material + case "usemtl": + var _ind = array_length(_model.MaterialNames) - 1; + for (/**/; _ind >= 0; --_ind) + { + if (_model.MaterialNames[_ind] == _line) + { + break; + } + } + if (_ind == -1) + { + _ind = array_length(_model.MaterialNames); + array_push(_model.MaterialNames, _line); + array_push(_model.Materials, BBMOD_MATERIAL_DEFAULT); + ++_model.MaterialCount; + } + _material = _ind; + break; + + // Vertex + case "v": + bbmod_string_split_on_first(_line, " ", _split); + var _vx = real(_split[0]); + + bbmod_string_split_on_first(_split[1], " ", _split); + var _vy = real(_split[0]); + + bbmod_string_split_on_first(_split[1], " ", _split); + var _vz = real(_split[0]); + + if (ConvertYToZUp) + { + var _temp = _vz; + _vz = _vy; + _vy = _temp; + } + + ds_list_add(__vertices, _vx, _vy, _vz); + break; + + // Normal + case "vn": + bbmod_string_split_on_first(_line, " ", _split); + var _nx = real(_split[0]); + + bbmod_string_split_on_first(_split[1], " ", _split); + var _ny = real(_split[0]); + + bbmod_string_split_on_first(_split[1], " ", _split); + var _nz = real(_split[0]); + + if (ConvertYToZUp) + { + var _temp = _nz; + _nz = _ny; + _ny = _temp; + } + + ds_list_add(__normals, _nx, _ny, _nz); + break; + + // Texture + case "vt": + bbmod_string_split_on_first(_line, " ", _split); + var _tx = real(_split[0]); + if (FlipUVHorizontally) + { + _tx = 1.0 - _tx; + } + + bbmod_string_split_on_first(_split[1], " ", _split); + var _ty = real(_split[0]); + if (FlipUVVertically) + { + _ty = 1.0 - _ty; + } + + ds_list_add(__textureCoords, _tx, _ty); + break; + + // Face + case "f": + if (_meshBuilder == undefined) + { + _meshBuilder = new BBMOD_MeshBuilder(); + } + + bbmod_string_split_on_first(_line, " ", _split); + _face[@ 0] = _split[0]; + bbmod_string_split_on_first(_split[1], " ", _split); + _face[@ 1] = _split[0]; + bbmod_string_split_on_first(_split[1], " ", _split); + _face[@ 2] = _split[0]; + + for (var i = 0; i < 3; ++i) + { + bbmod_string_split_on_first(_face[i], "/", _split); + var _v = (real(_split[0]) - 1) * 3; + + bbmod_string_split_on_first(_split[1], "/", _split); + var _t = (_split[0] != "") + ? (real(_split[0]) - 1) * 2 + : -1; + + bbmod_string_split_on_first(_split[1], "/", _split); + var _n = (real(_split[0]) - 1) * 3; + + var _vertex = new BBMOD_Vertex(_vformat); + _vertex.Position.X = __vertices[| _v]; + _vertex.Position.Y = __vertices[| _v + 1]; + _vertex.Position.Z = __vertices[| _v + 2]; + + _vertex.Normal.X = __normals[| _n]; + _vertex.Normal.Y = __normals[| _n + 1]; + _vertex.Normal.Z = __normals[| _n + 2]; + + if (_t != -1) + { + _vertex.TextureCoord.X = __textureCoords[| _t]; + _vertex.TextureCoord.Y = __textureCoords[| _t + 1]; + } + + _vertexInd[@ i] = _meshBuilder.add_vertex(_vertex); + } + + if (InvertWinding) + { + _meshBuilder.add_face(_vertexInd[2], _vertexInd[1], _vertexInd[0]); + } + else + { + _meshBuilder.add_face(_vertexInd[0], _vertexInd[1], _vertexInd[2]); + } + break; + } + + file_text_readln(_file); + + if (file_text_eof(_file)) + { + break; + } + } + + file_text_close(_file); + + // Build mesh + // TODO: Deduplicate code + if (_meshBuilder != undefined) + { + _meshBuilder.make_tangents(); + var _mesh = _meshBuilder.build(); + _mesh.Model = _model; + + var _meshIndex = array_length(_model.Meshes); + array_push(_model.Meshes, _mesh); + array_push(_node.Meshes, _meshIndex); + _node.set_renderable(); + + if (_material == undefined) + { + _material = 0; + _model.MaterialCount = 1; + array_push(_model.Materials, BBMOD_MATERIAL_DEFAULT); + array_push(_model.MaterialNames, "Material"); + } + _mesh.MaterialIndex = _material; + + _meshBuilder = _meshBuilder.destroy(); + } + + return _model; + }; + + static destroy = function () { + Importer_destroy(); + ds_list_destroy(__vertices); + ds_list_destroy(__normals); + ds_list_destroy(__textureCoords); + return undefined; + }; +} diff --git a/scripts/BBMOD_OBJImporter/BBMOD_OBJImporter.yy b/scripts/BBMOD_OBJImporter/BBMOD_OBJImporter.yy new file mode 100644 index 000000000..843522ef6 --- /dev/null +++ b/scripts/BBMOD_OBJImporter/BBMOD_OBJImporter.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_OBJImporter", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "OBJImporter", + "path": "folders/_Extensions/BBMOD/OBJImporter.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_OutOfRangeException/BBMOD_OutOfRangeException.gml b/scripts/BBMOD_OutOfRangeException/BBMOD_OutOfRangeException.gml new file mode 100644 index 000000000..97c3d75a5 --- /dev/null +++ b/scripts/BBMOD_OutOfRangeException/BBMOD_OutOfRangeException.gml @@ -0,0 +1,14 @@ +/// @func BBMOD_OutOfRangeException([_msg]) +/// +/// @extends BBMOD_Exception +/// +/// @desc An exception thrown when you try to read a value from a data structure +/// at an index which is out of its range. +/// +/// @param {string} [_msg] The exception message. Defaults to "Index out of +/// range!". +function BBMOD_OutOfRangeException(_msg="Index out of range!") + : BBMOD_Exception(_msg) constructor +{ + BBMOD_CLASS_GENERATED_BODY; +} diff --git a/scripts/BBMOD_OutOfRangeException/BBMOD_OutOfRangeException.yy b/scripts/BBMOD_OutOfRangeException/BBMOD_OutOfRangeException.yy new file mode 100644 index 000000000..49a5153f9 --- /dev/null +++ b/scripts/BBMOD_OutOfRangeException/BBMOD_OutOfRangeException.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_OutOfRangeException", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Exceptions", + "path": "folders/_Extensions/BBMOD/Core/Exceptions.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_ParticleEmitter/BBMOD_ParticleEmitter.gml b/scripts/BBMOD_ParticleEmitter/BBMOD_ParticleEmitter.gml new file mode 100644 index 000000000..4a9abc0f2 --- /dev/null +++ b/scripts/BBMOD_ParticleEmitter/BBMOD_ParticleEmitter.gml @@ -0,0 +1,499 @@ +/// @func BBMOD_ParticleEmitter(_position, _system) +/// +/// @desc Emits particles at a specific position in the world. The behavior of +/// the emitted particles is defined by a particle system. +/// +/// @param {Struct.BBMOD_Vec3} _position The emitter's position in world-space. +/// @param {Struct.BBMOD_ParticleSystem} _system The particle system that +/// defines behavior of emitted particles. +/// +/// @see BBBMOD_ParticleSystem +function BBMOD_ParticleEmitter(_position, _system) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Struct.BBMOD_Vec3} The emitter's position in world-space. + Position = _position; + + /// @var {Struct.BBMOD_ParticleSystem} The system of particles that this + /// emitter emits. + /// @readonly + System = _system; + + /// @var {Id.DsGrid} Grid of particle data. Particles are stored in rows and + /// particle data in columns. The order of particle data is defined in + /// {@link BBMOD_EParticle}. Particles alive are stored first, followed by + /// dead particles. + Particles = ds_grid_create(BBMOD_EParticle.SIZE, System.ParticleCount); + + /// @var {Id.DsGrid} Grid used to do fast computations on all particles at + /// once. Its dimensions are 4 x `System.ParticleCount`, i.e. sufficient for + /// processing vec4s. + GridCompute = ds_grid_create(4, System.ParticleCount); + + /// @var {Real} Number of particles that are alive. + /// @readonly + ParticlesAlive = 0; + + /// @var {Real} The index of the particle that will be spawned next if all + /// particles are already alive. + /// @private + __particleSpawnNext = 0; + + /// @var {Real} + /// @private + __time = 0.0; + + // Initialize particle index + ds_grid_clear(Particles, 0.0); + + for (var _particleIndex = 0; + _particleIndex < System.ParticleCount; + ++_particleIndex) + { + Particles[# BBMOD_EParticle.Id, _particleIndex] = _particleIndex; + } + + /// @func spawn_particle([_position]) + /// + /// @desc If the particle system has not reached the end of the emit cycle + /// yet or if it is looping, then a new particle is spawned. If the maximum + /// number of particles has already been reached, then an existing particle + /// is used, without calling {@link BBMOD_ParticleModule.on_particle_finish}. + /// + /// @param {Struct.BBMOD_Vec3} [_position] The position to spawn the particle + /// at. Defaults to the emitter's position. + /// + /// @return {Bool} Returns `true` if a particle was spawned. + static spawn_particle = function (_position=undefined) { + gml_pragma("forceinline"); + if (__time >= System.Duration && !System.Loop) + { + return false; + } + + var _particleIndex; + + if (System.ParticleCount - ParticlesAlive > 0) + { + _particleIndex = ParticlesAlive++; + } + else + { + _particleIndex = __particleSpawnNext; + if (++__particleSpawnNext >= System.ParticleCount) + { + __particleSpawnNext = 0; + } + } + + _position ??= Position; + + Particles[# BBMOD_EParticle.IsAlive, _particleIndex] = true; + Particles[# BBMOD_EParticle.TimeAlive, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.Health, _particleIndex] = 1.0; + Particles[# BBMOD_EParticle.HealthLeft, _particleIndex] = 1.0; + Particles[# BBMOD_EParticle.PositionX, _particleIndex] = _position.X; + Particles[# BBMOD_EParticle.PositionY, _particleIndex] = _position.Y; + Particles[# BBMOD_EParticle.PositionZ, _particleIndex] = _position.Z; + Particles[# BBMOD_EParticle.VelocityX, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.VelocityY, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.VelocityZ, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.AccelerationX, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.AccelerationY, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.AccelerationZ, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.Mass, _particleIndex] = 1.0; + Particles[# BBMOD_EParticle.Drag, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.Bounce, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.HasCollided, _particleIndex] = false; + Particles[# BBMOD_EParticle.AccelerationRealX, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.AccelerationRealY, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.AccelerationRealZ, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.RotationX, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.RotationY, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.RotationZ, _particleIndex] = 0.0; + Particles[# BBMOD_EParticle.RotationW, _particleIndex] = 1.0; + Particles[# BBMOD_EParticle.ScaleX, _particleIndex] = 1.0; + Particles[# BBMOD_EParticle.ScaleY, _particleIndex] = 1.0; + Particles[# BBMOD_EParticle.ScaleZ, _particleIndex] = 1.0; + Particles[# BBMOD_EParticle.ColorR, _particleIndex] = 255.0; + Particles[# BBMOD_EParticle.ColorG, _particleIndex] = 255.0; + Particles[# BBMOD_EParticle.ColorB, _particleIndex] = 255.0; + Particles[# BBMOD_EParticle.ColorA, _particleIndex] = 1.0; + + var _modules = System.Modules; + var m = 0; + repeat (array_length(_modules)) + { + var _module = _modules[m++]; + if (_module.Enabled && _module.on_particle_start) + { + _module.on_particle_start(self, _particleIndex); + } + } + + return true; + }; + + /// @func update(_deltaTime) + /// + /// @desc Updates the emitter and all its particles. + /// + /// @param {Real} _deltaTime How much time has passed since the last frame + /// (in microseconds). + /// + /// @return {Struct.BBMOD_ParticleEmitter} Returns `self`. + static update = function (_deltaTime) { + if (finished(true)) + { + return self; + } + + var _deltaTimeS = _deltaTime * 0.000001; + var _modules = System.Modules; + + var _timeStart = (__time == 0.0 && _deltaTime != 0.0); + __time += _deltaTimeS; + var _timeOut = (__time >= System.Duration); + if (_timeOut && System.Loop) + { + __time = 0.0; + } + + var _temp1 = _deltaTimeS * 0.5; + var _temp2 = _deltaTimeS * _deltaTimeS * 0.5; + + //////////////////////////////////////////////////////////////////////// + // Kill particles + for (var _particleIndex = 0; + _particleIndex < ParticlesAlive; + ++_particleIndex) + { + if (Particles[# BBMOD_EParticle.HealthLeft, _particleIndex] <= 0.0) + { + var m = 0; + repeat (array_length(_modules)) + { + var _module = _modules[m++]; + if (_module.Enabled && _module.on_particle_finish) + { + _module.on_particle_finish(self, _particleIndex); + } + } + + // Swap with alive particle that is stored last + var _lastAlive = ParticlesAlive - 1; + var _particleId = Particles[# BBMOD_EParticle.Id, _particleIndex]; + ds_grid_set_grid_region( + Particles, + Particles, + 0, _lastAlive, + BBMOD_EParticle.SIZE - 1, _lastAlive, + 0, _particleIndex); + Particles[# BBMOD_ERenderPass.Id, _lastAlive] = _particleId; + Particles[# BBMOD_EParticle.IsAlive, _lastAlive] = false; + + --ParticlesAlive; + --_particleIndex; + } + } + + //////////////////////////////////////////////////////////////////////// + // Particle pre-simulation + if (ParticlesAlive > 0) + { + //////////////////////////////////////////////////////////////////// + // Clear HasCollided + ds_grid_set_region( + Particles, + BBMOD_EParticle.HasCollided, 0, + BBMOD_EParticle.HasCollided, ParticlesAlive - 1, + false); + + //////////////////////////////////////////////////////////////////// + // Time alive + ds_grid_add_region( + Particles, + BBMOD_EParticle.TimeAlive, 0, + BBMOD_EParticle.TimeAlive, ParticlesAlive - 1, + _deltaTimeS); + + //////////////////////////////////////////////////////////////////// + // Physics + + // position += velocity * _deltaTimeS: + ds_grid_set_grid_region( + GridCompute, + Particles, + BBMOD_EParticle.VelocityX, 0, + BBMOD_EParticle.VelocityZ, ParticlesAlive - 1, + 0, 0); + + ds_grid_multiply_region( + GridCompute, + 0, 0, + 2, ParticlesAlive - 1, + _deltaTimeS); + + ds_grid_add_grid_region( + Particles, + GridCompute, + 0, 0, + 2, ParticlesAlive - 1, + BBMOD_EParticle.PositionX, 0); + + // position += accelerationReal * _temp2: + ds_grid_set_grid_region( + GridCompute, + Particles, + BBMOD_EParticle.AccelerationRealX, 0, + BBMOD_EParticle.AccelerationRealZ, ParticlesAlive - 1, + 0, 0); + + ds_grid_multiply_region( + GridCompute, + 0, 0, + 2, ParticlesAlive - 1, + _temp2); + + ds_grid_add_grid_region( + Particles, + GridCompute, + 0, 0, + 2, ParticlesAlive - 1, + BBMOD_EParticle.PositionX, 0); + + // acceleration = (0, 0, 0) + ds_grid_set_region( + Particles, + BBMOD_EParticle.AccelerationX, 0, + BBMOD_EParticle.AccelerationZ, ParticlesAlive - 1, + 0.0); + } + + //////////////////////////////////////////////////////////////////////// + // Execute modules + var m = 0; + repeat (array_length(_modules)) + { + var _module = _modules[m++]; + if (_module.Enabled) + { + // Emitter start + if (_timeStart && _module.on_start) + { + _module.on_start(self); + } + + // Emitter update + if (_module.on_update) + { + _module.on_update(self, _deltaTime); + } + + // Emitter finish + if (_timeOut && _module.on_finish) + { + _module.on_finish(self); + } + } + } + + //////////////////////////////////////////////////////////////////////// + // Particle simulate physics + if (ParticlesAlive > 0) + { + // velocity += (accelerationReal + acceleration) * _temp1 + ds_grid_set_grid_region( + GridCompute, + Particles, + BBMOD_EParticle.AccelerationRealX, 0, + BBMOD_EParticle.AccelerationRealZ, ParticlesAlive - 1, + 0, 0); + + ds_grid_add_grid_region( + GridCompute, + Particles, + BBMOD_EParticle.AccelerationX, 0, + BBMOD_EParticle.AccelerationZ, ParticlesAlive - 1, + 0, 0); + + ds_grid_multiply_region( + GridCompute, + 0, 0, + 2, ParticlesAlive - 1, + _temp1); + + ds_grid_add_grid_region( + Particles, + GridCompute, + 0, 0, + 2, ParticlesAlive - 1, + BBMOD_EParticle.VelocityX, 0); + + // accelerationReal = acceleration + ds_grid_set_grid_region( + Particles, + Particles, + BBMOD_EParticle.AccelerationX, 0, + BBMOD_EParticle.AccelerationZ, ParticlesAlive - 1, + BBMOD_EParticle.AccelerationRealX, 0); + } + + return self; + }; + + /// @func finished([_particlesDead]) + /// + /// @desc Checks if the emitter cycle has finished. + /// + /// @param {Bool} [_particlesDead] Also check if there are no particles + /// alive. Defaults to `false.` + /// + /// @return {Bool} Returns `true` if the emitter cycle has finished + /// (and there are no particles alive). Aalways returns `false` if the + /// emitted particle system is looping. + static finished = function (_particlesDead=false) { + gml_pragma("forceinline"); + if (System.Loop) + { + return false; + } + if (__time >= System.Duration) + { + if (!_particlesDead || ParticlesAlive == 0) + { + return true; + } + } + return false; + }; + + static _draw = function (_method, _material=undefined) { + gml_pragma("forceinline"); + + var _dynamicBatch = System.__dynamicBatch; + var _batchSize = _dynamicBatch.Size; + _material ??= System.Material; + + var _particleCount = ParticlesAlive; + var _particlesSorted; + + if (System.Sort) + { + _particlesSorted = array_create(_particleCount); + var i = 0; + repeat (_particleCount) + { + _particlesSorted[@ i] = i; + ++i; + } + + array_sort(_particlesSorted, method(self, function (_p1, _p2) { + var _camPos = global.__bbmodCameraPosition; + var _particles = Particles; + var _d1 = point_distance_3d( + _particles[# BBMOD_EParticle.PositionX, _p1], + _particles[# BBMOD_EParticle.PositionY, _p1], + _particles[# BBMOD_EParticle.PositionZ, _p1], + _camPos.X, + _camPos.Y, + _camPos.Z); + var _d2 = point_distance_3d( + _particles[# BBMOD_EParticle.PositionX, _p2], + _particles[# BBMOD_EParticle.PositionY, _p2], + _particles[# BBMOD_EParticle.PositionZ, _p2], + _camPos.X, + _camPos.Y, + _camPos.Z); + if (_d2 > _d1) return +1; + if (_d2 < _d1) return -1; + return 0; + })); + } + + var _particles = Particles; + //var _color = new BBMOD_Color(); + var _particleIndex = 0; + var _batchCount = ceil(_particleCount / _batchSize); + var _batchData = array_create(_batchCount); + var _batchIndex = 0; + + repeat (_batchCount) + { + var _data = array_create(_batchSize * 16, 0); + var d = 0; + repeat (min(_particleCount, _batchSize)) + { + var i = System.Sort + ? _particlesSorted[_particleIndex++] + : _particleIndex++; + + _data[d + 0] = _particles[# BBMOD_EParticle.PositionX, i]; + _data[d + 1] = _particles[# BBMOD_EParticle.PositionY, i]; + _data[d + 2] = _particles[# BBMOD_EParticle.PositionZ, i]; + + _data[d + 4] = _particles[# BBMOD_EParticle.RotationX, i]; + _data[d + 5] = _particles[# BBMOD_EParticle.RotationY, i]; + _data[d + 6] = _particles[# BBMOD_EParticle.RotationZ, i]; + _data[d + 7] = _particles[# BBMOD_EParticle.RotationW, i]; + + _data[d + 8] = _particles[# BBMOD_EParticle.ScaleX, i]; + _data[d + 9] = _particles[# BBMOD_EParticle.ScaleY, i]; + _data[d + 10] = _particles[# BBMOD_EParticle.ScaleZ, i]; + + _data[d + 12] = _particles[# BBMOD_EParticle.ColorR, i] / 255.0; + _data[d + 13] = _particles[# BBMOD_EParticle.ColorG, i] / 255.0; + _data[d + 14] = _particles[# BBMOD_EParticle.ColorB, i] / 255.0; + _data[d + 15] = _particles[# BBMOD_EParticle.ColorA, i]; + + d += 16; + } + _particleCount -= _batchSize; + _batchData[@ _batchIndex++] = _data; + } + + if (_batchCount > 0) + { + _method(_material, _batchData); + } + }; + + /// @func submit([_material]) + /// + /// @desc Immediately submits particles for rendering. + /// + /// @param {Struct.BBMOD_Material} [_material] The material to use instead + /// of the one defined in the particle system or `undefined`. + /// + /// @return {Struct.BBMOD_ParticleEmitter} Returns `self`. + static submit = function (_material=undefined) { + var _dynamicBatch = System.__dynamicBatch; + _draw(method(_dynamicBatch, _dynamicBatch.submit), _material); + return self; + }; + + /// @func render([_material]) + /// + /// @desc Enqueus particles for rendering. + /// + /// @param {Struct.BBMOD_Material} [_material] The material to use instead + /// of the one defined in the particle system or `undefined`. + /// + /// @return {Struct.BBMOD_ParticleEmitter} Returns `self`. + static render = function (_material=undefined) { + var _dynamicBatch = System.__dynamicBatch; + _draw(method(_dynamicBatch, _dynamicBatch.render), _material); + return self; + }; + + static destroy = function () { + Class_destroy(); + ds_grid_destroy(Particles); + ds_grid_destroy(GridCompute); + return undefined; + }; +} diff --git a/scripts/BBMOD_ParticleEmitter/BBMOD_ParticleEmitter.yy b/scripts/BBMOD_ParticleEmitter/BBMOD_ParticleEmitter.yy new file mode 100644 index 000000000..52fb6033d --- /dev/null +++ b/scripts/BBMOD_ParticleEmitter/BBMOD_ParticleEmitter.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_ParticleEmitter", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Particles", + "path": "folders/_Extensions/BBMOD/Particles.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_ParticleMaterial/BBMOD_ParticleMaterial.yy b/scripts/BBMOD_ParticleMaterial/BBMOD_ParticleMaterial.yy new file mode 100644 index 000000000..d10208205 --- /dev/null +++ b/scripts/BBMOD_ParticleMaterial/BBMOD_ParticleMaterial.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_ParticleMaterial", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Particles", + "path": "folders/_Extensions/BBMOD/Particles.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_ParticleMaterial/bbmod_particlematerial.gml b/scripts/BBMOD_ParticleMaterial/bbmod_particlematerial.gml new file mode 100644 index 000000000..2b6b1b015 --- /dev/null +++ b/scripts/BBMOD_ParticleMaterial/bbmod_particlematerial.gml @@ -0,0 +1,36 @@ +/// @func BBMOD_ParticleMaterial([_shader]) +/// +/// @extends BBMOD_DefaultMaterial +/// +/// @desc A material that can be used for rendering particles. +/// +/// @param {Struct.BBMOD_Shader} [_shader] A shader that the material uses in +/// the {@link BBMOD_ERenderPass.Forward} pass. Leave `undefined` if you would +/// like to use {@link BBMOD_Material.set_shader} to specify shaders used in +/// specific render passes. +/// +/// @see BBMOD_ParticleShader +function BBMOD_ParticleMaterial(_shader=undefined) + : BBMOD_DefaultMaterial(_shader) constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static DefaultMaterial_copy = copy; + + /// @var {Real} Distance over which the particle smoothly dissappears when + /// getting closer to geometry rendered in the depth buffer. Use values less + /// or equal to 0 to disable the effect. Default value is 0. + SoftDistance = 0.0; + + static copy = function (_dest) { + DefaultMaterial_copy(_dest); + _dest.SoftDistance = SoftDistance; + return self; + }; + + static clone = function () { + var _clone = new BBMOD_ParticleMaterial(); + copy(_clone); + return _clone; + }; +} diff --git a/scripts/BBMOD_ParticleModule/BBMOD_ParticleModule.gml b/scripts/BBMOD_ParticleModule/BBMOD_ParticleModule.gml new file mode 100644 index 000000000..6dcbaa1cc --- /dev/null +++ b/scripts/BBMOD_ParticleModule/BBMOD_ParticleModule.gml @@ -0,0 +1,50 @@ +/// @func BBMOD_ParticleModule() +/// +/// @desc BBMOD_Class +/// +/// @desc Base struct for particle modules. These are composed into particle +/// system to define behavior of their particles. +/// +/// @see BBMOD_ParticleSystem +function BBMOD_ParticleModule() + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Bool} If `true` then the module is enabled. Defaults value + /// is `true`. + Enabled = true; + + /// @func on_start(_emitter) + /// @desc Executed at the beginning of the emitter's emission cycle and + /// every time it loops. + /// @param {Struct.BBMOD_ParticleEmitter} _emitter The emitter. + static on_start = undefined; + + /// @func on_update(_emitter, _deltaTime) + /// @desc Executed every time the emitter is updated. + /// @param {Struct.BBMOD_ParticleEmitter} _emitter The emitter. + /// @param {Real} _deltaTime How much time in microseconds has passed since + /// the last frame. + static on_update = undefined; + + /// @func on_finish(_emitter) + /// @desc Executed once at the end of the emitter's emission cycle. Never + /// executed if the emitted particle system is looping! + /// @param {Struct.BBMOD_ParticleEmitter} _emitter The emitter. + static on_finish = undefined; + + /// @func on_particle_start(_emitter, _particleIndex) + /// @desc Executed when a new particle is spawned. + /// @param {Struct.BBMOD_ParticleEmitter} _emitter The emitter. + /// @param {Real} _particleIndex The row within + /// {@link BBMOD_ParticleEmitter.Particles} at which is the particle stored. + static on_particle_start = undefined; + + /// @func (_emitter, _particleIndex) + /// @desc Executed when a particle dies. + /// @param {Struct.BBMOD_ParticleEmitter} _emitter The emitter. + /// @param {Real} _particleIndex The row within + /// {@link BBMOD_ParticleEmitter.Particles} at which is the particle stored. + static on_particle_finish = undefined; +} diff --git a/scripts/BBMOD_ParticleModule/BBMOD_ParticleModule.yy b/scripts/BBMOD_ParticleModule/BBMOD_ParticleModule.yy new file mode 100644 index 000000000..29103640a --- /dev/null +++ b/scripts/BBMOD_ParticleModule/BBMOD_ParticleModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_ParticleModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Particles", + "path": "folders/_Extensions/BBMOD/Particles.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_ParticleShader/BBMOD_ParticleShader.yy b/scripts/BBMOD_ParticleShader/BBMOD_ParticleShader.yy new file mode 100644 index 000000000..6768c8ad5 --- /dev/null +++ b/scripts/BBMOD_ParticleShader/BBMOD_ParticleShader.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_ParticleShader", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Particles", + "path": "folders/_Extensions/BBMOD/Particles.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_ParticleShader/bbmod_particleshader.gml b/scripts/BBMOD_ParticleShader/bbmod_particleshader.gml new file mode 100644 index 000000000..0f058b472 --- /dev/null +++ b/scripts/BBMOD_ParticleShader/bbmod_particleshader.gml @@ -0,0 +1,26 @@ +/// @func BBMOD_ParticleShader(_shader, _vertexFormat) +/// +/// @extends BBMOD_DefaultShader +/// +/// @desc Shader used by particle materials. +/// +/// @param {Asset.GMShader} _shader The shader resource. +/// @param {Struct.BBMOD_VertexFormat} _vertexFormat The vertex format required by the shader. +/// +/// @see BBMOD_ParticleMaterial +function BBMOD_ParticleShader(_shader, _vertexFormat) + : BBMOD_DefaultShader(_shader, _vertexFormat) constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static DefaultShader_set_material = set_material; + + static set_material = function (_material) { + gml_pragma("forceinline"); + DefaultShader_set_material(_material); + shader_set_uniform_f( + shader_get_uniform(shader_current(), "bbmod_SoftDistance"), + _material.SoftDistance); + return self; + }; +} diff --git a/scripts/BBMOD_ParticleSystem/BBMOD_ParticleSystem.gml b/scripts/BBMOD_ParticleSystem/BBMOD_ParticleSystem.gml new file mode 100644 index 000000000..c5a175965 --- /dev/null +++ b/scripts/BBMOD_ParticleSystem/BBMOD_ParticleSystem.gml @@ -0,0 +1,81 @@ +/// @func BBMOD_ParticleSystem(_model, _material, _particleCount[, _batchSize]) +/// +/// @extends BBMOD_Class +/// +/// @desc A collection of particle modules that together define behavior of +/// particles. +/// +/// @param {Struct.BBMOD_Model} _model The particle model. +/// @param {Struct.BBMOD_Material} _material The material used by the particle +/// system. +/// @param {Real} _particleCount Maximum number of particles alive in the +/// system. +/// @param {Real} [_batchSize] Number of particles rendered in a single draw +/// call. Default value is 32. +/// +/// @see BBMOD_ParticleModule +/// @see BBMOD_ParticleEmitter +/// @see BBMOD_MODEL_PARTICLE +/// @see BBMOD_MATERIAL_PARTICLE_LIT +/// @see BBMOD_MATERIAL_PARTICLE_UNLIT +function BBMOD_ParticleSystem(_model, _material, _particleCount, _batchSize=32) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Struct.BBMOD_Material} _material The material used by the particle + /// system. + Material = _material; + + /// @var {Real} Maximum number of particles alive in the system. + /// @readonly + ParticleCount = _particleCount; + + /// @var {Bool} Use `true` to sort particles back to front. This should be + /// enabled if you would like to use alpha blending. Default value is `false`. + Sort = false; + + /// @var {Real} How long in seconds is the system emitting particles for. + /// Default value is 5s. + Duration = 5.0; + + /// @var {Bool} If `true` then the emission cycle repeats after the duration. + /// Default value is `false`. + Loop = false; + + /// @var {Struct.BBMOD_DynamicBatch} + /// @private + __dynamicBatch = new BBMOD_DynamicBatch(_model, _batchSize).freeze(); + + /// @var {Array} An array of modules + /// affecting individual particles in this system. + /// @readonly + Modules = []; + + /// @func add_modules(_module...) + /// + /// @desc Adds modules to the particle system. + /// + /// @param {Struct.BBMOD_ParticleModule} _module The module to add. + /// + /// @return {Struct.BBMOD_ParticleSystem} Returns `self`. + /// + /// @see BBMOD_ParticleModule + static add_modules = function (_module) { + gml_pragma("forceinline"); + var i = 0; + repeat (argument_count) + { + array_push(Modules, argument[i++]); + } + return self; + }; + + static destroy = function () { + Class_destroy(); + __dynamicBatch = __dynamicBatch.destroy(); + return undefined; + }; +} diff --git a/scripts/BBMOD_ParticleSystem/BBMOD_ParticleSystem.yy b/scripts/BBMOD_ParticleSystem/BBMOD_ParticleSystem.yy new file mode 100644 index 000000000..5d941da67 --- /dev/null +++ b/scripts/BBMOD_ParticleSystem/BBMOD_ParticleSystem.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_ParticleSystem", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Particles", + "path": "folders/_Extensions/BBMOD/Particles.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_PlaneCollider/BBMOD_PlaneCollider.gml b/scripts/BBMOD_PlaneCollider/BBMOD_PlaneCollider.gml new file mode 100644 index 000000000..3fb1044f0 --- /dev/null +++ b/scripts/BBMOD_PlaneCollider/BBMOD_PlaneCollider.gml @@ -0,0 +1,96 @@ +/// @func BBMOD_PlaneCollider([_normal[, _distance]]) +/// +/// @extends BBMOD_Collider +/// +/// @desc A plane collider. +/// +/// @param {Struct.BBMOD_Vec3} [_normal] The plane's normal vector. Defaults to +/// {@link BBMOD_VEC3_UP}. +/// @param {Real} [_distance] The plane's distance from the world origin. +/// +/// @see BBMOD_AABBCollider +/// @see BBMOD_FrustumCollider +/// @see BBMOD_SphereCollider +function BBMOD_PlaneCollider(_normal=undefined, _distance=0.0) + : BBMOD_Collider() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_Vec3} The plane's normal vector. + Normal = _normal ?? BBMOD_VEC3_UP; + + /// @var {Real} The plane's distance from the world origin. + Distance = _distance; + + /// @func __getPointDistance(_point) + /// + /// @param {Struct.BBMOD_Vec3} _point + /// + /// @return {Real} + /// + /// @private + static __getPointDistance = function (_point) { + gml_pragma("forceinline"); + return (_point.Dot(Normal) - Distance); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L188 + static GetClosestPoint = function (_point) { + gml_pragma("forceinline"); + return _point.Sub(Normal.Scale(__getPointDistance(_point))); + }; + + static TestAABB = function (_aabb) { + gml_pragma("forceinline"); + return _aabb.TestPlane(self); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L541 + static TestPlane = function (_plane) { + gml_pragma("forceinline"); + var _d = Normal.Cross(_plane.Normal); + return !bbmod_cmp(_d.Dot(_d), 0.0); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L101 + static TestPoint = function (_point) { + gml_pragma("forceinline"); + return bbmod_cmp(__getPointDistance(_point), 0.0); + }; + + static TestSphere = function (_sphere) { + gml_pragma("forceinline"); + return _sphere.TestPlane(self); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L769 + static Raycast = function (_ray, _result=undefined) { + if (_result != undefined) + { + _result.Reset(); + } + + var _nd = _ray.Direction.Dot(Normal); + var _pn = _ray.Origin.Dot(Normal); + + if (_nd >= 0.0) + { + return false; + } + + var _t = (Distance - _pn) / _nd; + + if (_t >= 0.0) + { + if (_result != undefined) + { + _result.Distance = _t; + _result.Point = _ray.Origin.Add(_ray.Direction.Scale(_t)); + _result.Normal = Normal.Normalize(); + } + return true; + } + + return false; + }; +} diff --git a/scripts/BBMOD_PlaneCollider/BBMOD_PlaneCollider.yy b/scripts/BBMOD_PlaneCollider/BBMOD_PlaneCollider.yy new file mode 100644 index 000000000..560120f4f --- /dev/null +++ b/scripts/BBMOD_PlaneCollider/BBMOD_PlaneCollider.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_PlaneCollider", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Raycasting", + "path": "folders/_Extensions/BBMOD/Raycasting.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_PointLight/BBMOD_PointLight.gml b/scripts/BBMOD_PointLight/BBMOD_PointLight.gml new file mode 100644 index 000000000..4ccc0bbc4 --- /dev/null +++ b/scripts/BBMOD_PointLight/BBMOD_PointLight.gml @@ -0,0 +1,99 @@ +/// @func BBMOD_PointLight([_color[, _position[, _range]]]) +/// +/// @extends BBMOD_PunctualLight +/// +/// @desc A point light. +/// +/// @param {Struct.BBMOD_Color} [_color] The light's color. Defaults +/// to {@link BBMOD_C_WHITE}. +/// @param {Struct.BBMOD_Vec3} [_position] The light's position. +/// Defaults to `(0, 0, 0)`. +/// @param {Real} [_range] The light's range. Defaults to 1. +function BBMOD_PointLight(_color=BBMOD_C_WHITE, _position=undefined, _range=1.0) + : BBMOD_PunctualLight(_color, _position, _range) constructor +{ + BBMOD_CLASS_GENERATED_BODY; +} + +/// @func bbmod_light_point_add(_light) +/// +/// @desc Adds a point light to be sent to shaders. +/// +/// @param {Struct.BBMOD_PointLight} _light The point light. +/// +/// @deprecated Please use {@link bbmod_light_punctual_add} instead. +function bbmod_light_point_add(_light) +{ + gml_pragma("forceinline"); + bbmod_light_punctual_add(_light); +} + +/// @func bbmod_light_point_count() +/// +/// @desc Retrieves number of point lights added to be sent to shaders. +/// +/// @return {Real} The number of point lights added to be sent to shaders. +/// +/// @deprecated Please use {@link bbmod_light_punctual_count} instead. +function bbmod_light_point_count() +{ + gml_pragma("forceinline"); + return bbmod_light_punctual_count(); +} + +/// @func bbmod_light_point_get(_index) +/// +/// @desc Retrieves a point light at given index. +/// +/// @param {Real} _index The index of the point light. +/// +/// @return {Struct.BBMOD_PointLight} The point light. +/// +/// @deprecated Please use {@link bbmod_light_punctual_get} instead. +function bbmod_light_point_get(_index) +{ + gml_pragma("forceinline"); + return bbmod_light_punctual_get(_index); +} + +/// @func bbmod_light_point_remove(_light) +/// +/// @desc Removes a point light so it is not sent to shaders anymore. +/// +/// @param {Struct.BBMOD_PointLight} _light The point light to remove. +/// +/// @return {Bool} Returns `true` if the point light was removed or `false` if +/// the light was not found. +/// +/// @deprecated Please use {@link bbmod_light_punctual_remove} instead. +function bbmod_light_point_remove(_light) +{ + gml_pragma("forceinline"); + return bbmod_light_punctual_remove(_light); +} + +/// @func bbmod_light_point_remove_index(_index) +/// +/// @desc Removes a point light so it is not sent to shaders anymore. +/// +/// @param {Real} _index The index to remove the point light at. +/// +/// @return {Bool} Always returns `true`. +/// +/// @deprecated Please use {@link bbmod_light_punctual_remove_index} instead. +function bbmod_light_point_remove_index(_index) +{ + gml_pragma("forceinline"); + return bbmod_light_punctual_remove_index(_index); +} + +/// @func bbmod_light_point_clear() +/// +/// @desc Removes all point lights sent to shaders. +/// +/// @deprecated Please use {@link bbmod_light_punctual_clear} instead. +function bbmod_light_point_clear() +{ + gml_pragma("forceinline"); + bbmod_light_punctual_clear(); +} diff --git a/scripts/BBMOD_PointLight/BBMOD_PointLight.yy b/scripts/BBMOD_PointLight/BBMOD_PointLight.yy new file mode 100644 index 000000000..dd1919a9f --- /dev/null +++ b/scripts/BBMOD_PointLight/BBMOD_PointLight.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_PointLight", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Lights", + "path": "folders/_Extensions/BBMOD/Core/Lights.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_PostProcessor/BBMOD_PostProcessor.gml b/scripts/BBMOD_PostProcessor/BBMOD_PostProcessor.gml new file mode 100644 index 000000000..98427565e --- /dev/null +++ b/scripts/BBMOD_PostProcessor/BBMOD_PostProcessor.gml @@ -0,0 +1,174 @@ +/// @func BBMOD_PostProcessor() +/// +/// @extends BBMOD_Class +/// +/// @desc Handles post-processing effects like color grading, chromatic aberration, +/// grayscale effect, vignette and anti-aliasing. +function BBMOD_PostProcessor() + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Id.Surface} + /// @private + __surPostProcess = noone; + + /// @var {Bool} If `true` then the post-processor is enabled. Default value + /// is `true`. + Enabled = true; + + /// @var {Pointer.Texture} The lookup table texture used for color grading. + /// @note Post-processing must be enabled for this to have any effect! + /// @see BBMOD_Renderer.EnablePostProcessing + ColorGradingLUT = sprite_get_texture(BBMOD_SprColorGradingLUT, 0); + + /// @var {Real} The strength of the chromatic aberration effect. Use 0 to + /// disable the effect. Defaults to 0. + /// @note Post-processing must be enabled for this to have any effect! + /// @see BBMOD_Renderer.EnablePostProcessing + ChromaticAberration = 0.0; + + /// @var {Real} Chromatic aberration offsets for RGB channels. Defaults to + /// `(-1, 0, 1)`. + /// @note Post-processing must be enabled for this to have any effect! + /// @see BBMOD_Renderer.EnablePostProcessing + ChromaticAberrationOffset = new BBMOD_Vec3(-1.0, 0.0, 1.0); + + /// @var {Real} The strength of the grayscale effect. Use values in range 0..1, + /// where 0 means the original color and 1 means grayscale. Defaults to 0. + /// @note Post-processing must be enabled for this to have any effect! + /// @see BBMOD_Renderer.EnablePostProcessing + Grayscale = 0.0; + + /// @var {Real} The strength of the vignette effect. Defaults to 0. + /// @note Post-processing must be enabled for this to have any effect! + /// @see BBMOD_Renderer.EnablePostProcessing + Vignette = 0.0; + + /// @var {Real} The color of the vignette effect. Defaults to `c_black`. + /// @note Post-processing must be enabled for this to have any effect! + /// @see BBMOD_Renderer.EnablePostProcessing + VignetteColor = c_black; + + /// @var {Real} Antialiasing technique to use. Use values from + /// {@link BBMOD_EAntialiasing}. Defaults to {@link BBMOD_EAntialiasing.None}. + Antialiasing = BBMOD_EAntialiasing.None; + + /// @func draw(_surface, _x, _y) + /// + /// @desc If enabled, draws a surface with post-processing applied, otherwise + /// draws the original surface. + /// + /// @param {Id.Surface} _surface The surface to draw with post-processing + /// applied. + /// @param {Real} _x The X position to draw the surface at. + /// @param {Real} _y The Y position to draw the surface at. + /// + /// @return {Struct.BBMOD_PostProcessor} Returns `self`. + /// + /// @see BBMOD_PostProcessor.Enabled + static draw = function (_surface, _x, _y) { + if (!Enabled) + { + draw_surface(_surface, _x, _y); + return self; + } + + var _world = matrix_get(matrix_world); + var _width = surface_get_width(_surface); + var _height = surface_get_height(_surface); + var _texelWidth = 1.0 / _width; + var _texelHeight = 1.0 / _height; + var _surFinal = _surface; + + gpu_push_state(); + gpu_set_tex_filter(true); + gpu_set_tex_repeat(false); + gpu_set_blendenable(false); + + //////////////////////////////////////////////////////////////////// + // Do post-processing + if (Antialiasing != BBMOD_EAntialiasing.None) + { + // If anti-aliasing is enabled, we need to do post-processing in + // another surface... + __surPostProcess = bbmod_surface_check(__surPostProcess, _width, _height); + surface_set_target(__surPostProcess); + matrix_set(matrix_world, matrix_build_identity()); + } + + var _shader = BBMOD_ShPostProcess; + shader_set(_shader); + texture_set_stage( + shader_get_sampler_index(_shader, "u_texLut"), + ColorGradingLUT); + shader_set_uniform_f( + shader_get_uniform(_shader, "u_vTexel"), + _texelWidth, _texelHeight); + shader_set_uniform_f( + shader_get_uniform(_shader, "u_vOffset"), + ChromaticAberrationOffset.X, + ChromaticAberrationOffset.Y, + ChromaticAberrationOffset.Z); + shader_set_uniform_f( + shader_get_uniform(_shader, "u_fDistortion"), + ChromaticAberration); + shader_set_uniform_f( + shader_get_uniform(_shader, "u_fGrayscale"), + Grayscale); + shader_set_uniform_f( + shader_get_uniform(_shader, "u_fVignette"), + Vignette); + shader_set_uniform_f( + shader_get_uniform(_shader, "u_vVignetteColor"), + color_get_red(VignetteColor) / 255.0, + color_get_green(VignetteColor) / 255.0, + color_get_blue(VignetteColor) / 255.0); + draw_surface( + _surface, + (Antialiasing == BBMOD_EAntialiasing.None) ? _x : 0, + (Antialiasing == BBMOD_EAntialiasing.None) ? _y : 0); + shader_reset(); + + if (Antialiasing != BBMOD_EAntialiasing.None) + { + // Reset surface... + surface_reset_target(); + matrix_set(matrix_world, _world); + _surFinal = __surPostProcess; + } + + //////////////////////////////////////////////////////////////////// + // Apply anti-aliasing to the final surface + if (Antialiasing == BBMOD_EAntialiasing.FXAA) + { + var _shader = BBMOD_ShFXAA; + shader_set(_shader); + shader_set_uniform_f( + shader_get_uniform(_shader, "u_vTexelVS"), + _texelWidth, _texelHeight); + shader_set_uniform_f( + shader_get_uniform(_shader, "u_vTexelPS"), + _texelWidth, _texelHeight); + draw_surface(_surFinal, _x, _y); + shader_reset(); + } + + gpu_pop_state(); + + return self; + }; + + static destroy = function () { + Class_destroy(); + + if (surface_exists(__surPostProcess)) + { + surface_free(__surPostProcess); + } + + return undefined; + }; +} diff --git a/scripts/BBMOD_PostProcessor/BBMOD_PostProcessor.yy b/scripts/BBMOD_PostProcessor/BBMOD_PostProcessor.yy new file mode 100644 index 000000000..aba3f127e --- /dev/null +++ b/scripts/BBMOD_PostProcessor/BBMOD_PostProcessor.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_PostProcessor", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "PostProcessing", + "path": "folders/_Extensions/BBMOD/Rendering/PostProcessing.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Property/BBMOD_Property.yy b/scripts/BBMOD_Property/BBMOD_Property.yy new file mode 100644 index 000000000..bbf0653fe --- /dev/null +++ b/scripts/BBMOD_Property/BBMOD_Property.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Property", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Properties", + "path": "folders/_Extensions/BBMOD/Core/Properties.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Property/bbmod_property.gml b/scripts/BBMOD_Property/bbmod_property.gml new file mode 100644 index 000000000..a4d2aa022 --- /dev/null +++ b/scripts/BBMOD_Property/bbmod_property.gml @@ -0,0 +1,28 @@ +/// @func BBMOD_Property(_name, _type) +/// +/// @extends BBMOD_Class +/// +/// @desc A descriptor of a serializable property. +/// +/// @param {String} _name The name of the property. +/// @param {Real} _type The type of the property. Use values from +/// {@link BBMOD_EPropertyType}. +/// +/// @see BBMOD_EPropertyType +function BBMOD_Property(_name, _type) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {String} The name of the property. + Name = _name; + + /// @var {Real} The type of the property. Use values from + /// {@link BBMOD_EPropertyType}. + /// @see BBMOD_EPropertyType + Type = _type; + + /// @var {Bool} If `true` then the property is private. Default value is + /// `false`. + Private = false; +} diff --git a/scripts/BBMOD_PunctualLight/BBMOD_PunctualLight.gml b/scripts/BBMOD_PunctualLight/BBMOD_PunctualLight.gml new file mode 100644 index 000000000..3305a7e6a --- /dev/null +++ b/scripts/BBMOD_PunctualLight/BBMOD_PunctualLight.gml @@ -0,0 +1,161 @@ +/// @var {Array} +/// @private +global.__bbmodPunctualLights = []; + +/// @func BBMOD_PunctualLight([_color[, _position[, _range]]]) +/// +/// @extends BBMOD_Light +/// +/// @desc Base struct for punctual lights. +/// +/// @param {Struct.BBMOD_Color} [_color] The light's color. Defaults +/// to {@link BBMOD_C_WHITE}. +/// @param {Struct.BBMOD_Vec3} [_position] The light's position. +/// Defaults to `(0, 0, 0)`. +/// @param {Real} [_range] The light's range. Defaults to 1. +/// +/// @see BBMOD_PointLight +/// @see BBMOD_SpotLight +function BBMOD_PunctualLight(_color=BBMOD_C_WHITE, _position=undefined, _range=1.0) + : BBMOD_Light() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_Color} The color of the light. Default value is + /// {@link BBMOD_C_WHITE}. + Color = _color; + + if (_position != undefined) + { + Position = _position; + } + + /// @var {Real} The range of the light. + Range = _range; +} + +/// @func bbmod_light_punctual_add(_light) +/// +/// @desc Adds a punctual light to be sent to shaders. +/// +/// @param {Struct.BBMOD_PunctualLight} _light The punctual light. +/// +/// @see bbmod_light_punctual_add +/// @see bbmod_light_punctual_count +/// @see bbmod_light_punctual_get +/// @see bbmod_light_punctual_remove +/// @see bbmod_light_punctual_remove_index +/// @see bbmod_light_punctual_clear +/// @see BBMOD_PunctualLight +function bbmod_light_punctual_add(_light) +{ + gml_pragma("forceinline"); + array_push(global.__bbmodPunctualLights, _light); +} + +/// @func bbmod_light_punctual_count() +/// +/// @desc Retrieves number of punctual lights added to be sent to shaders. +/// +/// @return {Real} The number of punctual lights added to be sent to shaders. +/// +/// @see bbmod_light_punctual_add +/// @see bbmod_light_punctual_get +/// @see bbmod_light_punctual_remove +/// @see bbmod_light_punctual_remove_index +/// @see bbmod_light_punctual_clear +/// @see BBMOD_PunctualLight +function bbmod_light_punctual_count() +{ + gml_pragma("forceinline"); + return array_length(global.__bbmodPunctualLights); +} + +/// @func bbmod_light_punctual_get(_index) +/// +/// @desc Retrieves a punctual light at given index. +/// +/// @param {Real} _index The index of the punctual light. +/// +/// @return {Struct.BBMOD_PunctualLight} The punctual light. +/// +/// @see bbmod_light_punctual_add +/// @see bbmod_light_punctual_count +/// @see bbmod_light_punctual_remove +/// @see bbmod_light_punctual_remove_index +/// @see bbmod_light_punctual_clear +/// @see BBMOD_PunctualLight +function bbmod_light_punctual_get(_index) +{ + gml_pragma("forceinline"); + return global.__bbmodPunctualLights[_index]; +} + +/// @func bbmod_light_punctual_remove(_light) +/// +/// @desc Removes a punctual light so it is not sent to shaders anymore. +/// +/// @param {Struct.BBMOD_PunctualLight} _light The punctual light to remove. +/// +/// @return {Bool} Returns `true` if the punctual light was removed or `false` if +/// the light was not found. +/// +/// @see bbmod_light_punctual_add +/// @see bbmod_light_punctual_count +/// @see bbmod_light_punctual_get +/// @see bbmod_light_punctual_remove_index +/// @see bbmod_light_punctual_clear +/// @see BBMOD_PunctualLight +function bbmod_light_punctual_remove(_light) +{ + gml_pragma("forceinline"); + var _punctualLights = global.__bbmodPunctualLights; + var i = 0; + repeat (array_length(_punctualLights)) + { + if (_punctualLights[i] == _light) + { + array_delete(_punctualLights, i, 1); + return true; + } + ++i; + } + return false; +} + +/// @func bbmod_light_punctual_remove_index(_index) +/// +/// @desc Removes a punctual light so it is not sent to shaders anymore. +/// +/// @param {Real} _index The index to remove the punctual light at. +/// +/// @return {Bool} Always returns `true`. +/// +/// @see bbmod_light_punctual_add +/// @see bbmod_light_punctual_count +/// @see bbmod_light_punctual_get +/// @see bbmod_light_punctual_remove +/// @see bbmod_light_punctual_clear +/// @see BBMOD_PunctualLight +function bbmod_light_punctual_remove_index(_index) +{ + gml_pragma("forceinline"); + array_delete(global.__bbmodPunctualLights, _index, 1); + return true; +} + +/// @func bbmod_light_punctual_clear() +/// +/// @desc Removes all punctual lights sent to shaders. +/// +/// @see bbmod_light_punctual_add +/// @see bbmod_light_punctual_count +/// @see bbmod_light_punctual_get +/// @see bbmod_light_punctual_remove +/// @see bbmod_light_punctual_remove_index +/// @see BBMOD_PunctualLight +function bbmod_light_punctual_clear() +{ + gml_pragma("forceinline"); + global.__bbmodPunctualLights = []; +} diff --git a/scripts/BBMOD_PunctualLight/BBMOD_PunctualLight.yy b/scripts/BBMOD_PunctualLight/BBMOD_PunctualLight.yy new file mode 100644 index 000000000..045d4d48c --- /dev/null +++ b/scripts/BBMOD_PunctualLight/BBMOD_PunctualLight.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_PunctualLight", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Lights", + "path": "folders/_Extensions/BBMOD/Core/Lights.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Quaternion/BBMOD_Quaternion.gml b/scripts/BBMOD_Quaternion/BBMOD_Quaternion.gml new file mode 100644 index 000000000..c50ac16b8 --- /dev/null +++ b/scripts/BBMOD_Quaternion/BBMOD_Quaternion.gml @@ -0,0 +1,604 @@ +/// @func BBMOD_Quaternion([_x, _y, _z, _w]) +/// +/// @desc A quaternion. +/// +/// @param {Real} [_x] The first component of the quaternion. Defaults to 0. +/// @param {Real} [_y] The second component of the quaternion. Defaults to 0. +/// @param {Real} [_z] The third component of the quaternion. Defaults to 0. +/// @param {Real} [_w] The fourth component of the quaternion. Defaults to 1. +/// +/// @note If you leave the arguments to their default values, then an identity +/// quaternion is created. +function BBMOD_Quaternion(_x=0.0, _y=0.0, _z=0.0, _w=1.0) constructor +{ + /// @var {Real} The first component of the quaternion. + X = _x; + + /// @var {Real} The second component of the quaternion. + Y = _y; + + /// @var {Real} The third component of the quaternion. + Z = _z; + + /// @var {Real} The fourth component of the quaternion. + W = _w; + + /// @func Add(_q) + /// + /// @desc Adds quaternions and returns the result as a new quaternion. + /// + /// @param {Struct.BBMOD_Quaternion} _q The other quaternion. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static Add = function (_q) { + gml_pragma("forceinline"); + return new BBMOD_Quaternion( + X + _q.X, + Y + _q.Y, + Z + _q.Z, + W + _q.W + ); + }; + + /// @func Clone() + /// + /// @desc Creates a clone of the quaternion. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static Clone = function () { + gml_pragma("forceinline"); + return new BBMOD_Quaternion(X, Y, Z, W); + }; + + /// @func Conjugate() + /// + /// @desc Conjugates the quaternion and returns the result as a quaternion. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static Conjugate = function () { + gml_pragma("forceinline"); + return new BBMOD_Quaternion(-X, -Y, -Z, W); + }; + + /// @func Copy(_dest) + /// + /// @desc Copies components of the quaternion into other quaternion. + /// + /// @param {Struct.BBMOD_Quaternion} _dest The destination quaternion. + /// + /// @return {Struct.BBMOD_Quaternion} Returns `self`. + static Copy = function (_dest) { + gml_pragma("forceinline"); + _dest.X = X; + _dest.Y = Y; + _dest.Z = Z; + _dest.W = W; + return self; + }; + + /// @func Dot(_q) + /// + /// @desc Computes a dot product of two dual quaternions. + /// + /// @param {Struct.BBMOD_Quaternion} _q The other quaternion. + /// + /// @return {Real} The dot product of the quaternions. + static Dot = function (_q) { + gml_pragma("forceinline"); + return ( + X * _q.X + + Y * _q.Y + + Z * _q.Z + + W * _q.W + ); + }; + + /// @func Exp() + /// + /// @desc Computes an exponential map of the quaternion and returns + /// the result as a new quaternion. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static Exp = function () { + gml_pragma("forceinline"); + var _length = Length(); + if (_length >= math_get_epsilon()) + { + var _sinc = Sinc(_length); + return new BBMOD_Quaternion( + X * _sinc, + Y * _sinc, + Z * _sinc, + exp(W) * cos(_length) + ); + } + return new BBMOD_Quaternion(0.0, 0.0, 0.0, exp(W)); + }; + + /// @func FromArray(_array[, _index]) + /// + /// @desc Loads quaternion components `(x, y, z, w)` from an array. + /// + /// @param {Array} _array The array to read the quaternion components + /// from. + /// @param {Real} [_index] The index to start reading the quaternion + /// components from. Defaults to 0. + /// + /// @return {Struct.BBMOD_Quaternion} Returns `self`. + static FromArray = function (_array, _index=0) { + gml_pragma("forceinline"); + X = _array[_index]; + Y = _array[_index + 1]; + Z = _array[_index + 2]; + W = _array[_index + 3]; + return self; + }; + + /// @func FromAxisAngle(_axis, _angle) + /// + /// @desc Initializes the quaternion using an axis and an angle. + /// + /// @param {Struct.BBMOD_Vec3} _axis The axis of rotaiton. + /// + /// @param {Real} _angle The rotation angle. + /// + /// @return {Struct.BBMOD_Quaternion} Returns `self`. + static FromAxisAngle = function (_axis, _angle) { + gml_pragma("forceinline"); + _angle = -_angle; + var _sinHalfAngle = dsin(_angle * 0.5); + X = _axis.X * _sinHalfAngle; + Y = _axis.Y * _sinHalfAngle; + Z = _axis.Z * _sinHalfAngle; + W = dcos(_angle * 0.5); + return self; + }; + + /// @func FromBuffer(_buffer, _type) + /// + /// @desc Loads quaternion components `(x, y, z, w)` from a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to read the quaternion components + /// from. + /// + /// @param {Constant.BufferDataType} [_type] The type of each component. + /// + /// @return {Struct.BBMOD_Quaternion} Returns `self`. + static FromBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + X = buffer_read(_buffer, _type); + Y = buffer_read(_buffer, _type); + Z = buffer_read(_buffer, _type); + W = buffer_read(_buffer, _type); + return self; + }; + + /// @func FromEuler(_x, _y, _z) + /// + /// @desc Initializes the quaternion using euler angles. + /// + /// @param {Real} _x The rotation around the X axis (in degrees). + /// @param {Real} _y The rotation around the Y axis (in degrees). + /// @param {Real} _z The rotation around the Z axis (in degrees). + /// + /// @return {Struct.BBMOD_Quaternion} Returns `self`. + /// + /// @note The order of rotations is YXZ, same as in the `matrix_build` + /// function. + static FromEuler = function (_x, _y, _z) { + gml_pragma("forceinline"); + + _x = -_x * 0.5; + _y = -_y * 0.5; + _z = -_z * 0.5; + + var _q1Sin, _q1Cos, _temp; + var _qX, _qY, _qZ, _qW; + + _q1Sin = dsin(_z); + _q1Cos = dcos(_z); + + _temp = dsin(_x); + + _qX = _q1Cos * _temp; + _qY = _q1Sin * _temp; + + _temp = dcos(_x); + + _qZ = _q1Sin * _temp; + _qW = _q1Cos * _temp; + + _q1Sin = dsin(_y); + _q1Cos = dcos(_y); + + X = _qX * _q1Cos - _qZ * _q1Sin; + Y = _qW * _q1Sin + _qY * _q1Cos; + Z = _qZ * _q1Cos + _qX * _q1Sin; + W = _qW * _q1Cos - _qY * _q1Sin; + + return self; + }; + + /// @func FromLookRotation(_forward, _up) + /// + /// @desc Initializes the quaternion using a forward and an up vector. These + /// vectors must not be parallel! If they are, the quaternion will be set to an + /// identity. + /// + /// @param {Struct.BBMOD_Vec3} _forward The vector facing forward. + /// @param {Struct.BBMOD_Vec3} _up The vector facing up. + /// + /// @return {Struct.BBMOD_Quaternion} Returns `self`. + static FromLookRotation = function (_forward, _up) { + gml_pragma("forceinline"); + + _forward = _forward.Clone(); + _up = _up.Clone(); + + if (!_forward.Orthonormalize(_up)) + { + X = 0.0; + Y = 0.0; + Z = 0.0; + W = 1.0; + return self; + } + + var _right = _up.Cross(_forward); + var _w = sqrt(1.0 + _right.X + _up.Y + _forward.Z) * 0.5; + var _w4Recip = 1.0 / (4.0 * _w); + + X = (_up.Z - _forward.Y) * _w4Recip; + Y = (_forward.X - _right.Z) * _w4Recip; + Z = (_right.Y - _up.X) * _w4Recip; + W = _w; + return self; + }; + + /// @func GetAngle() + /// + /// @desc Retrieves the rotation angle of the quaternion. + /// + /// @return {Real} The rotation angle. + static GetAngle = function () { + gml_pragma("forceinline"); + return radtodeg(arccos(W) * 2.0); + }; + + /// @func GetAxis() + /// + /// @desc Retrieves the axis of rotation of the quaternion. + /// + /// @return {Struct.BBMOD_Vec3} The axis of rotation. + static GetAxis = function () { + gml_pragma("forceinline"); + var _sinThetaInv = 1.0 / sin(arccos(W)); + return new BBMOD_Vec3( + X * _sinThetaInv, + Y * _sinThetaInv, + Z * _sinThetaInv + ); + }; + + /// @func Inverse() + /// + /// @desc Computes an inverse of the quaternion and returns the result + /// as a new quaternion. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static Inverse = function () { + gml_pragma("forceinline"); + return Conjugate().Scale(1.0 / Length()); + }; + + /// @func Length() + /// + /// @desc Computes the length of the quaternion. + /// + /// @return {Real} The length of the quaternion. + static Length = function () { + gml_pragma("forceinline"); + return sqrt( + X * X + + Y * Y + + Z * Z + + W * W + ); + }; + + /// @func LengthSqr() + /// + /// @desc Computes a squared length of the quaternion. + /// + /// @return {Real} The squared length of the quaternion. + static LengthSqr = function () { + gml_pragma("forceinline"); + return ( + X * X + + Y * Y + + Z * Z + + W * W + ); + }; + + /// @func Lerp(_q, _s) + /// + /// @desc Computes a linear interpolation of two quaternions + /// and returns the result as a new quaternion. + /// + /// @param {Struct.BBMOD_Quaternion} _q The other quaternion. + /// @param {Real} _s The interpolation factor. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static Lerp = function (_q, _s) { + gml_pragma("forceinline"); + return new BBMOD_Quaternion( + lerp(X, _q.X, _s), + lerp(Y, _q.Y, _s), + lerp(Z, _q.Z, _s), + lerp(W, _q.W, _s) + ); + }; + + /// @func Log() + /// + /// @desc Computes the logarithm map of the quaternion and returns the + /// result as a new quaternion. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static Log = function () { + gml_pragma("forceinline"); + var _length = Length(); + var _w = logn(2.71828, _length); + var _a = arccos(W / _length); + if (_a >= math_get_epsilon()) + { + var _mag = 1.0 / _length / Sinc(_a); + return new BBMOD_Quaternion( + X * _mag, + Y * _mag, + Z * _mag, + _w + ); + } + return new BBMOD_Quaternion(0.0, 0.0, 0.0, _w); + }; + + /// @func Mul(_q) + /// + /// @desc Multiplies two quaternions and returns the result as a new + /// quaternion. + /// + /// @param {Struct.BBMOD_Quaternion} _q The other quaternion. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static Mul = function (_q) { + gml_pragma("forceinline"); + return new BBMOD_Quaternion( + W * _q.X + X * _q.W + Y * _q.Z - Z * _q.Y, + W * _q.Y + Y * _q.W + Z * _q.X - X * _q.Z, + W * _q.Z + Z * _q.W + X * _q.Y - Y * _q.X, + W * _q.W - X * _q.X - Y * _q.Y - Z * _q.Z + ); + }; + + /// @func Normalize() + /// + /// @desc Normalizes the quaternion and returns the result as a new + /// quaternion. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static Normalize = function () { + gml_pragma("forceinline"); + var _lengthSqr = LengthSqr(); + if (_lengthSqr >= math_get_epsilon()) + { + return Scale(1.0 / sqrt(_lengthSqr)); + } + return Clone(); + }; + + /// @func Rotate(_v) + /// + /// @desc Rotates a vector using the quaternion and returns the result + /// as a new vector. + /// + /// @param {Struct.BBMOD_Vec3} _v The vector to rotate. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + static Rotate = function (_v) { + gml_pragma("forceinline"); + var _q = Normalize(); + var _V = new BBMOD_Quaternion(_v.X, _v.Y, _v.Z, 0.0); + var _rot = _q.Mul(_V).Mul(_q.Conjugate()); + return new BBMOD_Vec3(_rot.X, _rot.Y, _rot.Z); + }; + + /// @func Scale(_s) + /// + /// @desc Scales each component of the quaternion by a real value and + /// returns the result as a new quaternion. + /// + /// @param {Real} _s The value to scale the quaternion by. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static Scale = function (_s) { + gml_pragma("forceinline"); + return new BBMOD_Quaternion( + X * _s, + Y * _s, + Z * _s, + W * _s + ); + }; + + static Sinc = function (_x) { + gml_pragma("forceinline"); + return (_x >= math_get_epsilon()) ? (sin(_x) / _x) : 1.0; + }; + + /// @func Slerp(_q, _s) + /// + /// @desc Computes a spherical linear interpolation of two quaternions + /// and returns the result as a new quaternion. + /// + /// @param {Struct.BBMOD_Quaternion} _q The other quaternion. + /// @param {Real} _s The interpolation factor. + /// + /// @return {Struct.BBMOD_Quaternion} The created quaternion. + static Slerp = function (_q, _s) { + gml_pragma("forceinline"); + + var _q10 = X; + var _q11 = Y; + var _q12 = Z; + var _q13 = W; + + var _q20 = _q.X; + var _q21 = _q.Y; + var _q22 = _q.Z; + var _q23 = _q.W; + + var _norm; + + _norm = 1.0 / sqrt(_q10 * _q10 + + _q11 * _q11 + + _q12 * _q12 + + _q13 * _q13); + + _q10 *= _norm; + _q11 *= _norm; + _q12 *= _norm; + _q13 *= _norm; + + _norm = sqrt(_q20 * _q20 + + _q21 * _q21 + + _q22 * _q22 + + _q23 * _q23); + + _q20 *= _norm; + _q21 *= _norm; + _q22 *= _norm; + _q23 *= _norm; + + var _dot = _q10 * _q20 + + _q11 * _q21 + + _q12 * _q22 + + _q13 * _q23; + + if (_dot < 0.0) + { + _dot = -_dot; + _q20 *= -1.0; + _q21 *= -1.0; + _q22 *= -1.0; + _q23 *= -1.0; + } + + if (_dot > 0.9995) + { + return new BBMOD_Quaternion( + lerp(_q10, _q20, _s), + lerp(_q11, _q21, _s), + lerp(_q12, _q22, _s), + lerp(_q13, _q23, _s) + ); + } + + var _theta0 = arccos(_dot); + var _theta = _theta0 * _s; + var _sinTheta = sin(_theta); + var _sinTheta0 = sin(_theta0); + var _s2 = _sinTheta / _sinTheta0; + var _s1 = cos(_theta) - (_dot * _s2); + + return new BBMOD_Quaternion( + (_q10 * _s1) + (_q20 * _s2), + (_q11 * _s1) + (_q21 * _s2), + (_q12 * _s1) + (_q22 * _s2), + (_q13 * _s1) + (_q23 * _s2) + ); + }; + + /// @func ToArray([_array[, _index]]) + /// + /// @desc Writes components `(x, y, z, w)` of the quaternion into an array. + /// + /// @param {Array} [_array] The destination array. If not defined, a + /// new one is created. + /// @param {Real} [_index] The index to start writing to. Defaults to 0. + /// + /// @return {Array} Returns the destination array. + static ToArray = function (_array=undefined, _index=0) { + gml_pragma("forceinline"); + _array ??= array_create(4, 0.0); + _array[@ _index] = X; + _array[@ _index + 1] = Y; + _array[@ _index + 2] = Z; + _array[@ _index + 3] = W; + return _array; + }; + + /// @func ToBuffer(_buffer, _type) + /// + /// @desc Writes the quaternion into a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to write the quaternion to. + /// @param {Constant.BufferDataType} _type The type of each component. + /// + /// @return {Struct.BBMOD_Quaternion} Returns `self`. + static ToBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + buffer_write(_buffer, _type, X); + buffer_write(_buffer, _type, Y); + buffer_write(_buffer, _type, Z); + buffer_write(_buffer, _type, W); + return self; + }; + + /// @func ToMatrix([_dest[, _index]]) + /// + /// @desc Converts quaternion into a matrix. + /// + /// @param {Array} [_dest] The destination array. If not specified, a + /// new one is created. + /// @param {Real} [_index] The starting index in the destination array. + /// Defaults to 0. + /// + /// @return {Array} Returns the destination array. + static ToMatrix = function (_dest=undefined, _index=0) { + gml_pragma("forceinline"); + + _dest ??= matrix_build_identity(); + + var _temp0, _temp1, _temp2; + var _q0 = X; + var _q1 = Y; + var _q2 = Z; + var _q3 = W; + + _temp0 = _q0 * _q0; + _temp1 = _q1 * _q1; + _temp2 = _q2 * _q2; + _dest[@ _index] = 1.0 - 2.0 * (_temp1 + _temp2); + _dest[@ _index + 5] = 1.0 - 2.0 * (_temp0 + _temp2); + _dest[@ _index + 10] = 1.0 - 2.0 * (_temp0 + _temp1); + + _temp0 = _q0 * _q1; + _temp1 = _q3 * _q2; + _dest[@ _index + 1] = 2.0 * (_temp0 + _temp1); + _dest[@ _index + 4] = 2.0 * (_temp0 - _temp1); + + _temp0 = _q0 * _q2 + _temp1 = _q3 * _q1; + _dest[@ _index + 2] = 2.0 * (_temp0 - _temp1); + _dest[@ _index + 8] = 2.0 * (_temp0 + _temp1); + + _temp0 = _q1 * _q2; + _temp1 = _q3 * _q0; + _dest[@ _index + 6] = 2.0 * (_temp0 + _temp1); + _dest[@ _index + 9] = 2.0 * (_temp0 - _temp1); + + return _dest; + }; +} diff --git a/scripts/BBMOD_Quaternion/BBMOD_Quaternion.yy b/scripts/BBMOD_Quaternion/BBMOD_Quaternion.yy new file mode 100644 index 000000000..9780a1e78 --- /dev/null +++ b/scripts/BBMOD_Quaternion/BBMOD_Quaternion.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Quaternion", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Math", + "path": "folders/_Extensions/BBMOD/Core/Math.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_RandomRotationModule/BBMOD_RandomRotationModule.yy b/scripts/BBMOD_RandomRotationModule/BBMOD_RandomRotationModule.yy new file mode 100644 index 000000000..37f6baae2 --- /dev/null +++ b/scripts/BBMOD_RandomRotationModule/BBMOD_RandomRotationModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_RandomRotationModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rotation", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Rotation.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_RandomRotationModule/bbmod_randomrotationmodule.gml b/scripts/BBMOD_RandomRotationModule/bbmod_randomrotationmodule.gml new file mode 100644 index 000000000..b038cccfc --- /dev/null +++ b/scripts/BBMOD_RandomRotationModule/bbmod_randomrotationmodule.gml @@ -0,0 +1,38 @@ +/// @func BBMOD_RandomRotationModule([_axis[, _from[, _to]]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that randomly sets particles' rotation on their spawn. +/// +/// @param {Struct.BBMOD_Vec3} [_axis] The axis of rotation. Defaults to +/// {@link BBMOD_VEC3_UP}. +/// @param {Real} [_from] The minimum angle of rotation. Defaults to 0. +/// @param {Real} [_to] The maximum angle of rotation. Defaults to 360. +/// +/// @see BBMOD_EParticle.RotationX +/// @see BBMOD_EParticle.RotationY +/// @see BBMOD_EParticle.RotationZ +/// @see BBMOD_EParticle.RotationW +function BBMOD_RandomRotationModule(_axis=BBMOD_VEC3_UP, _from=0.0, _to=360.0) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_Vec3} The axis of rotation. Default value is + /// {@link BBMOD_VEC3_UP}. + Axis = _axis; + + /// @var {Real} The minimum angle of rotation. Default value is 0. + From = _from; + + /// @var {Real} The maximum angle of rotation. Default value is 360. + To = _to; + + static on_particle_start = function (_emitter, _particleIndex) { + var _rotation = new BBMOD_Quaternion().FromAxisAngle(Axis, random_range(From, To)); + _emitter.Particles[# BBMOD_EParticle.RotationX, _particleIndex] = _rotation.X; + _emitter.Particles[# BBMOD_EParticle.RotationY, _particleIndex] = _rotation.Y; + _emitter.Particles[# BBMOD_EParticle.RotationZ, _particleIndex] = _rotation.Z; + _emitter.Particles[# BBMOD_EParticle.RotationW, _particleIndex] = _rotation.W; + }; +} diff --git a/scripts/BBMOD_Ray/BBMOD_Ray.gml b/scripts/BBMOD_Ray/BBMOD_Ray.gml new file mode 100644 index 000000000..278482c41 --- /dev/null +++ b/scripts/BBMOD_Ray/BBMOD_Ray.gml @@ -0,0 +1,64 @@ +/// @func BBMOD_Ray(_origin, _direction) +/// +/// @desc A ray used to raycast colliders. +/// +/// @param {Struct.BBMOD_Vec3} _origin The ray's origin. +/// @param {Struct.BBMOD_Vec3} _direction The ray's direction. Should be +/// normalized! +/// +/// @see BBMOD_Collider.Raycast +function BBMOD_Ray(_origin, _direction) constructor +{ + /// @var {Struct.BBMOD_Vec3} The ray's origin. + Origin = _origin; + + /// @var {Struct.BBMOD_Vec3} The ray's direction. Should be normalized! + Direction = _direction; + + /// @func Raycast(_collider[, _result]) + /// + /// @desc Casts the ray against a collider. + /// + /// @param {Struct.BBMOD_Collider} _collider The collider to cast the ray + /// against. + /// @param {Struct.BBMOD_RaycastResult} [_result] Where to store + /// additional raycast info to or `undefined`. + /// + /// @return {Bool} Returns `true` if the ray hits the collider. + /// + /// @throws {BBMOD_NotImplementedException} If the collider does not + /// implement method `Raycast`. + /// + /// @note This is the same as calling `_collider.Raycast(_ray, _result)`! + /// + /// @see BBMOD_RaycastResult + /// @see BBMOD_Collider.Raycast + static Raycast = function (_collider, _result=undefined) { + gml_pragma("forceinline"); + return _collider.Raycast(self, _result); + }; + + /// @func DrawDebug([_length[, _color]]) + /// + /// @desc Draws a debug preview of the ray. + /// + /// @param {Real} [_length] The length of the ray. Defaults to 9999. + /// @param {Constant.Color} [_color] The debug color. Defaults to + /// `c_white`. + /// @param {Real} [_alpha] The debug alpha. Defaults to 1. + /// + /// @return {Struct.BBMOD_Ray} Returns `self`. + static DrawDebug = function (_length=9999.0, _color=c_white, _alpha=1.0) { + var _vbuffer = global.__bbmodVBufferDebug; + var _start = Origin; + var _end = _start.Add(Direction.Normalize().Scale(_length)); + + vertex_begin(_vbuffer, BBMOD_VFORMAT_DEBUG.Raw); + vertex_position_3d(_vbuffer, _start.X, _start.Y, _start.Z); vertex_color(_vbuffer, _color, _alpha); + vertex_position_3d(_vbuffer, _end.X, _end.Y, _end.Z); vertex_color(_vbuffer, _color, _alpha); + vertex_end(_vbuffer); + vertex_submit(_vbuffer, pr_linelist, -1); + + return self; + }; +} diff --git a/scripts/BBMOD_Ray/BBMOD_Ray.yy b/scripts/BBMOD_Ray/BBMOD_Ray.yy new file mode 100644 index 000000000..efcd34c03 --- /dev/null +++ b/scripts/BBMOD_Ray/BBMOD_Ray.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Ray", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Raycasting", + "path": "folders/_Extensions/BBMOD/Raycasting.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_RaycastResult/BBMOD_RaycastResult.gml b/scripts/BBMOD_RaycastResult/BBMOD_RaycastResult.gml new file mode 100644 index 000000000..ffeacdb3e --- /dev/null +++ b/scripts/BBMOD_RaycastResult/BBMOD_RaycastResult.gml @@ -0,0 +1,33 @@ +/// @func BBMOD_RaycastResult() +/// +/// @desc A structure for holding additional raycast data. +/// +/// @see BBMOD_Collider.Raycast +/// @see BBMOD_Ray.Raycast +function BBMOD_RaycastResult() constructor +{ + /// @var {Real} The distance from the ray's origin at which it has hit + /// the collider. + Distance = 0.0; + + /// @var {Struct.BBMOD_Vec3} The point of collision in world-space or + /// `undefined`. + Point = undefined; + + /// @var {Struct.BBMOD_Vec3} The normal vector at the collision or + /// `undefined`. + Normal = undefined; + + /// @func Reset() + /// + /// @desc Resets properties to their default values. + /// + /// @return {Struct.BBMOD_RaycastResult} Returns `self`. + static Reset = function () { + gml_pragma("forceinline"); + Distance = 0.0; + Point = undefined; + Normal = undefined; + return self; + }; +} diff --git a/scripts/BBMOD_RaycastResult/BBMOD_RaycastResult.yy b/scripts/BBMOD_RaycastResult/BBMOD_RaycastResult.yy new file mode 100644 index 000000000..650289b80 --- /dev/null +++ b/scripts/BBMOD_RaycastResult/BBMOD_RaycastResult.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_RaycastResult", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Raycasting", + "path": "folders/_Extensions/BBMOD/Raycasting.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_RenderQueue/BBMOD_RenderQueue.gml b/scripts/BBMOD_RenderQueue/BBMOD_RenderQueue.gml new file mode 100644 index 000000000..28f7e6003 --- /dev/null +++ b/scripts/BBMOD_RenderQueue/BBMOD_RenderQueue.gml @@ -0,0 +1,1845 @@ +/// @func bbmod_render_queues_get() +/// +/// @desc Retrieves a read-only array of existing render queues, sorted by +/// their priority in an asceding order. +/// +/// @return {Array} The array of render queues. +/// +/// @see BBMOD_RenderQueue +function bbmod_render_queues_get() +{ + gml_pragma("forceinline"); + static _renderQueues = []; + return _renderQueues; +} + +/// @func BBMOD_RenderQueue([_name[, _priority]]) +/// +/// @extends BBMOD_Class +/// +/// @desc A cointainer of render commands. +/// +/// @param {String} [_name] The name of the render queue. Defaults to +/// "RenderQueue" + number of created render queues - 1 (e.g. "RenderQueue0", +/// "RenderQueue1" etc.) if `undefined`. +/// @param {Real} [_priority] The priority of the render queue. Defaults to 0. +/// +/// @see bbmod_render_queue_get_default +/// @see BBMOD_ERenderCommand +function BBMOD_RenderQueue(_name=undefined, _priority=0) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + static IdNext = 0; + + /// @var {String} The name of the render queue. This can be useful for + /// debugging purposes. + Name = _name ?? ("RenderQueue" + string(IdNext++)); + + /// @var {Real} The priority of the render queue. Render queues with lower + /// priority come first in the array returned by {@link bbmod_render_queues_get}. + /// @readonly + Priority = _priority; + + /// @var {Id.DsList} + /// @see BBMOD_RenderCommand + /// @private + __renderCommands = ds_list_create(); + + /// @func set_priority(_p) + /// + /// @desc Changes the priority of the render queue. Render queues with lower + /// priority come first in the array returned by {@link bbmod_render_queues_get}. + /// + /// @param {Real} _p The new priority of the render queue. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_priority = function (_p) { + gml_pragma("forceinline"); + Priority = _p; + __bbmod_reindex_render_queues(); + return self; + }; + + /// @func apply_material(_material, _vertexFormat[, _enabledPasses]) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.ApplyMaterial} command into + /// the queue. + /// + /// @param {Struct.BBMOD_Material} _material The material to apply. + /// @param {Real} [_enabledPasses] Mask of enabled rendering passes. The + /// material will not be applied if the current rendering pass is not one + /// of them. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static apply_material = function (_material, _vertexFormat, _enabledPasses=~0) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.ApplyMaterial, + 3, + _vertexFormat, + _material, + _enabledPasses); + return self; + }; + + /// @func begin_conditional_block() + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.BeginConditionalBlock} command + /// into the queue. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static begin_conditional_block = function () { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.BeginConditionalBlock, + 0); + return self; + }; + + /// @func check_render_pass(_passes) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.CheckRenderPass} command into + /// the queue. + /// + /// @param {Real} [_passes] Mask of allowed rendering passes. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static check_render_pass = function (_passes) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.CheckRenderPass, + 1, + _passes); + return self; + }; + + /// @func draw_mesh(_vertexBuffer, _vertexFormat, _primitiveType, _materialIndex, _material, _matrix) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.DrawMesh} command into the + /// queue. + /// + /// @param {Id.VertexBuffer} _vertexBuffer The vertex buffer to draw. + /// @param {Struct.BBMOD_VertexFormat} _vertexFormat The format of the vertex buffer. + /// @param {Constant.PrimitiveType} _primitiveType The primitive type of + /// the mesh. + /// @param {Real} _materialIndex The material's index within the material array. + /// @param {Struct.BBMOD_Material} _material The material to use. + /// @param {Array} _matrix The world matrix. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static draw_mesh = function (_vertexBuffer, _vertexFormat, _primitiveType, _materialIndex, _material, _matrix) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.DrawMesh, + 7, + global.__bbmodInstanceID, + _vertexFormat, + _material, + _matrix, + _materialIndex, + _primitiveType, + _vertexBuffer); + return self; + }; + + /// @func draw_mesh_animated(_vertexBuffer, _vertexFormat, _primitiveType, _materialIndex, _material, _matrix, _boneTransform) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.DrawMeshAnimated} command into + /// the queue. + /// + /// @param {Id.VertexBuffer} _vertexBuffer The vertex buffer to draw. + /// @param {Struct.BBMOD_VertexFormat} _vertexFormat The format of the vertex buffer. + /// @param {Constant.PrimitiveType} _primitiveType The primitive type of + /// the mesh. + /// @param {Real} _materialIndex The material's index within the material array. + /// @param {Struct.BBMOD_Material} _material The material to use. + /// @param {Array} _matrix The world matrix. + /// @param {Array} _boneTransform An array with bone transformation + /// data. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static draw_mesh_animated = function (_vertexBuffer, _vertexFormat, _primitiveType, _materialIndex, _material, _matrix, _boneTransform) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.DrawMeshAnimated, + 8, + global.__bbmodInstanceID, + _vertexFormat, + _material, + _matrix, + _boneTransform, + _materialIndex, + _primitiveType, + _vertexBuffer); + return self; + }; + + /// @func draw_mesh_batched(_vertexBuffer, _vertexFormat, _primitiveType, _materialIndex, _material, _matrix, _batchData) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.DrawMeshBatched} command into + /// the queue. + /// + /// @param {Id.VertexBuffer} _vertexBuffer The vertex buffer to draw. + /// @param {Struct.BBMOD_VertexFormat} _vertexFormat The format of the vertex buffer. + /// @param {Constant.PrimitiveType} _primitiveType The primitive type of + /// the mesh. + /// @param {Real} _materialIndex The material's index within the material array. + /// @param {Struct.BBMOD_Material} _material The material to use. + /// @param {Array} _matrix The world matrix. + /// @param {Array, Array>} _batchData Either a single array + /// of batch data or an array of arrays of batch data. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static draw_mesh_batched = function (_vertexBuffer, _vertexFormat, _primitiveType, _materialIndex, _material, _matrix, _batchData) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.DrawMeshBatched, + 7, + (global.__bbmodInstanceIDBatch != undefined) + ? global.__bbmodInstanceIDBatch + : global.__bbmodInstanceID, + _vertexFormat, + _material, + _matrix, + _batchData, + _primitiveType, + _vertexBuffer); + return self; + }; + + /// @func end_conditional_block() + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.EndConditionalBlock} command + /// into the queue. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static end_conditional_block = function () { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.EndConditionalBlock, + 0); + return self; + }; + + /// @func pop_gpu_state() + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.PopGpuState} command into the + /// queue. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static pop_gpu_state = function () { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.PopGpuState, + 0); + return self; + }; + + /// @func push_gpu_state() + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.PushGpuState} command into the + /// queue. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static push_gpu_state = function () { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.PushGpuState, + 0); + return self; + }; + + /// @func reset_material() + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.ResetMaterial} command into the + /// queue. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static reset_material = function () { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.ResetMaterial, + 0); + return self; + }; + + /// @func reset_shader() + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.ResetShader} command into the + /// queue. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static reset_shader = function () { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.ResetShader, + 0); + return self; + }; + + /// @func set_gpu_alphatestenable(_enable) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuAlphaTestEnable} command + /// into the queue. + /// + /// @param {Bool} _enable Use `true` to enable alpha testing. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_alphatestenable = function (_enable) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuAlphaTestEnable, + 1, + _enable); + return self; + }; + + /// @func set_gpu_alphatestref(_value) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuAlphaTestRef} command + /// into the queue. + /// + /// @param {Real} _value The new alpha test threshold value. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_alphatestref = function (_value) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuAlphaTestRef, + 1, + _value); + return self; + }; + + /// @func set_gpu_blendenable(_enable) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuBlendEnable} command into + /// the queue. + /// + /// @param {Bool} _enable Use `true` to enable alpha blending. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_blendenable = function (_enable) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuBlendEnable, + 1, + _enable); + return self; + }; + + /// @func set_gpu_blendmode(_blendmode) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuBlendMode} command into + /// the queue. + /// + /// @param {Constant.BlendMode} _blendmode The new blend mode. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_blendmode = function (_blendmode) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuBlendMode, + 1, + _blendmode); + return self; + }; + + /// @func set_gpu_blendmode_ext(_src, _dest) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuBlendModeExt} command + /// into the queue. + /// + /// @param {Constant.BlendMode} _src Source blend mode. + /// @param {Constant.BlendMode} _dest Destination blend mode. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_blendmode_ext = function (_src, _dest) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuBlendModeExt, + 2, + _src, + _dest); + return self; + }; + + /// @func set_gpu_blendmode_ext_sepalpha(_src, _dest, _srcalpha, _destalpha) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuBlendModeExtSepAlpha} + /// command into the queue. + /// + /// @param {Constant.BlendMode} _src Source blend mode. + /// @param {Constant.BlendMode} _dest Destination blend mode. + /// @param {Constant.BlendMode} _srcalpha Blend mode for source alpha channel. + /// @param {Constant.BlendMode} _destalpha Blend mode for destination alpha + /// channel. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_blendmode_ext_sepalpha = function (_src, _dest, _srcalpha, _destalpha) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuBlendModeExtSepAlpha, + 4, + _src, + _dest, + _srcalpha, + _destalpha); + return self; + }; + + /// @func set_gpu_colorwriteenable(_red, _green, _blue, _alpha) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuColorWriteEnable} command + /// into the queue. + /// + /// @param {Bool} _red Use `true` to enable writing to the red color + /// channel. + /// @param {Bool} _green Use `true` to enable writing to the green color + /// channel. + /// @param {Bool} _blue Use `true` to enable writing to the blue color + /// channel. + /// @param {Bool} _alpha Use `true` to enable writing to the alpha channel. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_colorwriteenable = function (_red, _green, _blue, _alpha) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuColorWriteEnable, + 4, + _red, + _green, + _blue, + _alpha); + return self; + }; + + /// @func set_gpu_cullmode(_cullmode) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuCullMode} command into + /// the queue. + /// + /// @param {Constant.CullMode} _cullmode The new coll mode. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_cullmode = function (_cullmode) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuCullMode, + 1, + _cullmode); + return self; + }; + + /// @func set_gpu_fog(_enable, _color, _start, _end) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuFog} command into the + /// queue. + /// + /// @param {Bool} _enable Use `true` to enable fog. + /// @param {Constant.Color} _color The color of the fog. + /// @param {Real} _start The distance from the camera at which the fog + /// starts. + /// @param {Real} _end The distance from the camera at which the fog reaches + /// maximum intensity. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_fog = function (_enable, _color, _start, _end) { + gml_pragma("forceinline"); + if (_enable) + { + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuFog, + 4, + true, + _color, + _start, + _end); + } + else + { + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuFog, + 1, + false); + } + return self; + }; + + /// @func set_gpu_tex_filter(_linear) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexFilter} command into + /// the queue. + /// + /// @param {Bool} _linear Use `true` to enable linear texture filtering. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_filter = function (_linear) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexFilter, + 1, + _linear); + return self; + }; + + /// @func set_gpu_tex_filter_ext(_name, _linear) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexFilterExt} command + /// into the queue. + /// + /// @param {String} _name The name of the sampler. + /// @param {Bool} _linear Use `true` to enable linear texture filtering for + /// the sampler. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_filter_ext = function (_name, _linear) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexFilterExt, + 2, + _name, + _linear); + return self; + }; + + /// @func set_gpu_tex_max_aniso(_value) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMaxAniso} command into + /// the queue. + /// + /// @param {Real} _value The maximum level of anisotropy. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_max_aniso = function (_value) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMaxAniso, + 1, + _value); + return self; + }; + + /// @func set_gpu_tex_max_aniso_ext(_name, _value) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMaxAnisoExt} command + /// into the queue. + /// + /// @param {String} _name The name of the sampler. + /// @param {Real} _value The maximum level of anisotropy. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_max_aniso_ext = function (_name, _value) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMaxAnisoExt, + 2, + _name, + _value); + return self; + }; + + /// @func set_gpu_tex_max_mip(_value) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMaxMip} command into + /// the queue. + /// + /// @param {Real} _value The maximum mipmap level. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_max_mip = function (_value) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMaxMip, + 1, + _value); + return self; + }; + + /// @func set_gpu_tex_max_mip_ext(_name, _value) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMaxMipExt} command + /// into the queue. + /// + /// @param {String} _name The name of the sampler. + /// @param {Real} _value The maximum mipmap level. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_max_mip_ext = function (_name, _value) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMaxMipExt, + 2, + _name, + _value); + return self; + }; + + /// @func set_gpu_tex_min_mip(_value) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMinMip} command into + /// the queue. + /// + /// @param {Real} _value The minimum mipmap level. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_min_mip = function (_value) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMinMip, + 1, + _value); + return self; + }; + + /// @func set_gpu_tex_min_mip_ext(_name, _value) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMinMipExt} command + /// into the queue. + /// + /// @param {String} _name The name of the sampler. + /// @param {Real} _value The minimum mipmap level. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_min_mip_ext = function (_name, _value) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMinMipExt, + 2, + _name, + _value); + return self; + }; + + /// @func set_gpu_tex_mip_bias(_value) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMipBias} command into + /// the queue. + /// + /// @param {Real} _value The mipmap bias. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_mip_bias = function (_value) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMipBias, + 1, + _value); + return self; + }; + + /// @func set_gpu_tex_mip_bias_ext(_name, _value) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMipBiasExt} command + /// into the queue. + /// + /// @param {String} _name The name of the sampler. + /// @param {Real} _value The mipmap bias. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_mip_bias_ext = function (_name, _value) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMipBiasExt, + 2, + _name, + _value); + return self; + }; + + /// @func set_gpu_tex_mip_enable(_enable) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMipEnable} command + /// into the queue. + /// + /// @param {Bool} _enable Use `true` to enable mipmapping. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_mip_enable = function (_enable) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMipEnable, + 1, + _enable); + return self; + }; + + /// @func set_gpu_tex_mip_enable_ext(_name, _enable) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMipEnableExt} command + /// into the queue. + /// + /// @param {String} _name The name of the sampler. + /// @param {Bool} _enable Use `true` to enable mipmapping. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_mip_enable_ext = function (_name, _enable) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMipEnableExt, + 2, + _name, + _enable); + return self; + }; + + /// @func set_gpu_tex_mip_filter(_filter) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMipFilter} command + /// into the queue. + /// + /// @param {Constant.MipFilter} _filter The mipmap filter. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_mip_filter = function (_filter) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMipFilter, + 1, + _filter); + return self; + }; + + /// @func set_gpu_tex_mip_filter_ext(_name, _filter) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexMipFilterExt} command + /// into the queue. + /// + /// @param {String} _name The name of the sampler. + /// @param {Constant.MipFilter} _filter The mipmap filter. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_mip_filter_ext = function (_name, _filter) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexMipFilterExt, + 2, + _name, + _filter); + return self; + }; + + /// @func set_gpu_tex_repeat(_enable) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexRepeat} command into + /// the queue. + /// + /// @param {Bool} _enable Use `true` to enable texture repeat. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_repeat = function (_enable) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexRepeat, + 1, + _enable); + return self; + }; + + /// @func set_gpu_tex_repeat_ext(_name, _enable) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuTexRepeatExt} command + /// into the queue. + /// + /// @param {String} _name The name of the sampler. + /// @param {Bool} _enable Use `true` to enable texture repeat. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_tex_repeat_ext = function (_name, _enable) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuTexRepeatExt, + 2, + _name, + _enable); + return self; + }; + + /// @func set_gpu_zfunc(_func) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuZFunc} command into the + /// queue. + /// + /// @param {Constant.CmpFunc} _func The depth test function. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_zfunc = function (_func) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuZFunc, + 1, + _func); + return self; + }; + + /// @func set_gpu_ztestenable(_enable) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuZTestEnable} command into + /// the queue. + /// + /// @param {Bool} _enable Use `true` to enable testing against the detph + /// buffer. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_ztestenable = function (_enable) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuZTestEnable, + 1, + _enable); + return self; + }; + + /// @func set_gpu_zwriteenable(_enable) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetGpuZWriteEnable} command + /// into the queue. + /// + /// @param {Bool} _enable Use `true` to enable writing to the depth buffer. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_gpu_zwriteenable = function (_enable) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetGpuZWriteEnable, + 1, + _enable); + return self; + }; + + /// @func set_projection_matrix(_matrix) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetProjectionMatrix} command + /// into the queue. + /// + /// @param {Array} _matrix The new projection matrix. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_projection_matrix = function (_matrix) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetProjectionMatrix, + 1, + _matrix); + return self; + }; + + /// @func set_sampler(_nameOrIndex, _texture) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetSampler} command into the + /// queue. + /// + /// @param {String, Real} _nameOrIndex The name or index of the sampler. + /// @param {Pointer.Texture} _texture The new texture. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_sampler = function (_nameOrIndex, _texture) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetSampler, + 2, + _nameOrIndex, + _texture); + return self; + }; + + /// @func set_shader(_shader) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetShader} command into the + /// queue. + /// + /// @param {Asset.GMShader} _shader The shader to set. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_shader = function (_shader) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetShader, + 1, + _shader); + return self; + }; + + /// @func set_uniform_f(_name, _value) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformFloat} command into + /// the queue. + /// + /// @param {String} _name The name of the uniform. + /// @param {Real} _value The new uniform value. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_f = function (_name, _value) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformFloat, + 2, + _name, + _value); + return self; + }; + + /// @func set_uniform_f2(_name, _v1, _v2) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformFloat2} command into + /// the queue. + /// + /// @param {String} _name The name of the uniform. + /// @param {Real} _v1 The value of the first component. + /// @param {Real} _v2 The value of the second component. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_f2 = function (_name, _v1, _v2) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformFloat2, + 3, + _name, + _v1, + _v2); + return self; + }; + + /// @func set_uniform_f3(_name, _v1, _v2, _v3) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformFloat3} command into + /// the queue. + /// + /// @param {String} _name The name of the uniform. + /// @param {Real} _v1 The value of the first component. + /// @param {Real} _v2 The value of the second component. + /// @param {Real} _v3 The value of the third component. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_f3 = function (_name, _v1, _v2, _v3) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformFloat3, + 4, + _name, + _v1, + _v2, + _v3); + return self; + }; + + /// @func set_uniform_f4(_name, _v1, _v2, _v3, _v4) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformFloat4} command into + /// the queue. + /// + /// @param {String} _name The name of the uniform. + /// @param {Real} _v1 The value of the first component. + /// @param {Real} _v2 The value of the second component. + /// @param {Real} _v3 The value of the third component. + /// @param {Real} _v4 The value of the fourth component. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_f4 = function (_name, _v1, _v2, _v3, _v4) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformFloat4, + 5, + _name, + _v1, + _v2, + _v3, + _v4); + return self; + }; + + /// @func set_uniform_f_array(_name, _array) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformFloatArray} command + /// into the queue. + /// + /// @param {String} _name The name of the uniform. + /// @param {Array} _array The array of values. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_f_array = function (_name, _array) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformFloatArray, + 2, + _name, + _array); + return self; + }; + + /// @func set_uniform_i(_name, _value) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformInt} command into the + /// queue. + /// + /// @param {String} _name The name of the uniform. + /// @param {Real} _value The new uniform value. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_i = function (_name, _value) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformInt, + 2, + _name, + _value); + return self; + }; + + /// @func set_uniform_i2(_name, _v1, _v2) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformInt2} command into + /// the queue. + /// + /// @param {String} _name The name of the uniform. + /// @param {Real} _v1 The value of the first component. + /// @param {Real} _v2 The value of the second component. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_i2 = function (_name, _v1, _v2) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformInt2, + 3, + _name, + _v1, + _v2); + return self; + }; + + /// @func set_uniform_i3(_name, _v1, _v2, _v3) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformInt3} command into + /// the queue. + /// + /// @param {String} _name The name of the uniform. + /// @param {Real} _v1 The value of the first component. + /// @param {Real} _v2 The value of the second component. + /// @param {Real} _v3 The value of the third component. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_i3 = function (_name, _v1, _v2, _v3) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformInt3, + 4, + _name, + _v1, + _v2, + _v3); + return self; + }; + + /// @func set_uniform_i4(_name, _v1, _v2, _v3, _v4) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformInt4} command into + /// the queue. + /// + /// @param {String} _name The name of the uniform. + /// @param {Real} _v1 The value of the first component. + /// @param {Real} _v2 The value of the second component. + /// @param {Real} _v3 The value of the third component. + /// @param {Real} _v4 The value of the fourth component. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_i4 = function (_name, _v1, _v2, _v3, _v4) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformInt4, + 5, + _name, + _v1, + _v2, + _v3, + _v4); + return self; + }; + + /// @func set_uniform_i_array(_name, _array) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformIntArray} command + /// into the queue. + /// + /// @param {String} _name The name of the uniform. + /// @param {Array} _array The array of values. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_i_array = function (_name, _array) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformIntArray, + 2, + _name, + _array); + return self; + }; + + /// @func set_uniform_matrix(_name) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformMatrix} command into + /// the queue. + /// + /// @param {String} _name The name of the uniform. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_matrix = function (_name) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformMatrix, + 1, + _name); + return self; + }; + + /// @func set_uniform_matrix_array(_name, _array) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetUniformMatrixArray} command + /// into the queue. + /// + /// @param {String} _name The name of the uniform. + /// @param {Array} _array The array of values. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_uniform_matrix_array = function (_name, _array) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetUniformMatrixArray, + 2, + _name, + _array); + return self; + }; + + /// @func set_view_matrix(_matrix) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetViewMatrix} command into the + /// queue. + /// + /// @param {Array} _matrix The new view matrix. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_view_matrix = function (_matrix) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetViewMatrix, + 1, + _matrix); + return self; + }; + + /// @func set_world_matrix(_matrix) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SetWorldMatrix} command into + /// the queue. + /// + /// @param {Array} _matrix The new world matrix. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static set_world_matrix = function (_matrix) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SetWorldMatrix, + 1, + _matrix); + return self; + }; + + /// @func submit_vertex_buffer(_vertexBuffer, _prim, _texture) + /// + /// @desc Adds a {@link BBMOD_ERenderCommand.SubmitVertexBuffer} command + /// into the queue. + /// + /// @param {Id.VertexBuffer} _vertexBuffer The vertex buffer to submit. + /// @param {Constant.PrimitiveType} _prim Primitive type of the vertex + /// buffer. + /// @param {Pointer.Texture} _texture The texture to use. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + static submit_vertex_buffer = function (_vertexBuffer, _prim, _texture) { + gml_pragma("forceinline"); + ds_list_add( + __renderCommands, + BBMOD_ERenderCommand.SubmitVertexBuffer, + 3, + _vertexBuffer, + _prim, + _texture); + return self; + }; + + /// @func is_empty() + /// + /// @desc Checks whether the render queue is empty. + /// + /// @return {Bool} Returns `true` if there are no commands in the render + /// queue. + static is_empty = function () { + gml_pragma("forceinline"); + return ds_list_empty(__renderCommands); + }; + + /// @func submit([_instances]) + /// + /// @desc Submits render commands. + /// + /// @param {Id.DsList} [_instances] If specified then only + /// meshes with an instance ID from the list are submitted. Defaults to + /// `undefined`. + /// + /// @return {Struct.BBMOD_RenderQueue} Returns `self`. + /// + /// @see BBMOD_RenderQueue.clear + static submit = function (_instances=undefined) { + var i = 0; + var _renderCommands = __renderCommands; + var _renderCommandsCount = ds_list_size(_renderCommands); + var _condition = false; + + while (i < _renderCommandsCount) + { + var _command = _renderCommands[| i++]; + var _size = _renderCommands[| i++]; + + switch (_command) + { + case BBMOD_ERenderCommand.ApplyMaterial: + var _vertexFormat = _renderCommands[| i++]; + var _material = _renderCommands[| i++]; + var _enabledPasses = _renderCommands[| i++]; + if (((1 << bbmod_render_pass_get()) & _enabledPasses) == 0 + || !_material.apply(_vertexFormat)) + { + _condition = false; + continue; + } + break; + + case BBMOD_ERenderCommand.BeginConditionalBlock: + if (!_condition) + { + var _counter = 1; + while (_counter > 0) + { + var _commandInner = _renderCommands[| i++]; + var _sizeInner = _renderCommands[| i++]; + switch (_commandInner) + { + case BBMOD_ERenderCommand.BeginConditionalBlock: + ++_counter; + break; + + case BBMOD_ERenderCommand.EndConditionalBlock: + --_counter; + break; + } + i += _sizeInner; + } + } + break; + + case BBMOD_ERenderCommand.CheckRenderPass: + if (((1 << bbmod_render_pass_get()) & _renderCommands[| i++]) == 0) + { + _condition = false; + continue; + } + break; + + case BBMOD_ERenderCommand.DrawMesh: + var _id = _renderCommands[| i++]; + var _vertexFormat = _renderCommands[| i++]; + var _material = _renderCommands[| i++]; + if ((_instances != undefined && ds_list_find_index(_instances, _id) == -1) + || !_material.apply(_vertexFormat)) + { + i += 4; + _condition = false; + continue; + } + with (BBMOD_SHADER_CURRENT) + { + set_instance_id(_id); + matrix_set(matrix_world, _renderCommands[| i++]); + set_material_index(_renderCommands[| i++]); + } + var _primitiveType = _renderCommands[| i++]; + vertex_submit(_renderCommands[| i++], _primitiveType, _material.BaseOpacity); + break; + + case BBMOD_ERenderCommand.DrawMeshAnimated: + var _id = _renderCommands[| i++]; + var _vertexFormat = _renderCommands[| i++]; + var _material = _renderCommands[| i++]; + if ((_instances != undefined && ds_list_find_index(_instances, _id) == -1) + || !_material.apply(_vertexFormat)) + { + i += 5; + _condition = false; + continue; + } + with (BBMOD_SHADER_CURRENT) + { + set_instance_id(_id); + matrix_set(matrix_world, _renderCommands[| i++]); + set_bones(_renderCommands[| i++]); + set_material_index(_renderCommands[| i++]); + } + var _primitiveType = _renderCommands[| i++]; + vertex_submit(_renderCommands[| i++], _primitiveType, _material.BaseOpacity); + break; + + case BBMOD_ERenderCommand.DrawMeshBatched: + var _id = _renderCommands[| i++]; + var _vertexFormat = _renderCommands[| i++]; + var _material = _renderCommands[| i++]; + + if (!_material.apply(_vertexFormat)) + { + i += 4; + _condition = false; + continue; + } + + var _matrix = _renderCommands[| i++]; + var _batchData = _renderCommands[| i++]; + + //////////////////////////////////////////////////////////////// + // Filter batch data by instance ID + + if (_instances != undefined) + { + if (is_array(_id)) + { + var _hasInstances = false; + + if (is_array(_id[0])) + { + //////////////////////////////////////////////////// + // _id is an array of arrays of IDs + + _batchData = bbmod_array_clone(_batchData); + + var j = 0; + repeat (array_length(_id)) + { + var _idsCurrent = _id[j]; + var _idsCount = array_length(_idsCurrent); + var _dataCurrent = bbmod_array_clone(_batchData[j]); + _batchData[@ j] = _dataCurrent; + var _slotsPerInstance = array_length(_dataCurrent) / _idsCount; + var _hasData = false; + + var k = 0; + repeat (_idsCount) + { + if (ds_list_find_index(_instances, _idsCurrent[k]) == -1) + { + var l = 0; + repeat (_slotsPerInstance) + { + _dataCurrent[@ (k * _slotsPerInstance) + l] = 0.0; + ++l; + } + } + else + { + _hasData = true; + _hasInstances = true; + } + ++k; + } + + if (!_hasData) + { + // Filtered out all instances in _dataCurrent, + // we can remove it from _batchData + array_delete(_batchData, j, 1); + } + else + { + ++j; + } + } + } + else + { + //////////////////////////////////////////////////// + // _id is an array of IDs + + _batchData = bbmod_array_clone(_batchData); + + var _idsCurrent = _id; + var _idsCount = array_length(_idsCurrent); + var _dataCurrent = _batchData; + var _slotsPerInstance = array_length(_dataCurrent) / _idsCount; + + var k = 0; + repeat (_idsCount) + { + if (ds_list_find_index(_instances, _idsCurrent[k]) == -1) + { + var l = 0; + repeat (_slotsPerInstance) + { + _dataCurrent[@ (k * _slotsPerInstance) + l] = 0.0; + ++l; + } + } + else + { + _hasInstances = true; + } + ++k; + } + } + + if (!_hasInstances) + { + i += 2; + _condition = false; + continue; + } + } + else + { + //////////////////////////////////////////////////////// + // _id is a single ID + if (ds_list_find_index(_instances, _id) == -1) + { + i += 2; + _condition = false; + continue; + } + } + } + + //////////////////////////////////////////////////////////////// + + if (is_real(_id)) + { + BBMOD_SHADER_CURRENT.set_instance_id(_id); + } + + matrix_set(matrix_world, _matrix); + var _primitiveType = _renderCommands[| i++]; + var _vertexBuffer = _renderCommands[| i++]; + if (is_array(_batchData[0])) + { + var _dataIndex = 0; + repeat (array_length(_batchData)) + { + BBMOD_SHADER_CURRENT.set_batch_data(_batchData[_dataIndex++]); + vertex_submit(_vertexBuffer, _primitiveType, _material.BaseOpacity); + } + } + else + { + BBMOD_SHADER_CURRENT.set_batch_data(_batchData); + vertex_submit(_vertexBuffer, _primitiveType, _material.BaseOpacity); + } + break; + + case BBMOD_ERenderCommand.EndConditionalBlock: + // TODO: show_error("Found unmatching end of conditional block in render queue " + Name + "!", true); + break; + + case BBMOD_ERenderCommand.PopGpuState: + gpu_pop_state(); + break; + + case BBMOD_ERenderCommand.PushGpuState: + gpu_push_state(); + break; + + case BBMOD_ERenderCommand.ResetMaterial: + bbmod_material_reset(); + break; + + case BBMOD_ERenderCommand.ResetShader: + shader_reset(); + break; + + case BBMOD_ERenderCommand.SetGpuAlphaTestEnable: + gpu_set_alphatestenable(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuAlphaTestRef: + gpu_set_alphatestref(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuBlendEnable: + gpu_set_blendenable(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuBlendMode: + gpu_set_blendmode(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuBlendModeExt: + var _src = _renderCommands[| i++]; + var _dest = _renderCommands[| i++]; + gpu_set_blendmode_ext(_src, _dest); + break; + + case BBMOD_ERenderCommand.SetGpuBlendModeExtSepAlpha: + var _src = _renderCommands[| i++]; + var _dest = _renderCommands[| i++]; + var _srcalpha = _renderCommands[| i++]; + var _destalpha = _renderCommands[| i++]; + gpu_set_blendmode_ext_sepalpha(_src, _dest, _srcalpha, _destalpha); + break; + + case BBMOD_ERenderCommand.SetGpuColorWriteEnable: + var _red = _renderCommands[| i++]; + var _green = _renderCommands[| i++]; + var _blue = _renderCommands[| i++]; + var _alpha = _renderCommands[| i++]; + gpu_set_colorwriteenable(_red, _green, _blue, _alpha); + break; + + case BBMOD_ERenderCommand.SetGpuCullMode: + gpu_set_cullmode(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuFog: + if (_renderCommands[| i++]) + { + var _color = _renderCommands[| i++]; + var _start = _renderCommands[| i++]; + var _end = _renderCommands[| i++]; + gpu_set_fog(true, _color, _start, _end); + } + else + { + gpu_set_fog(false, c_black, 0, 1); + } + break; + + case BBMOD_ERenderCommand.SetGpuTexFilter: + gpu_set_tex_filter(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexFilterExt: + var _index = shader_get_sampler_index(shader_current(), _renderCommands[| i++]); + gpu_set_tex_filter_ext(_index, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMaxAniso: + gpu_set_tex_max_aniso(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMaxAnisoExt: + var _index = shader_get_sampler_index(shader_current(), _renderCommands[| i++]); + gpu_set_tex_max_aniso_ext(_index, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMaxMip: + gpu_set_tex_max_mip(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMaxMipExt: + var _index = shader_get_sampler_index(shader_current(), _renderCommands[| i++]); + gpu_set_tex_max_mip_ext(_index, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMinMip: + gpu_set_tex_min_mip(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMinMipExt: + var _index = shader_get_sampler_index(shader_current(), _renderCommands[| i++]); + gpu_set_tex_min_mip_ext(_index, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMipBias: + gpu_set_tex_mip_bias(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMipBiasExt: + var _index = shader_get_sampler_index(shader_current(), _renderCommands[| i++]); + gpu_set_tex_mip_bias_ext(_index, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMipEnable: + gpu_set_tex_mip_enable(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMipEnableExt: + var _index = shader_get_sampler_index(shader_current(), _renderCommands[| i++]); + gpu_set_tex_mip_enable_ext(_index, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMipFilter: + gpu_set_tex_mip_filter(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexMipFilterExt: + var _index = shader_get_sampler_index(shader_current(), _renderCommands[| i++]); + gpu_set_tex_mip_filter_ext(_index, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexRepeat: + gpu_set_tex_repeat(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuTexRepeatExt: + var _index = shader_get_sampler_index(shader_current(), _renderCommands[| i++]); + gpu_set_tex_repeat_ext(_index, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuZFunc: + gpu_set_zfunc(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuZTestEnable: + gpu_set_ztestenable(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetGpuZWriteEnable: + gpu_set_zwriteenable(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetProjectionMatrix: + matrix_set(matrix_projection, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetSampler: + var _nameOrIndex = _renderCommands[| i++]; + var _index = is_string(_nameOrIndex) + ? shader_get_sampler_index(shader_current(), _nameOrIndex) + : _nameOrIndex; + texture_set_stage(_index, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetShader: + shader_set(_renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetUniformFloat: + var _uniform = shader_get_uniform(shader_current(), _renderCommands[| i++]); + shader_set_uniform_f(_uniform, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetUniformFloat2: + var _uniform = shader_get_uniform(shader_current(), _renderCommands[| i++]); + var _v1 = _renderCommands[| i++]; + var _v2 = _renderCommands[| i++]; + shader_set_uniform_f(_uniform, _v1, _v2); + break; + + case BBMOD_ERenderCommand.SetUniformFloat3: + var _uniform = shader_get_uniform(shader_current(), _renderCommands[| i++]); + var _v1 = _renderCommands[| i++]; + var _v2 = _renderCommands[| i++]; + var _v3 = _renderCommands[| i++]; + shader_set_uniform_f(_uniform, _v1, _v2, _v3); + break; + + case BBMOD_ERenderCommand.SetUniformFloat4: + var _uniform = shader_get_uniform(shader_current(), _renderCommands[| i++]); + var _v1 = _renderCommands[| i++]; + var _v2 = _renderCommands[| i++]; + var _v3 = _renderCommands[| i++]; + var _v4 = _renderCommands[| i++]; + shader_set_uniform_f(_uniform, _v1, _v2, _v3, _v4); + break; + + case BBMOD_ERenderCommand.SetUniformFloatArray: + var _uniform = shader_get_uniform(shader_current(), _renderCommands[| i++]); + shader_set_uniform_f_array(_uniform, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetUniformInt: + var _uniform = shader_get_uniform(shader_current(), _renderCommands[| i++]); + shader_set_uniform_i(_uniform, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetUniformInt2: + var _uniform = shader_get_uniform(shader_current(), _renderCommands[| i++]); + var _v1 = _renderCommands[| i++]; + var _v2 = _renderCommands[| i++]; + shader_set_uniform_i(_uniform, _v1, _v2); + break; + + case BBMOD_ERenderCommand.SetUniformInt3: + var _uniform = shader_get_uniform(shader_current(), _renderCommands[| i++]); + var _v1 = _renderCommands[| i++]; + var _v2 = _renderCommands[| i++]; + var _v3 = _renderCommands[| i++]; + shader_set_uniform_i(_uniform, _v1, _v2, _v3); + break; + + case BBMOD_ERenderCommand.SetUniformInt4: + var _uniform = shader_get_uniform(shader_current(), _renderCommands[| i++]); + var _v1 = _renderCommands[| i++]; + var _v2 = _renderCommands[| i++]; + var _v3 = _renderCommands[| i++]; + var _v4 = _renderCommands[| i++]; + shader_set_uniform_i(_uniform, _v1, _v2, _v3, _v4); + break; + + case BBMOD_ERenderCommand.SetUniformIntArray: + var _uniform = shader_get_uniform(shader_current(), _renderCommands[| i++]); + shader_set_uniform_i_array(_uniform, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetUniformMatrix: + shader_set_uniform_matrix(shader_get_uniform(shader_current(), _renderCommands[| i++])); + break; + + case BBMOD_ERenderCommand.SetUniformMatrixArray: + var _uniform = shader_get_uniform(shader_current(), _renderCommands[| i++]); + shader_set_uniform_matrix_array(_uniform, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetViewMatrix: + matrix_set(matrix_view, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SetWorldMatrix: + matrix_set(matrix_world, _renderCommands[| i++]); + break; + + case BBMOD_ERenderCommand.SubmitVertexBuffer: + var _vertexBuffer = _renderCommands[| i++]; + var _prim = _renderCommands[| i++]; + var _texture = _renderCommands[| i++]; + vertex_submit(_vertexBuffer, _prim, _texture); + break; + } + + _condition = true; + } + + return self; + }; + + /// @func clear() + /// + /// @desc Clears the render queue. + /// + /// @return {Struct.BBMOD_Material} Returns `self`. + static clear = function () { + gml_pragma("forceinline"); + ds_list_clear(__renderCommands); + return self; + }; + + static destroy = function () { + Class_destroy(); + __bbmod_remove_render_queue(self); + return undefined; + }; + + __bbmod_add_render_queue(self); +} + +function __bbmod_add_render_queue(_renderQueue) +{ + gml_pragma("forceinline"); + static _renderQueues = bbmod_render_queues_get(); + array_push(_renderQueues, _renderQueue); + __bbmod_reindex_render_queues(); +} + +function __bbmod_remove_render_queue(_renderQueue) +{ + gml_pragma("forceinline"); + static _renderQueues = bbmod_render_queues_get(); + var _renderQueueCount = array_length(_renderQueues); + for (var i = 0; i < _renderQueueCount; ++i) + { + if (_renderQueues[i] == _renderQueue) + { + array_delete(_renderQueues, i, 1); + break; + } + } + __bbmod_reindex_render_queues(); +} + +function __bbmod_reindex_render_queues() +{ + gml_pragma("forceinline"); + static _renderQueues = bbmod_render_queues_get(); + static _sortFn = function (_a, _b) { + if (_b.Priority > _a.Priority) return -1; + if (_b.Priority < _a.Priority) return +1; + return 0; + }; + array_sort(_renderQueues, _sortFn); +} + +/// @func bbmod_render_queue_get_default() +/// +/// @desc Retrieves the default render queue. +/// +/// @return {Struct.BBMOD_RenderQueue} The default render queue. +/// +/// @see BBMOD_RenderQueue +function bbmod_render_queue_get_default() +{ + static _renderQueue = new BBMOD_RenderQueue("Default"); + return _renderQueue; +} diff --git a/scripts/BBMOD_RenderQueue/BBMOD_RenderQueue.yy b/scripts/BBMOD_RenderQueue/BBMOD_RenderQueue.yy new file mode 100644 index 000000000..9aaa3eaa3 --- /dev/null +++ b/scripts/BBMOD_RenderQueue/BBMOD_RenderQueue.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_RenderQueue", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Renderer/BBMOD_Renderer.gml b/scripts/BBMOD_Renderer/BBMOD_Renderer.gml new file mode 100644 index 000000000..fbc3387b4 --- /dev/null +++ b/scripts/BBMOD_Renderer/BBMOD_Renderer.gml @@ -0,0 +1,10 @@ +/// @func BBMOD_Renderer() +/// +/// @extends BBMOD_DefaultRenderer +/// +/// @deprecated Please use {@link BBMOD_DefaultRenderer} instead. +function BBMOD_Renderer() + : BBMOD_DefaultRenderer() constructor +{ + BBMOD_CLASS_GENERATED_BODY; +} diff --git a/scripts/BBMOD_Renderer/BBMOD_Renderer.yy b/scripts/BBMOD_Renderer/BBMOD_Renderer.yy new file mode 100644 index 000000000..4d430c82e --- /dev/null +++ b/scripts/BBMOD_Renderer/BBMOD_Renderer.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Renderer", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Deprecated", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Deprecated.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Resouce/BBMOD_Resouce.gml b/scripts/BBMOD_Resouce/BBMOD_Resouce.gml new file mode 100644 index 000000000..9753f5068 --- /dev/null +++ b/scripts/BBMOD_Resouce/BBMOD_Resouce.gml @@ -0,0 +1,286 @@ +/// @func BBMOD_Resource() +/// +/// @extends BBMOD_Class +/// +/// @desc Base struct for all BBMOD resources. +function BBMOD_Resource() + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Bool} If `false` then the resource has not been loaded yet. + /// @readonly + IsLoaded = false; + + /// @var {String} The path to the file from which was the resource + /// loaded, or `undefined` if the resource does not come from a file. + /// @readonly + Path = undefined; + + /// @var {Bool] If `true` then the resource is persistent and it is not + /// destroyed when method `free` is used. Default value is `false`. + /// @see BBMOD_Resource.free + /// @see BBMOD_ResourceManager.free + Persistent = false; + + /// @var {Struct.BBMOD_ResourceManager} The resource manager that loaded + /// the resource. + /// @private + __manager = undefined; + + /// @var {Real} Number of resource "lives". If it reaches 0, the resource is + /// destroyed. + /// @private + __counter = 1; + + /// @func from_buffer(_buffer) + /// + /// @desc Loads the resource from a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to load the resource from. + /// + /// @return {Struct.BBMOD_Resource} Returns `self`. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static from_buffer = function (_buffer) { + throw new BBMOD_NotImplementedException(); + // When implementing this method, do not forget to set IsLoaded to true! + //return self; + }; + + /// @func to_buffer(_buffer) + /// + /// @desc Writes the resource to a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to write the resource to. + /// + /// @return {Struct.BBMOD_Resource} Returns `self`. + /// + /// @throws {BBMOD_NotImplementedException} If the method is not implemented. + static to_buffer = function (_buffer) { + throw new BBMOD_NotImplementedException(); + // When implementing this method, do not forget to set IsLoaded to true! + //return self; + }; + + /// @func check_file(_file[, _sha1[, _callback]]) + /// + /// @param {String} _file + /// @param {String} [_sha1] + /// @param {Function} [_callback] + /// + /// @return {Bool} + /// + /// @throws {BBMOD_Exception} + /// + /// @private + static check_file = function (_file, _sha1=undefined, _callback=undefined) { + var _err = undefined; + + if (!file_exists(_file)) + { + _err = new BBMOD_Exception("File " + _file + " does not exist!"); + } + else if (_sha1 != undefined) + { + if (sha1_file(_file) != _sha1) + { + _err = new BBMOD_Exception("SHA1 does not match!"); + } + } + + if (_err != undefined) + { + if (_callback != undefined) + { + _callback(_err); + return false; + } + else + { + throw _err; + } + } + + return true; + }; + + /// @func from_file(_file[, _sha1]) + /// + /// @desc Loads the resource from a file. + /// + /// @param {String} _file The path to the file. + /// @param {String} [_sha1] Expected SHA1 of the file. If the actual one + /// does not match with this, then the resource will not be loaded. Use + /// `undefined` if you do not want to check the SHA1 of the file. + /// + /// @return {Struct.BBMOD_Resource} Returns `self`. + /// + /// @throws {BBMOD_Exception} If loading fails. + static from_file = function (_file, _sha1=undefined) { + Path = _file; + + check_file(_file, _sha1); + + var _buffer = buffer_load(_file); + buffer_seek(_buffer, buffer_seek_start, 0); + + try + { + from_buffer(_buffer); + buffer_delete(_buffer); + } + catch (_e) + { + buffer_delete(_buffer); + throw _e; + } + + return self; + }; + + /// @func to_file(_file) + /// + /// @desc Writes a resource to a file. + /// + /// @param {String} _file The path to the file. + /// + /// @return {Struct.BBMOD_Resource} Returns `self`. + /// + /// @throws {BBMOD_Exception} If saving fails. + static to_file = function (_file) { + var _buffer = buffer_create(1, buffer_grow, 1); + + var _dirname = filename_dir(_file); + if (!directory_exists(_dirname)) + { + directory_create(_dirname); + } + + try + { + to_buffer(_buffer); + buffer_save(_buffer, _file); + buffer_delete(_buffer); + } + catch (_e) + { + buffer_delete(_buffer); + throw _e; + } + + return self; + }; + + /// @func from_file_async(_file[, _sha1[, _callback]]) + /// + /// @desc Asynchronnously loads the resource from a file. + /// + /// @param {String} _file The path to the file. + /// @param {String} [_sha1] Expected SHA1 of the file. If the actual + /// one does not match with this, then the resource will not be loaded. Use + /// `undefined` if you do not want to check the SHA1 of the file. + /// @param {Function} [_callback] The function to execute when the + /// resource is loaded or if an error occurs or. It must take the error as the + /// first argument and the resource as the second argument. If no error occurs, + /// then `undefined` is passed. Defaults to `undefined`. + /// + /// @return {Struct.BBMOD_Resource} Returns `self`. + /// + /// @note Do not forget to call {@link bbmod_async_save_load_update} and + /// {@link bbmod_async_image_loaded_update} in appropriate events when using + /// asynchronnous loading! You can also use {@link BBMOD_ResourceManager} for + /// unified asynchronnous loading of resources. + static from_file_async = function (_file, _sha1=undefined, _callback=undefined) { + Path = _file; + + if (!check_file(_file, _sha1, _callback ?? bbmod_empty_callback)) + { + return self; + } + + var _resource = self; + var _struct = { + Resource: _resource, + Callback: _callback, + }; + + bbmod_buffer_load_async(_file, method(_struct, function (_err, _buffer) { + var _callback = Callback; + if (_err) + { + if (_callback != undefined) + { + _callback(_err, Resource); + } + return; + } + + try + { + Resource.from_buffer(_buffer); + } + catch (_err2) + { + if (_callback != undefined) + { + _callback(_err2, Resource); + } + return; + } + + if (_callback != undefined) + { + _callback(undefined, Resource); + } + })); + + return self; + }; + + /// @func ref() + /// + /// @desc Retrieves a reference to the resource. + /// + /// @return {Struct.BBMOD_Resource} Returns `self`. + static ref = function () { + gml_pragma("forceinline"); + if (!Persistent) + { + ++__counter; + } + return self; + }; + + /// @func free() + /// + /// @desc Releases a reference to the resource. + /// + /// @return {Bool} Returns `true` if there are no other references to the + /// resource and the resource is destroyed. + /// + /// @note This does not destroy the resource if it is persistent! + /// + /// @see BBMOD_Resource.Persistent + static free = function () { + gml_pragma("forceinline"); + if (!Persistent && --__counter == 0) + { + destroy(); + return true; + } + return false; + }; + + static destroy = function () { + Class_destroy(); + if (__manager != undefined) + { + ds_map_delete(__manager.__resources, Path); + __manager = undefined; + } + return undefined; + }; +} diff --git a/scripts/BBMOD_Resouce/BBMOD_Resouce.yy b/scripts/BBMOD_Resouce/BBMOD_Resouce.yy new file mode 100644 index 000000000..edc6dca79 --- /dev/null +++ b/scripts/BBMOD_Resouce/BBMOD_Resouce.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Resouce", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Core", + "path": "folders/_Extensions/BBMOD/Core.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_ResourceManager/BBMOD_ResourceManager.gml b/scripts/BBMOD_ResourceManager/BBMOD_ResourceManager.gml new file mode 100644 index 000000000..7a0dd745a --- /dev/null +++ b/scripts/BBMOD_ResourceManager/BBMOD_ResourceManager.gml @@ -0,0 +1,423 @@ +/// @macro {Struct.BBMOD_ResourceManager} The default resource manager. +/// @note This resoure manager should never be destroyed! +#macro BBMOD_RESOURCE_MANAGER __bbmod_resource_manager() + +/// @func BBMOD_ResourceManager() +/// +/// @extends BBMOD_Class +/// +/// @desc Using this struct you can easily load, retrieve and free from memory +/// any BBMOD resources. +/// +/// @example +/// Create a resource manager in `OMain`: +/// ```gml +/// /// @desc Create event +/// resourceManager = new BBMOD_ResourceManager(); +/// +/// /// @desc Clean Up event +/// resourceManager = resourceManager.destroy(); +/// +/// /// @desc Async - Image Loaded event +/// resourceManager.async_image_loaded_update(async_load); +/// +/// /// @desc Async - Save/Load event +/// resourceManager.async_save_load_update(async_load); +/// ``` +/// +/// Use the resource manager in another object to load its model or just +/// retrieve a reference to it, if it is already loaded. +/// ```gml +/// /// @desc Create event +/// model = OMain.resourceManager.load("Data/Model.bbmod"); +/// +/// /// @desc Clean Up event +/// // Free the reference. This is not necessary if you do not want to unload +/// // the model when all instances are destroyed. It will always be unloaded +/// // when the resource manager is destroyed. +/// model.free(); +/// +/// /// @desc Draw event +/// matrix_set(matrix_world, matrix_build(x, y, z, 0, 0, 0, 1, 1, 1)); +/// model.render(); +/// ``` +function BBMOD_ResourceManager() + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Id.DsMap} + /// @private + __resources = ds_map_create(); + + /// @var {Real} Number of resources that are currently loading. + /// @readonly + Loading = 0; + + /// @func add(_uniqueName, _resource) + /// + /// @desc Adds a resource to the resource manager. + /// + /// @param {String} _uniqueName The name of the resource. Must be unique! + /// @param {Struct.BBMOD_Resource} _resource The resource to add. + /// + /// @return {Struct.BBMOD_ResourceManager} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the resource is already added to a manager. + static add = function (_uniqueName, _resource) { + gml_pragma("forceinline"); + if (_resource.__manager != undefined) + { + throw new BBMOD_Exception("Resource is already added to a manager!"); + } + __resources[? _uniqueName] = _resource; + _resource.__manager = self; + return self; + }; + + /// @func has(_pathOrUniqueName) + /// + /// @desc Checks if the resource manager has a resource. + /// + /// @param {String} _pathOrUniqueName The path to the resource file or + /// unique name of the resource. + /// + /// @return {Bool} Returns `true` if the resource manager has the resource. + static has = function (_pathOrUniqueName) { + gml_pragma("forceinline"); + return ds_map_exists(__resources, _pathOrUniqueName); + }; + + /// @func get(_pathOrUniqueName) + /// + /// @desc Retrieves a reference to a resource. + /// + /// @param {String} _pathOrUniqueName The path to the resource file or + /// unique name of the resource. + /// + /// @return {Struct.BBMOD_Resource} The resource. + /// + /// @see BBMOD_ResourceManager.has + /// + /// @throws {BBMOD_Exception} If the resource manager does not have such + /// resource. + static get = function (_pathOrUniqueName) { + gml_pragma("forceinline"); + if (!ds_map_exists(__resources, _pathOrUniqueName)) + { + throw new BBMOD_Exception("Resource not found!"); + } + return __resources[? _pathOrUniqueName].ref(); + }; + + /// @func get_or_add(_uniqueName, _onAdd) + /// + /// @desc Retrieves a reference to a resource. If the resource does not + /// exist yet, then it is added. + /// + /// @param {String} _uniqueName The name of the resource. Must be unique! + /// @param {Function} _onAdd A function which creates the resource if it + /// does not exist yet. Must take no arguments and must return the created + /// resource. + /// + /// @example + /// Following code shows Create event of an object, where it assings its + /// material using this method. When the first instance is created, it + /// creates the material and adds it to the resource manager. When other + /// instances are created, the material already exists and so they only + /// get a reference to it. + /// + /// ```gml + /// /// @desc Create event + /// material = resourceManager.get_or_add("material", function () { + /// var _mat = BBMOD_MATERIAL_DEFAULT.clone(); + /// _mat.BaseOpacity = sprite_get_texture(SprBaseOpacity, 0); + /// return _mat; + /// }); + /// ``` + static get_or_add = function (_uniqueName, _onAdd) { + gml_pragma("forceinline"); + if (ds_map_exists(__resources, _uniqueName)) + { + return __resources[? _uniqueName].ref(); + } + var _res = _onAdd(); + add(_uniqueName, _res); + return _res; + }; + + /// @func load(_path[, _sha1[, _onLoad]]) + /// + /// @desc Asynchronnously loads a resource from a file or retrieves + /// a reference to it, if it is already loaded. + /// + /// @param {String} _path The path to the resource. + /// @param {String} [_sha1] Expected SHA1 of the file. If the actual + /// one does not match with this, then the resource will not be loaded. Use + /// `undefined` if you do not want to check the SHA1 of the file. + /// @param {Function} [_onLoad] A function to execute when the + /// resource is loaded or if an error occurs while loading it. It must take + /// the error as the first argument and the resource as the second argument. + /// If no error occurs, then `undefined` is passed. If the resource was already + /// loaded when calling this function, then this callback is not executed. + /// + /// @return {Struct.BBMOD_Resource} The resource or `undefined`. + /// + /// @note Currently supported files formats are `*.bbmod` for {@link BBMOD_Model}, + /// `*.bbanim` for {@link BBMOD_Animation}, `*.bbmat` for {@link BBMOD_Material} + /// and `*.png`, `*.gif`, `*.jpg/jpeg` for {@link BBMOD_Sprite}. + static load = function (_path, _sha1=undefined, _onLoad=undefined) { + var _resources = __resources; + + if (ds_map_exists(_resources, _path)) + { + return _resources[? _path].ref(); + } + + var _ext = filename_ext(_path); + var _res; + + //////////////////////////////////////////////////////////////////////// + // BBMAT + if (_ext == ".bbmat") + { + // Check SHA1 + if (_sha1 != undefined) + { + if (sha1_file(_path) != _sha1) + { + if (_onLoad != undefined) + { + _onLoad(new BBMOD_Exception("SHA1 does not match!"), undefined); + } + return undefined; + } + } + + // Load JSON + var _json = bbmod_json_load(_path); + + // Check if the material is registered + var _materialName = _json[$ "__MaterialName"]; + + if (_materialName == undefined + || !bbmod_material_exists(_materialName)) + { + if (_onLoad != undefined) + { + _onLoad(new BBMOD_Exception("Material \"" + _materialName + "\" does not exist!"), undefined); + } + return undefined; + } + + // Load textures + var _textures = _json[$ "__Textures"]; + + if (_textures != undefined) + { + var _pathAbsolute = bbmod_path_get_absolute(_path); + var _propertyNames = variable_struct_get_names(_textures); + var _index = 0; + + repeat (array_length(_propertyNames)) + { + var _property = _propertyNames[_index++]; + var _propertyValue = _textures[$ _property]; + + var _texturePath; + var _textureSha1 = undefined; + + if (is_string(_propertyValue)) + { + _texturePath = _propertyValue; + } + else + { + _texturePath = _propertyValue.Path; + _textureSha1 = _propertyValue[$ "SHA1"]; + } + + _texturePath = bbmod_path_get_absolute(_texturePath, filename_dir(_pathAbsolute)); + + var _sprite; + if (has(_texturePath)) + { + _sprite = get(_texturePath); + } + else + { + _sprite = new BBMOD_Sprite(_texturePath, _textureSha1); + add(_texturePath, _sprite); + } + + _json[$ _property] = _sprite.get_texture(); + } + } + + // Create the material and apply props. + _res = bbmod_material_get(_materialName).clone().from_json(_json); + _resources[? _path] = _res; + + if (_onLoad != undefined) + { + _onLoad(undefined, _res); + } + + return _res; + } + + //////////////////////////////////////////////////////////////////////// + // Others... + switch (_ext) + { + case ".bbmod": + _res = new BBMOD_Model(); + break; + + case ".bbanim": + _res = new BBMOD_Animation(); + break; + + case ".png": + case ".gif": + case ".jpg": + case ".jpeg": + _res = new BBMOD_Sprite(); + break; + + default: + _onLoad(new BBMOD_Exception("Invalid file extension '" + _ext + "'!")); + return undefined; + } + + _res.__manager = self; + var _manager = self; + var _struct = { + __manager: _manager, + Callback: _onLoad, + }; + ++Loading; + _res.from_file_async(_path, _sha1, method(_struct, function (_err, _res) { + --__manager.Loading; + if (Callback != undefined) + { + Callback(_err, _res); + } + })); + _resources[? _path] = _res; + + return _res; + }; + + /// @func free(_resourceOrPath) + /// + /// @desc Frees a reference to the resource. When there are no other no other + /// references, the resource is destroyed. + /// + /// @param {Struct.BBMOD_Resource, String} _resourceOrPath Either a resource + /// or a path (string). + /// + /// @return {Struct.BBMOD_ResourceManager} Returns `self`. + static free = function (_resourceOrPath) { + // Note: Resource removes itself from the map + var _resources = __resources; + if (is_struct(_resourceOrPath)) + { + _resourceOrPath.free(); + } + else + { + _resources[? _resourceOrPath].free(); + } + return self; + }; + + /// @func clear() + /// + /// @desc Destroys all non-persistent resources. + /// + /// @return {Struct.BBMOD_ResourceManager} Returns `self`. + /// + /// @see BBMOD_Resource.Persistent + static clear = function () { + var _resources = __resources; + var _key = ds_map_find_first(_resources); + repeat (ds_map_size(_resources)) + { + var _keyNext = ds_map_find_next(_resources, _key); + var _res = _resources[? _key]; + if (!_res.Persistent) + { + _res.destroy(); + } + _key = _keyNext; + } + return self; + }; + + /// @func async_image_loaded_update(_asyncLoad) + /// + /// @desc Must be executed in the "Async - Image Loaded" event! + /// + /// @param {Id.DsMap} _asyncLoad The `async_load` map. + /// + /// @return {Struct.BBMOD_ResourceManager} Returns `self`. + /// + /// @note This calls {@link bbmod_async_image_loaded_update}, so you do not + /// need to call it again! + /// + /// @see bbmod_async_image_loaded_update + static async_image_loaded_update = function (_asyncLoad) { + gml_pragma("forceinline"); + bbmod_async_image_loaded_update(_asyncLoad); + return self; + }; + + /// @func async_save_load_update(_asyncLoad) + /// + /// @desc Must be executed in the "Async - Save/Load" event! + /// + /// @param {Id.DsMap} _asyncLoad The `async_load` map. + /// + /// @return {Struct.BBMOD_ResourceManager} Returns `self`. + /// + /// @note This calls {@link bbmod_async_image_loaded_update}, so you do not + /// need to call it again! + /// + /// @see bbmod_async_image_loaded_update + static async_save_load_update = function (_asyncLoad) { + gml_pragma("forceinline"); + bbmod_async_save_load_update(_asyncLoad); + return self; + }; + + static destroy = function () { + Class_destroy(); + var _resources = __resources; + var _key = ds_map_find_first(_resources); + repeat (ds_map_size(_resources)) + { + var _res = _resources[? _key]; + _res.__manager = undefined; // To not remove from the map, we destroy it anyways... + _res.destroy(); + _key = ds_map_find_next(_resources, _key); + } + ds_map_destroy(_resources); + return undefined; + }; +} + + +/// @func __bbmod_resource_manager() +/// +/// @return {Struct.BBMOD_ResourceManager} +/// +/// @private +function __bbmod_resource_manager() +{ + gml_pragma("forceinline"); + static _resourceManager = new BBMOD_ResourceManager(); + return _resourceManager; +} diff --git a/scripts/BBMOD_ResourceManager/BBMOD_ResourceManager.yy b/scripts/BBMOD_ResourceManager/BBMOD_ResourceManager.yy new file mode 100644 index 000000000..cbb5478ea --- /dev/null +++ b/scripts/BBMOD_ResourceManager/BBMOD_ResourceManager.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_ResourceManager", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Core", + "path": "folders/_Extensions/BBMOD/Core.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_SetColorModule/BBMOD_SetColorModule.yy b/scripts/BBMOD_SetColorModule/BBMOD_SetColorModule.yy new file mode 100644 index 000000000..f2b8e5899 --- /dev/null +++ b/scripts/BBMOD_SetColorModule/BBMOD_SetColorModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_SetColorModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "SetProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/SetProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_SetColorModule/bbmod_setcolormodule.gml b/scripts/BBMOD_SetColorModule/bbmod_setcolormodule.gml new file mode 100644 index 000000000..bda67e76a --- /dev/null +++ b/scripts/BBMOD_SetColorModule/bbmod_setcolormodule.gml @@ -0,0 +1,37 @@ +/// @func BBMOD_SetColorModule([_property[, _value]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that sets initial value of particles' +/// color property when they are spawned. +/// +/// @param {Real} [_property] The first of the four properties that together +/// form a color. Use values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Color} [_value] The initial value of the color +/// property. Defaults to {@link BBMOD_C_WHITE}. +/// +/// @see BBMOD_EParticle +function BBMOD_SetColorModule(_property=undefined, _value=BBMOD_C_WHITE) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four properties that together form a color. + /// Use values from {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Color} The initial value of the color + /// property. Default value is {@link BBMOD_C_WHITE}. + Value = _value; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + var _value = Value; + _emitter.Particles[# Property, _particleIndex] = _value.Red; + _emitter.Particles[# Property + 1, _particleIndex] = _value.Green; + _emitter.Particles[# Property + 2, _particleIndex] = _value.Blue; + _emitter.Particles[# Property + 3, _particleIndex] = _value.Alpha; + } + }; +} diff --git a/scripts/BBMOD_SetQuaternionModule/BBMOD_SetQuaternionModule.yy b/scripts/BBMOD_SetQuaternionModule/BBMOD_SetQuaternionModule.yy new file mode 100644 index 000000000..598accd63 --- /dev/null +++ b/scripts/BBMOD_SetQuaternionModule/BBMOD_SetQuaternionModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_SetQuaternionModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "SetProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/SetProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_SetQuaternionModule/bbmod_setquaternionmodule.gml b/scripts/BBMOD_SetQuaternionModule/bbmod_setquaternionmodule.gml new file mode 100644 index 000000000..e5e644bbf --- /dev/null +++ b/scripts/BBMOD_SetQuaternionModule/bbmod_setquaternionmodule.gml @@ -0,0 +1,39 @@ +/// @func BBMOD_SetQuaternionModule([_property[, _value]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that sets initial value of particles' +/// quaternion property when they are spawned. +/// +/// @param {Real} [_property] The first of the four properties that together +/// form a quaternion. Use values from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Quaternion} [_value] The initial value of the quaternion +/// property. Defaults to an identity quaternion. +/// +/// @see BBMOD_EParticle +function BBMOD_SetQuaternionModule( + _property=undefined, + _value=new BBMOD_Quaternion() +) : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first of the four properties that together form a + /// quaternion. Use values from {@link BBMOD_EParticle}. Defaults to `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Quaternion} The initial value of the quaternion property. + /// Default value is an idenitity quaternion. + Value = _value; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + var _value = Value; + _emitter.Particles[# Property, _particleIndex] = _value.X; + _emitter.Particles[# Property + 1, _particleIndex] = _value.Y; + _emitter.Particles[# Property + 2, _particleIndex] = _value.Z; + _emitter.Particles[# Property + 3, _particleIndex] = _value.W; + } + }; +} diff --git a/scripts/BBMOD_SetRealModule/BBMOD_SetRealModule.yy b/scripts/BBMOD_SetRealModule/BBMOD_SetRealModule.yy new file mode 100644 index 000000000..9c65e1c4f --- /dev/null +++ b/scripts/BBMOD_SetRealModule/BBMOD_SetRealModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_SetRealModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "SetProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/SetProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_SetRealModule/bbmod_setrealmodule.gml b/scripts/BBMOD_SetRealModule/bbmod_setrealmodule.gml new file mode 100644 index 000000000..d31786c70 --- /dev/null +++ b/scripts/BBMOD_SetRealModule/bbmod_setrealmodule.gml @@ -0,0 +1,31 @@ +/// @func BBMOD_SetRealModule([_property[, _value]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that sets initial value of particles' +/// property when they are spawned. +/// +/// @param {Real} [_property] The property to set initial value of. Use values +/// from {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Real} [_value] The initial value of the property. Defaults to 0.0. +/// +/// @see BBMOD_EParticle +function BBMOD_SetRealModule(_property=undefined, _value=0.0) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The property to set initial value of. Use values from + /// {@link BBMOD_EParticle}. Default value is `undefined`. + Property = _property; + + /// @var {Real} The initial value of the property. Defaults to 0.0. + Value = _value; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + _emitter.Particles[# Property, _particleIndex] = Value; + } + }; +} diff --git a/scripts/BBMOD_SetVec2Module/BBMOD_SetVec2Module.yy b/scripts/BBMOD_SetVec2Module/BBMOD_SetVec2Module.yy new file mode 100644 index 000000000..6a9c329c8 --- /dev/null +++ b/scripts/BBMOD_SetVec2Module/BBMOD_SetVec2Module.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_SetVec2Module", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "SetProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/SetProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_SetVec2Module/bbmod_setvec2module.gml b/scripts/BBMOD_SetVec2Module/bbmod_setvec2module.gml new file mode 100644 index 000000000..2a0de29bd --- /dev/null +++ b/scripts/BBMOD_SetVec2Module/bbmod_setvec2module.gml @@ -0,0 +1,35 @@ +/// @func BBMOD_SetVec2Module([_property[, _value]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that sets initial value of two consecutive +/// particle properties when it is spawned. +/// +/// @param {Real} [_property] The first property. Use values from +/// {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec2} [_value] The initial value of the properties. +/// Defaults to `(0.0, 0.0)`. +/// +/// @see BBMOD_EParticle +function BBMOD_SetVec2Module(_property=undefined, _value=new BBMOD_Vec2()) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first property. Use values from {@link BBMOD_EParticle}. + /// Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec2} The initial value of the properties. Default + /// value is `(0.0, 0.0)`. + Value = _value; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + var _value = Value; + _emitter.Particles[# Property, _particleIndex] = _value.X; + _emitter.Particles[# Property + 1, _particleIndex] = _value.Y; + } + }; +} diff --git a/scripts/BBMOD_SetVec3Module/BBMOD_SetVec3Module.yy b/scripts/BBMOD_SetVec3Module/BBMOD_SetVec3Module.yy new file mode 100644 index 000000000..f408cb4af --- /dev/null +++ b/scripts/BBMOD_SetVec3Module/BBMOD_SetVec3Module.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_SetVec3Module", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "SetProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/SetProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_SetVec3Module/bbmod_setvec3module.gml b/scripts/BBMOD_SetVec3Module/bbmod_setvec3module.gml new file mode 100644 index 000000000..52a822484 --- /dev/null +++ b/scripts/BBMOD_SetVec3Module/bbmod_setvec3module.gml @@ -0,0 +1,36 @@ +/// @func BBMOD_SetVec3Module([_property[, _value]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that sets initial value of three consecutive +/// particle properties when it is spawned. +/// +/// @param {Real} [_property] The first property. Use values from +/// {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec3} [_value] The initial value of the properties. +/// Defaults to `(0.0, 0.0, 0.0)`. +/// +/// @see BBMOD_EParticle +function BBMOD_SetVec3Module(_property=undefined, _value=new BBMOD_Vec3()) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first property. Use values from {@link BBMOD_EParticle}. + /// Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec3} The initial value of the properties. Default + /// value is `(0.0, 0.0, 0.0)`. + Value = _value; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + var _value = Value; + _emitter.Particles[# Property, _particleIndex] = _value.X; + _emitter.Particles[# Property + 1, _particleIndex] = _value.Y; + _emitter.Particles[# Property + 2, _particleIndex] = _value.Z; + } + }; +} diff --git a/scripts/BBMOD_SetVec4Module/BBMOD_SetVec4Module.yy b/scripts/BBMOD_SetVec4Module/BBMOD_SetVec4Module.yy new file mode 100644 index 000000000..392c17892 --- /dev/null +++ b/scripts/BBMOD_SetVec4Module/BBMOD_SetVec4Module.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_SetVec4Module", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "SetProperty", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Universal/SetProperty.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_SetVec4Module/bbmod_setvec4module.gml b/scripts/BBMOD_SetVec4Module/bbmod_setvec4module.gml new file mode 100644 index 000000000..55e7e04dc --- /dev/null +++ b/scripts/BBMOD_SetVec4Module/bbmod_setvec4module.gml @@ -0,0 +1,37 @@ +/// @func BBMOD_SetVec4Module([_property[, _value]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A universal particle module that sets initial value of four consecutive +/// particle properties when it is spawned. +/// +/// @param {Real} [_property] The first property. Use values from +/// {@link BBMOD_EParticle}. Defaults to `undefined`. +/// @param {Struct.BBMOD_Vec4} [_value] The initial value of the properties. +/// Defaults to `(0.0, 0.0, 0.0, 0.0)`. +/// +/// @see BBMOD_EParticle +function BBMOD_SetVec4Module(_property=undefined, _value=new BBMOD_Vec4()) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The first property. Use values from {@link BBMOD_EParticle}. + /// Default value is `undefined`. + Property = _property; + + /// @var {Struct.BBMOD_Vec4} The initial value of the properties. Default + /// value is `(0.0, 0.0, 0.0, 0.0)`. + Value = _value; + + static on_particle_start = function (_emitter, _particleIndex) { + if (Property != undefined) + { + var _value = Value; + _emitter.Particles[# Property, _particleIndex] = _value.X; + _emitter.Particles[# Property + 1, _particleIndex] = _value.Y; + _emitter.Particles[# Property + 2, _particleIndex] = _value.Z; + _emitter.Particles[# Property + 3, _particleIndex] = _value.W; + } + }; +} diff --git a/scripts/BBMOD_Shader/BBMOD_Shader.gml b/scripts/BBMOD_Shader/BBMOD_Shader.gml new file mode 100644 index 000000000..5200c8581 --- /dev/null +++ b/scripts/BBMOD_Shader/BBMOD_Shader.gml @@ -0,0 +1,860 @@ +/// @enum Enum of shader uniform types. Used in global shader uniforms. +/// @private +enum __BBMOD_EShaderUniformType +{ + Float, + Float2, + Float3, + Float4, + FloatArray, + Int, + Int2, + Int3, + Int4, + IntArray, + Matrix, + MatrixArray, + Sampler, +}; + +/// @func __bbmod_shader_get_map() +/// +/// @desc Retrieves a map of registered shader. +/// +/// @return {Id.DsMap} The map of registered +/// shader. +/// +/// @private +function __bbmod_shader_get_map() +{ + static _map = ds_map_create(); + return _map; +} + +/// @func bbmod_shader_register(_name, _shader) +/// +/// @desc Registers a shader. +/// +/// @param {String} _name The name of the shader. +/// @param {Struct.BBMOD_Shader} _shader The shader. +function bbmod_shader_register(_name, _shader) +{ + gml_pragma("forceinline"); + static _map =__bbmod_shader_get_map(); + _map[? _name] = _shader; + _shader.__name = _name; +} + +/// @func bbmod_shader_exists(shader) +/// +/// @desc Checks if there is a shader registered under the name. +/// +/// @param {String} _name The name of the shader. +/// +/// @return {Bool} Returns `true` if there is a shader registered under the +/// name. +function bbmod_shader_exists(_name) +{ + gml_pragma("forceinline"); + static _map =__bbmod_shader_get_map(); + return ds_map_exists(_map, _name); +} + +/// @func bbmod_shader_get(_name) +/// +/// @desc Retrieves a shader registered under the name. +/// +/// @param {String} _name The name of the shader. +/// +/// @return {Struct.BBMOD_Shader} The shader or `undefined` if no +/// shader registered under the given name exists. +function bbmod_shader_get(_name) +{ + gml_pragma("forceinline"); + static _map =__bbmod_shader_get_map(); + return _map[? _name]; +} + +/// @var {Struct.BBMOD_Shader} +/// @private +global.__bbmodShaderCurrent = undefined; + +/// @macro {Struct.BBMOD_Shader} The current shader in use or +/// `undefined`. +/// @readonly +#macro BBMOD_SHADER_CURRENT global.__bbmodShaderCurrent + +/// @func BBMOD_Shader([_shader[, _vertexFormat]]) +/// +/// @extends BBMOD_Class +/// +/// @desc Base class for wrappers of raw GameMaker shader assets. +/// +/// @param {Asset.GMShader} [_shader] The raw GameMaker shader asset. +/// @param {Struct.BBMOD_VertexFormat} [_vertexFormat] The vertex format required +/// by the shader. +/// +/// @note You can use method {@link BBMOD_Shader.add_variant} to add different +/// variants of the shader to be used with different vertex formats. +/// +/// @see BBMOD_VertexFormat +function BBMOD_Shader(_shader=undefined, _vertexFormat=undefined) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {String} The name under which is the shader registered or + /// `undefined`. + /// @private + __name = undefined; + + /// @var {Struct} A mapping from vertex format hashes to raw GameMaker shader + /// assets. + /// @private + __raw = {}; + + /// @var {Asset.GMShader} The shader asset. + /// @readonly + /// @obsolete This has been replaced with {@link BBMOD_Shader.get_variant} and + /// will always equal `undefined`! + Raw = undefined; + + /// @var {Struct.BBMOD_VertexFormat} The vertex format required by the + /// shader. + /// @readonly + /// @obsolete This has been replaced with {@link BBMOD_Shader.has_variant} + /// and will always equal `undefined`! + VertexFormat = undefined; + + if (_shader != undefined && _vertexFormat != undefined) + { + add_variant(_shader, _vertexFormat); + } + + /// @func add_variant(_shader, _vertexFormat) + /// + /// @desc Adds a shader variant to be used with a specific vertex format. + /// + /// @param {Asset.GMShader} [_shader] The raw GameMaker shader asset. + /// @param {Struct.BBMOD_VertexFormat} [_vertexFormat] The vertex format required + /// by the shader. + /// + /// @return {Struct.BBMOD_Shader} Returns `self`. + static add_variant = function (_shader, _vertexFormat) { + gml_pragma("forceinline"); + __raw[$ _vertexFormat.get_hash()] = _shader; + return self; + }; + + /// @func get_variant(_vertexFormat) + /// + /// @desc Retrieves a shader variant for given vertex format. + /// + /// @param {Struct.BBMOD_VertexFormat} _vertexFormat The vertex format. + /// + /// @return {Asset.GMShader} The raw shader asset or `undefined` if the + /// vertex format is not supported. + static get_variant = function (_vertexFormat) { + gml_pragma("forceinline"); + return __raw[$ _vertexFormat.get_hash()]; + }; + + /// @func has_variant(_vertexFormat) + /// + /// @desc Checks whether the shader has a variant for given vertex format. + /// + /// @param {Struct.BBMOD_VertexFormat} _vertexFormat The vertex format. + /// + /// @return {Bool} Returns `true` if the shader has a variant for given + /// vertex format. + static has_variant = function (_vertexFormat) { + gml_pragma("forceinline"); + return (get_raw(_vertexFormat) != undefined); + }; + + /// @func get_name() + /// + /// @desc Retrieves the name of the shader. + /// + /// @return {String} The name of the shader. + /// + /// @obsolete This method is now obsolete and will always return `undefined`! + /// Please use `shader_get_name(shader.get_variant(vertexFormat))` instead. + static get_name = function () { + gml_pragma("forceinline"); + return undefined; + }; + + /// @func is_compiled() + /// + /// @desc Checks whether all shader variants are compiled. + /// + /// @return {Bool} Returns `true` if all shader variants are compiled. + static is_compiled = function () { + gml_pragma("forceinline"); + var _keys = variable_struct_get_names(__raw); + var i = 0; + repeat (array_length(_keys)) + { + if (!shader_is_compiled(__raw[$ _keys[i++]])) + { + return false; + } + } + return true; + }; + + /// @func get_uniform(_name, _vertexFormat) + /// + /// @desc Retrieves a handle of a shader uniform. + /// + /// @param {String} _name The name of the shader uniform. + /// + /// @return {Id.Uniform} The handle of the shader uniform. + /// + /// @obsolete This method is now obsolete and will always return -1! + static get_uniform = function (_name) { + gml_pragma("forceinline"); + return -1; + }; + + /// @func get_sampler_index(_name) + /// + /// @desc Retrieves an index of a texture sampler. + /// + /// @param {String} _name The name of the sampler. + /// + /// @return {Real} The index of the texture sampler. + /// + /// @obsolete This method is now obsolete and will always return -1! + static get_sampler_index = function (_name) { + gml_pragma("forceinline"); + return -1; + }; + + /// @func on_set() + /// + /// @desc A function executed when the shader is set. + static on_set = function () { + }; + + /// @func set(_vertexFormat) + /// + /// @desc Sets the shader as the current shader. + /// + /// @param {Struct.BBMOD_VertexFormat} _vertexFormat Used to set a specific + /// shader variant. + /// + /// @return {Struct.BBMOD_Shader} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the shader is not applied. + static set = function (_vertexFormat) { + gml_pragma("forceinline"); + + if (BBMOD_SHADER_CURRENT != undefined + && BBMOD_SHADER_CURRENT != self) + { + throw new BBMOD_Exception("Another shader is already active!"); + } + + var _shaderRaw = get_variant(_vertexFormat); + + if (_shaderRaw == undefined) + { + throw new BBMOD_Exception( + "Shader variant for vertex format " + + string(_vertexFormat.get_hash()) + + " was not found!"); + } + + if (BBMOD_SHADER_CURRENT != undefined) // Same as == self + { + reset(); + } + + shader_set(_shaderRaw); + BBMOD_SHADER_CURRENT = self; + on_set(); + __bbmod_shader_set_globals(_shaderRaw); + + return self; + }; + + /// @func set_material(_material) + /// + /// @desc Sets shader uniforms using values from the material. + /// + /// @param {Struct.BBMOD_BaseMaterial} _material The material to take the + /// values from. + /// + /// @return {Struct.BBMOD_Shader} Returns `self`. + /// + /// @see BBMOD_BaseMaterial + static set_material = function (_material) { + return self; + }; + + /// @func is_current() + /// + /// @desc Checks if the shader is currently in use. + /// + /// @return {Bool} Returns `true` if the shader is currently in use. + /// + /// @see BBMOD_Shader.set + static is_current = function () { + gml_pragma("forceinline"); + return (BBMOD_SHADER_CURRENT == self); + }; + + /// @func on_reset() + /// + /// @desc A function executed when the shader is reset. + static on_reset = function () { + }; + + /// @func reset() + /// + /// @desc Unsets the shaders. + /// + /// @return {Struct.BBMOD_Shader} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the shader is not currently in use. + static reset = function () { + gml_pragma("forceinline"); + if (!is_current()) + { + throw new BBMOD_Exception("Cannot reset a shader which is not active!"); + } + shader_reset(); + BBMOD_SHADER_CURRENT = undefined; + on_reset(); + return self; + }; +} + +/// @func __bbmod_shader_get_globals() +/// +/// @desc +/// +/// @return {Array} +/// +/// @private +function __bbmod_shader_get_globals() +{ + static _globals = []; + return _globals; +} + +/// @func __bbmod_shader_set_globals(_shader) +/// +/// @desc +/// +/// @param {Asset.GMShader} _shader +/// +/// @private +function __bbmod_shader_set_globals(_shader) +{ + static _globals = __bbmod_shader_get_globals(); + var i = 0; + repeat (array_length(_globals) / 3) + { + var _value = _globals[i + 2]; + if (_value != undefined) + { + switch (_globals[i + 1]) + { + case __BBMOD_EShaderUniformType.Float: + shader_set_uniform_f(shader_get_uniform(_shader, _globals[i]), _value); + break; + + case __BBMOD_EShaderUniformType.Float2: + shader_set_uniform_f(shader_get_uniform(_shader, _globals[i]), _value[0], _value[1]); + break; + + case __BBMOD_EShaderUniformType.Float3: + shader_set_uniform_f(shader_get_uniform(_shader, _globals[i]), _value[0], _value[1], _value[2]); + break; + + case __BBMOD_EShaderUniformType.Float4: + shader_set_uniform_f(shader_get_uniform(_shader, _globals[i]), _value[0], _value[1], _value[2], _value[3]); + break; + + case __BBMOD_EShaderUniformType.FloatArray: + shader_set_uniform_f_array(shader_get_uniform(_shader, _globals[i]), _value); + break; + + case __BBMOD_EShaderUniformType.Int: + shader_set_uniform_i(shader_get_uniform(_shader, _globals[i]), _value); + break; + + case __BBMOD_EShaderUniformType.Int2: + shader_set_uniform_i(shader_get_uniform(_shader, _globals[i]), _value[0], _value[1]); + break; + + case __BBMOD_EShaderUniformType.Int3: + shader_set_uniform_i(shader_get_uniform(_shader, _globals[i]), _value[0], _value[1], _value[2]); + break; + + case __BBMOD_EShaderUniformType.Int4: + shader_set_uniform_i(shader_get_uniform(_shader, _globals[i]), _value[0], _value[1], _value[2], _value[3]); + break; + + case __BBMOD_EShaderUniformType.IntArray: + shader_set_uniform_i_array(shader_get_uniform(_shader, _globals[i]), _value); + break; + + case __BBMOD_EShaderUniformType.Matrix: + shader_set_uniform_matrix(shader_get_uniform(_shader, _globals[i])); + break; + + case __BBMOD_EShaderUniformType.MatrixArray: + shader_set_uniform_matrix_array(shader_get_uniform(_shader, _globals[i]), _value); + break; + + case __BBMOD_EShaderUniformType.Sampler: + var _index = shader_get_sampler_index(_shader, _globals[i]); + + if (_index != -1) + { + texture_set_stage(_index, _value.__texture); + + var _temp = _value.__filter; + if (_temp != undefined) + { + gpu_set_tex_filter_ext(_index, _temp); + } + + _temp = _value.__maxAniso; + if (_temp != undefined) + { + gpu_set_tex_max_aniso_ext(_index, _temp); + } + + _temp = _value.__maxMip; + if (_temp != undefined) + { + gpu_set_tex_max_mip_ext(_index, _temp); + } + + _temp = _value.__minMip; + if (_temp != undefined) + { + gpu_set_tex_min_mip_ext(_index, _temp); + } + + _temp = _value.__mipBias; + if (_temp != undefined) + { + gpu_set_tex_mip_bias_ext(_index, _temp); + } + + _temp = _value.__mipEnable; + if (_temp != undefined) + { + gpu_set_tex_mip_enable_ext(_index, _temp); + } + + _temp = _value.__mipFilter; + if (_temp != undefined) + { + gpu_set_tex_mip_filter_ext(_index, _temp); + } + + _temp = _value.__repeat; + if (_temp != undefined) + { + gpu_set_tex_repeat_ext(_index, _temp); + } + } + break; + } + } + + i += 3; + } +} + +/// @func bbmod_shader_clear_globals() +/// +/// @desc Clears all global uniforms. +function bbmod_shader_clear_globals() +{ + gml_pragma("forceinline"); + static _globals = __bbmod_shader_get_globals(); + array_delete(_globals, 0, array_length(_globals)); +} + +/// @func bbmod_shader_get_global(_name) +/// +/// @desc Retrieves the value of a global shader uniform. +/// +/// @param {String} _name The name of the uniform. +/// +/// @return {Any} The value of the uniform or `undefined` if it is not set. +/// The type of the returned value changes based on the type of the uniform. +function bbmod_shader_get_global(_name) +{ + gml_pragma("forceinline"); + static _globals = __bbmod_shader_get_globals(); + var i = 0; + repeat (array_length(_globals) / 3) + { + if (_globals[i] == _name) + { + return _globals[i + 2]; + } + i += 3; + } + return undefined; +} + +/// @func __bbmod_shader_set_global_impl(_name, _type, _value) +/// +/// @desc +/// +/// @param {String} _name +/// @param {Real} _type +/// @param {Any} _value +/// +/// @private +function __bbmod_shader_set_global_impl(_name, _type, _value) +{ + gml_pragma("forceinline"); + static _globals = __bbmod_shader_get_globals(); + var i = 0; + repeat (array_length(_globals) / 3) + { + if (_globals[i] == _name) + { + _globals[@ i + 1] = _type; + _globals[@ i + 2] = _value; + return; + } + i += 3; + } + array_push(_globals, _name, _type, _value); +} + +/// @func bbmod_shader_set_global_f(_name, _value) +/// +/// @desc Sets a value of a global shader uniform of type float. +/// +/// @param {String} _name The name of the uniform. +/// @param {Real} _value The new value of the uniform. +function bbmod_shader_set_global_f(_name, _value) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.Float, _value); +} + +/// @func bbmod_shader_set_global_f2(_name, _v1, _v2) +/// +/// @desc Sets a value of a global shader uniform of type float2. +/// +/// @param {String} _name The name of the uniform. +/// @param {Real} _v1 The first component of the new value. +/// @param {Real} _v2 The second component of the new value. +function bbmod_shader_set_global_f2(_name, _v1, _v2) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.Float2, [_v1, _v2]); +} + +/// @func bbmod_shader_set_global_f3(_name, _v1, _v2, _v3) +/// +/// @desc Sets a value of a global shader uniform of type float3. +/// +/// @param {String} _name The name of the uniform. +/// @param {Real} _v1 The first component of the new value. +/// @param {Real} _v2 The second component of the new value. +/// @param {Real} _v3 The third component of the new value. +function bbmod_shader_set_global_f3(_name, _v1, _v2, _v3) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.Float3, [_v1, _v2, _v3]); +} + +/// @func bbmod_shader_set_global_f4(_name, _v1, _v2, _v3) +/// +/// @desc Sets a value of a global shader uniform of type float4. +/// +/// @param {String} _name The name of the uniform. +/// @param {Real} _v1 The first component of the new value. +/// @param {Real} _v2 The second component of the new value. +/// @param {Real} _v3 The third component of the new value. +/// @param {Real} _v4 The fourth component of the new value. +function bbmod_shader_set_global_f4(_name, _v1, _v2, _v3, _v4) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.Float4, [_v1, _v2, _v3, _v4]); +} + +/// @func bbmod_shader_set_global_f_array(_name, _fArray) +/// +/// @desc Sets a value of a global shader uniform of type float array. +/// +/// @param {String} _name The name of the uniform. +/// @param {Array} _fArray The new array of values. +function bbmod_shader_set_global_f_array(_name, _fArray) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.FloatArray, _fArray); +} + +/// @func bbmod_shader_set_global_i(_name, _value) +/// +/// @desc Sets a value of a global shader uniform of type int. +/// +/// @param {String} _name The name of the uniform. +/// @param {Real} _value The new value of the uniform. +function bbmod_shader_set_global_i(_name, _value) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.Int, _value); +} + +/// @func bbmod_shader_set_global_i2(_name, _v1, _v2) +/// +/// @desc Sets a value of a global shader uniform of type int2. +/// +/// @param {String} _name The name of the uniform. +/// @param {Real} _v1 The first component of the new value. +/// @param {Real} _v2 The second component of the new value. +function bbmod_shader_set_global_i2(_name, _v1, _v2) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.Int2, [_v1, _v2]); +} + +/// @func bbmod_shader_set_global_i3(_name, _v1, _v2, _v3) +/// +/// @desc Sets a value of a global shader uniform of type int3. +/// +/// @param {String} _name The name of the uniform. +/// @param {Real} _v1 The first component of the new value. +/// @param {Real} _v2 The second component of the new value. +/// @param {Real} _v3 The third component of the new value. +function bbmod_shader_set_global_i3(_name, _v1, _v2, _v3) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.Int3, [_v1, _v2, _v3]); +} + +/// @func bbmod_shader_set_global_i4(_name, _v1, _v2, _v3) +/// +/// @desc Sets a value of a global shader uniform of type int4. +/// +/// @param {String} _name The name of the uniform. +/// @param {Real} _v1 The first component of the new value. +/// @param {Real} _v2 The second component of the new value. +/// @param {Real} _v3 The third component of the new value. +/// @param {Real} _v4 The fourth component of the new value. +function bbmod_shader_set_global_i4(_name, _v1, _v2, _v3, _v4) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.Int4, [_v1, _v2, _v3, _v4]); +} + +/// @func bbmod_shader_set_global_i_array(_name, _iArray) +/// +/// @desc Sets a value of a global shader uniform of type int array. +/// +/// @param {String} _name The name of the uniform. +/// @param {Array} _iArray The new array of values. +function bbmod_shader_set_global_i_array(_name, _iArray) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.IntArray, _iArray); +} + +/// @func bbmod_shader_set_global_matrix(_name) +/// +/// @desc Enables passing of the current transform matrix to a global shader uniform. +/// +/// @param {String} _name The name of the uniform. +function bbmod_shader_set_global_matrix(_name) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.Matrix, true); +} + +/// @func bbmod_shader_set_global_matrix_array(_name, _matrixArray) +/// +/// @desc Sets a value of a global shader uniform of type matrix array. +/// +/// @param {String} _name The name of the uniform. +/// @param {Array} _matrixArray The new array of values. +function bbmod_shader_set_global_matrix_array(_name, _matrixArray) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.MatrixArray, _matrixArray); +} + +/// @func bbmod_shader_set_global_sampler(_name, _texture) +/// +/// @desc Sets a global shader texture sampler. +/// +/// @param {String} _name The name of the sampler. +/// @param {Pointer.Texture} _texture The new texture. +function bbmod_shader_set_global_sampler(_name, _texture) +{ + gml_pragma("forceinline"); + __bbmod_shader_set_global_impl(_name, __BBMOD_EShaderUniformType.Sampler, + { + __texture: _texture, + __filter: undefined, + __maxAniso: undefined, + __maxMip: undefined, + __minMip: undefined, + __mipBias: undefined, + __mipEnable: undefined, + __mipFilter: undefined, + __repeat: undefined, + }); +} + +/// @func bbmod_shader_set_global_sampler_filter(_name, _filter) +/// +/// @desc Enables/disables linear filtering of a global texture sampler. +/// +/// @param {String} _name The name of the sampler. +/// @param {Bool} _filter Use `true`/`false` to enable/disable linear texture +/// filtering or `undefined` to unset. +/// +/// @note The sampler must be first set using +/// {@link bbmod_shader_set_global_sampler}! +function bbmod_shader_set_global_sampler_filter(_name, _filter) +{ + gml_pragma("forceinline"); + bbmod_shader_get_global(_name).__filter = _filter; +} + +/// @func bbmod_shader_set_global_sampler_max_aniso(_name, _value) +/// +/// @desc Sets maximum anisotropy level of a global texture sampler. +/// +/// @param {String} _name The name of the sampler. +/// @param {Real} _value The new maximum anisotropy. Use `undefined` to unset. +/// +/// @note The sampler must be first set using +/// {@link bbmod_shader_set_global_sampler}! +function bbmod_shader_set_global_sampler_max_aniso(_name, _value) +{ + gml_pragma("forceinline"); + bbmod_shader_get_global(_name).__maxAniso = _value; +} + +/// @func bbmod_shader_set_global_sampler_max_mip(_name, _value) +/// +/// @desc Sets maximum mipmap level of a global texture sampler. +/// +/// @param {String} _name The name of the sampler. +/// @param {Real} _value The new maxmimum mipmap level or `undefined` to unset. +/// +/// @note The sampler must be first set using +/// {@link bbmod_shader_set_global_sampler}! +function bbmod_shader_set_global_sampler_max_mip(_name, _value) +{ + gml_pragma("forceinline"); + bbmod_shader_get_global(_name).__maxMip = _value; +} + +/// @func bbmod_shader_set_global_sampler_min_mip(_name, _value) +/// +/// @desc Sets minimum mipmap level of a global texture sampler. +/// +/// @param {String} _name The name of the sampler. +/// @param {Real} _value The new minimum mipmap level or `undefined` to unset. +/// +/// @note The sampler must be first set using +/// {@link bbmod_shader_set_global_sampler}! +function bbmod_shader_set_global_sampler_min_mip(_name, _value) +{ + gml_pragma("forceinline"); + bbmod_shader_get_global(_name).__minMip = _value; +} + +/// @func bbmod_shader_set_global_sampler_mip_bias(_name, _value) +/// +/// @desc Sets mipmap bias of a global texture sampler. +/// +/// @param {String} _name The name of the sampler. +/// @param {Real} _value The new bias or `undefined` to unset. +/// +/// @note The sampler must be first set using +/// +/// {@link bbmod_shader_set_global_sampler}! +function bbmod_shader_set_global_sampler_mip_bias(_name, _value) +{ + gml_pragma("forceinline"); + bbmod_shader_get_global(_name).__mipBias = _value; +} + +/// @func bbmod_shader_set_global_sampler_mip_enable(_name, _enable) +/// +/// @desc Enable/disable mipmapping of a global texture sampler. +/// +/// @param {String} _name The name of the sampler. +/// @param {Bool} _enable Use `true`/`false` to enable/disable mipmapping or +/// `undefined` to unset. +/// +/// @note The sampler must be first set using +/// {@link bbmod_shader_set_global_sampler}! +function bbmod_shader_set_global_sampler_mip_enable(_name, _enable) +{ + gml_pragma("forceinline"); + bbmod_shader_get_global(_name).__mipEnable = _enable; +} + +/// @func bbmod_shader_set_global_sampler_mip_filter(_name, _filter) +/// +/// @desc Sets mipmap filter function of a global texture sampler. +/// +/// @param {String} _name The name of the sampler. +/// @param {Constant.MipFilter} _filter The new mipmap filter or `undefined` to +/// unset. +/// +/// @note The sampler must be first set using +/// {@link bbmod_shader_set_global_sampler}! +function bbmod_shader_set_global_sampler_mip_filter(_name, _filter) +{ + gml_pragma("forceinline"); + bbmod_shader_get_global(_name).__mipFilter = _filter; +} + +/// @func bbmod_shader_set_global_sampler_repeat(_name, _enable) +/// +/// @desc Enable/disable repeat of a global texture sampler. +/// +/// @param {String} _name The name of the sampler. +/// @param {Bool} _enable Use `true`/`false` to enable/disable texture repeat or +/// `undefined` to unset. +/// +/// @note The sampler must be first set using +/// {@link bbmod_shader_set_global_sampler}! +function bbmod_shader_set_global_sampler_repeat(_name, _enable) +{ + gml_pragma("forceinline"); + bbmod_shader_get_global(_name).__repeat = _enable; +} + +/// @func bbmod_shader_unset_global(_name) +/// +/// @desc Unsets a value of a global shader uniform. +/// +/// @param {String} _name The name of the uniform. +function bbmod_shader_unset_global(_name) +{ + gml_pragma("forceinline"); + static _globals = __bbmod_shader_get_globals(); + var i = 0; + repeat (array_length(_globals) / 3) + { + if (_globals[i] == _name) + { + array_delete(_globals, i, 3); + return; + } + i += 3; + } +} diff --git a/scripts/BBMOD_Shader/BBMOD_Shader.yy b/scripts/BBMOD_Shader/BBMOD_Shader.yy new file mode 100644 index 000000000..fff54de68 --- /dev/null +++ b/scripts/BBMOD_Shader/BBMOD_Shader.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Shader", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_SphereCollider/BBMOD_SphereCollider.gml b/scripts/BBMOD_SphereCollider/BBMOD_SphereCollider.gml new file mode 100644 index 000000000..11395b720 --- /dev/null +++ b/scripts/BBMOD_SphereCollider/BBMOD_SphereCollider.gml @@ -0,0 +1,152 @@ +/// @func BBMOD_SphereCollider([_position[, _radius]]) +/// +/// @extends BBMOD_Collider +/// +/// @desc A sphere collider. +/// +/// @param {Struct.BBMOD_Vec3} [_position] The position of the sphere Defaults +/// to `(0, 0, 0)`. +/// +/// @param {Real} [_radius] The radius of the sphere. Defaults to 1. +/// +/// @see BBMOD_AABBCollider +/// @see BBMOD_FrustumCollider +/// @see BBMOD_PlaneCollider +function BBMOD_SphereCollider(_position=new BBMOD_Vec3(), _radius=1.0) + : BBMOD_Collider() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_Vec3} The position of the sphere. + Position = _position; + + /// @var {Real} The radius of the sphere. + Radius = _radius; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L142 + static GetClosestPoint = function (_point) { + gml_pragma("forceinline"); + var _sphereToPoint = _point.Sub(Position).Normalize() + .Scale(Radius); + return Position.Add(_sphereToPoint); + }; + + static __testImpl = function (_collider) { + gml_pragma("forceinline"); + var _closestPoint = _collider.GetClosestPoint(Position); + return (Position.Sub(_closestPoint).Length() < Radius); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L319 + static TestAABB = __testImpl; + + static TestFrustum = function (_frustum) { + gml_pragma("forceinline"); + return _frustum.TestSphere(self); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L333 + static TestPlane = __testImpl; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L97 + static TestPoint = function (_point) { + gml_pragma("forceinline"); + return (_point.Sub(Position).Length() < Radius); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L313 + static TestSphere = function (_sphere) { + gml_pragma("forceinline"); + return (Position.Sub(_sphere.Position).Length() < Radius + _sphere.Radius); + }; + + // Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L552 + static Raycast = function (_ray, _result=undefined) { + if (_result != undefined) + { + _result.Reset(); + } + + var _e = Position.Sub(_ray.Origin); + var _rSq = Radius * Radius; + + var _eSq = _e.LengthSqr(); + var _a = _e.Dot(_ray.Direction); // _ray.Direction should be normalized! + var _bSq = _eSq - (_a * _a); + var _f = sqrt(abs((_rSq) - _bSq)); + + if (_rSq - _bSq < 0.0) + { + return false; + } + + var _t = _a - _f; + + if (_eSq < _rSq) + { + _t = _a + _f; + } + + if (_result != undefined) + { + _result.Distance = _t; + _result.Point = _ray.Origin.Add(_ray.Direction.Scale(_t)); + _result.Normal = _result.Point.Sub(Position).Normalize(); + } + + return true; + }; + + static DrawDebug = function (_color=c_white, _alpha=1.0) { + var _vbuffer = global.__bbmodVBufferDebug; + + vertex_begin(_vbuffer, BBMOD_VFORMAT_DEBUG.Raw); + + var _x = Position.X; + var _y = Position.Y; + var _z = Position.Z; + var _radius = Radius; + var _steps = 16; + var _inc = 360.0 / _steps; + var _angle = 0.0; + var _ldirx1 = lengthdir_x(_radius, _angle); + var _ldiry1 = lengthdir_y(_radius, _angle); + + repeat (_steps) + { + var _ldirx2 = lengthdir_x(_radius, _angle + _inc); + var _ldiry2 = lengthdir_y(_radius, _angle + _inc); + + // Circle around X axis + vertex_position_3d(_vbuffer, _x, _y + _ldirx1, _z + _ldiry1); + vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x, _y + _ldirx2, _z + _ldiry2); + vertex_color(_vbuffer, _color, _alpha); + + // Circle around Y axis + vertex_position_3d(_vbuffer, _x + _ldirx1, _y, _z + _ldiry1); + vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x + _ldirx2, _y, _z + _ldiry2); + vertex_color(_vbuffer, _color, _alpha); + + // Circle around Z axis + vertex_position_3d(_vbuffer, _x + _ldirx1, _y + _ldiry1, _z); + vertex_color(_vbuffer, _color, _alpha); + + vertex_position_3d(_vbuffer, _x + _ldirx2, _y + _ldiry2, _z); + vertex_color(_vbuffer, _color, _alpha); + + _ldirx1 = _ldirx2; + _ldiry1 = _ldiry2; + _angle += _inc; + } + + vertex_end(_vbuffer); + + vertex_submit(_vbuffer, pr_linelist, -1); + + return self; + }; +} diff --git a/scripts/BBMOD_SphereCollider/BBMOD_SphereCollider.yy b/scripts/BBMOD_SphereCollider/BBMOD_SphereCollider.yy new file mode 100644 index 000000000..6329928f1 --- /dev/null +++ b/scripts/BBMOD_SphereCollider/BBMOD_SphereCollider.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_SphereCollider", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Raycasting", + "path": "folders/_Extensions/BBMOD/Raycasting.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_SphereEmissionModule/BBMOD_SphereEmissionModule.gml b/scripts/BBMOD_SphereEmissionModule/BBMOD_SphereEmissionModule.gml new file mode 100644 index 000000000..793cfac21 --- /dev/null +++ b/scripts/BBMOD_SphereEmissionModule/BBMOD_SphereEmissionModule.gml @@ -0,0 +1,40 @@ +/// @func BBMOD_SphereEmissionModule([_radius[, _inside]]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that positions spawned particles into a sphere +/// shape. +/// +/// @param {Real} [_radius] The radius of the sphere. Defaults to 0.5. +/// @param {Bool} [_inside] Whether the particles can be spawned inside the +/// sphere. +/// Defaults to `true`. +/// +/// @see BBMOD_EParticle.PositionX +/// @see BBMOD_EParticle.PositionY +/// @see BBMOD_EParticle.PositionZ +function BBMOD_SphereEmissionModule(_radius=0.5, _inside=true) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Real} The radius of the sphere. Default value is 0.5. + Radius = _radius; + + /// @var {Bool} If `true`, then the particles can be spawned inside the sphere. + /// Default value is `true`. + Inside = _inside; + + static on_particle_start = function (_emitter, _particleIndex) { + var _offsetX = random_range(-1.0, 1.0); + var _offsetY = random_range(-1.0, 1.0); + var _offsetZ = random_range(-1.0, 1.0); + var _scale = (Inside ? random(Radius) : Radius) + / point_distance_3d(0.0, 0.0, 0.0, _offsetX, _offsetY, _offsetZ); + var _particles = _emitter.Particles; + + _particles[# BBMOD_EParticle.PositionX, _particleIndex] += _offsetX * _scale; + _particles[# BBMOD_EParticle.PositionY, _particleIndex] += _offsetY * _scale; + _particles[# BBMOD_EParticle.PositionZ, _particleIndex] += _offsetZ * _scale; + }; +} diff --git a/scripts/BBMOD_SphereEmissionModule/BBMOD_SphereEmissionModule.yy b/scripts/BBMOD_SphereEmissionModule/BBMOD_SphereEmissionModule.yy new file mode 100644 index 000000000..efa325ca6 --- /dev/null +++ b/scripts/BBMOD_SphereEmissionModule/BBMOD_SphereEmissionModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_SphereEmissionModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Shape", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Emission/Shape.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_SpotLight/BBMOD_SpotLight.gml b/scripts/BBMOD_SpotLight/BBMOD_SpotLight.gml new file mode 100644 index 000000000..f8067d3b8 --- /dev/null +++ b/scripts/BBMOD_SpotLight/BBMOD_SpotLight.gml @@ -0,0 +1,74 @@ +/// @func BBMOD_SpotLight([_color[, _position[, _range[, _direction[, _angleInner[, _angleOuter]]]]]]) +/// +/// @extends BBMOD_PunctualLight +/// +/// @desc A spot light. +/// +/// @param {Struct.BBMOD_Color} [_color] The light's color. Defaults to +/// {@link BBMOD_C_WHITE}. +/// @param {Struct.BBMOD_Vec3} [_position] The light's position. Defaults to +/// `(0, 0, 0)` if `undefined`. +/// @param {Real} [_range] The light's range. Defaults to 1. +/// @param {Struct.BBMOD_Vec3} [_direction] The light's direction. Defaults to +/// {@link BBMOD_VEC3_FORWARD} if `undefined`. +/// @param {Real} [_angleInner] The inner cone angle in degrees. Defaults to 10. +/// @param {Real} [_angleOuter] The outer cone angle in degrees. Defaults to 20. +function BBMOD_SpotLight( + _color=BBMOD_C_WHITE, + _position=undefined, + _range=1.0, + _direction=undefined, + _angleInner=10, + _angleOuter=20 +) : BBMOD_PunctualLight(_color, _position, _range) constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_Vec3} The direction of the light. The default value is + /// `(1, 0, 0)`. + Direction = _direction ?? BBMOD_VEC3_FORWARD; + + /// @var {Real} The inner cone angle in degrees. Default value is 10. + AngleInner = _angleInner; + + /// @var {Real} The inner cone angle in degrees. Default value is 20. + AngleOuter = _angleOuter; + + __getZFar = __get_shadowmap_zfar; + + __getViewMatrix = __get_shadowmap_view; + + __getProjMatrix = __get_shadowmap_projection; + + __getShadowmapMatrix = __get_shadowmap_matrix; + + static __get_shadowmap_zfar = function () { + gml_pragma("forceinline"); + return Range; + }; + + static __get_shadowmap_view = function () { + gml_pragma("forceinline"); + return matrix_build_lookat( + Position.X, + Position.Y, + Position.Z, + Position.X + Direction.X, + Position.Y + Direction.Y, + Position.Z + Direction.Z, + 0.0, 0.0, 1.0); // TODO: Find the up vector + }; + + static __get_shadowmap_projection = function () { + gml_pragma("forceinline"); + return matrix_build_projection_perspective_fov( + AngleOuter * 2.0, 1.0, 0.01, Range); + }; + + static __get_shadowmap_matrix = function () { + gml_pragma("forceinline"); + return matrix_multiply( + __getViewMatrix(), + __getProjMatrix()); + }; +} diff --git a/scripts/BBMOD_SpotLight/BBMOD_SpotLight.yy b/scripts/BBMOD_SpotLight/BBMOD_SpotLight.yy new file mode 100644 index 000000000..63e9ab5fb --- /dev/null +++ b/scripts/BBMOD_SpotLight/BBMOD_SpotLight.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_SpotLight", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Lights", + "path": "folders/_Extensions/BBMOD/Core/Lights.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Sprite/BBMOD_Sprite.gml b/scripts/BBMOD_Sprite/BBMOD_Sprite.gml new file mode 100644 index 000000000..d1bb9bb00 --- /dev/null +++ b/scripts/BBMOD_Sprite/BBMOD_Sprite.gml @@ -0,0 +1,110 @@ +/// @func BBMOD_Sprite([_file[, _sha1]]) +/// +/// @extends BBMOD_Resource +/// +/// @desc A sprite. +/// +/// @param {String} [_file] The file to load the sprite from or `undefined`. +/// @param {String} [_sha1] Expected SHA1 of the file. If the actual one does +/// not match with this, then the model will not be loaded. Use `undefined` if +/// you do not want to check the SHA1 of the file. +/// +/// @throws {BBMOD_Exception} When the sprite fails to load. +function BBMOD_Sprite(_file=undefined, _sha1=undefined) + : BBMOD_Resource() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Resource_destroy = destroy; + + /// @var {Asset.GMSprite} The raw sprite resource of `undefined` if it + /// has not been loaded yet. + /// @readonly + Raw = undefined; + + /// @var {Real} The width of the sprite. + /// @readonly + Width = 0; + + /// @var {Real} The height of the sprite. + /// @readonly + Height = 0; + + static from_file = function (_file, _sha1=undefined) { + Path = _file; + check_file(_file, _sha1); + Raw = sprite_add(_file, 1, false, false, 0, 0); + Width = sprite_get_width(Raw); + Height = sprite_get_height(Raw); + IsLoaded = true; + return self; + }; + + static from_file_async = function (_file, _sha1=undefined, _callback=undefined) { + Path = _file; + + if (!check_file(_file, _sha1, _callback ?? bbmod_empty_callback)) + { + return self; + } + + var _sprite = self; + var _struct = { + Sprite: _sprite, + Callback: _callback, + }; + bbmod_sprite_add_async(_file, method(_struct, function (_err, _res) { + if (_err == undefined) + { + Sprite.Raw = _res; + Sprite.Width = sprite_get_width(_res); + Sprite.Height = sprite_get_height(_res); + Sprite.IsLoaded = true; + } + if (Callback) + { + Callback(_err, _res); + } + })); + + return self; + }; + + static to_file = function (_file) { + var _dirname = filename_dir(_file); + if (!directory_exists(_dirname)) + { + directory_create(_dirname); + } + sprite_save_strip(Raw, _file); + return self; + }; + + /// @func get_texture() + /// + /// @desc Retrieves a pointer to the texture. + /// + /// @return {Pointer.Texture} The pointer to the texture. + static get_texture = function () { + gml_pragma("forceinline"); + if (Raw == undefined) + { + return -1; + } + return sprite_get_texture(Raw, 0); + }; + + static destroy = function () { + Resource_destroy(); + if (Raw != undefined) + { + sprite_delete(Raw); + } + return undefined; + }; + + if (_file != undefined) + { + from_file(_file, _sha1); + } +} diff --git a/scripts/BBMOD_Sprite/BBMOD_Sprite.yy b/scripts/BBMOD_Sprite/BBMOD_Sprite.yy new file mode 100644 index 000000000..19121f4e7 --- /dev/null +++ b/scripts/BBMOD_Sprite/BBMOD_Sprite.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Sprite", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_State/BBMOD_State.gml b/scripts/BBMOD_State/BBMOD_State.gml new file mode 100644 index 000000000..b52ca47cb --- /dev/null +++ b/scripts/BBMOD_State/BBMOD_State.gml @@ -0,0 +1,55 @@ +/// @func BBMOD_State(_name) +/// +/// @extends BBMOD_Class +/// +/// @desc A state of a state machine. +/// +/// @param {String} _name The name of the state. +/// +/// @see BBMOD_StateMachine +function BBMOD_State(_name) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_StateMachine} The state machine to which this state + /// belongs or `undefined`. + /// @readonly + StateMachine = undefined; + + /// @var {String} The name of the state. + Name = _name; + + /// @var {Function} A function executed when a state machines enters this + /// state. Should take the state as the first argument. Default value is + /// `undefined`. + OnEnter = undefined; + + /// @var {Function} A function executed while the state is active. Should + /// take the state as the first argument and delta time as the second. + /// Default value is `undefined`. + OnUpdate = undefined; + + /// @var {Function} A function executed when a state machine exists this + /// state. Should take the state as the first argument. Default value is + /// `undefined`. + OnExit = undefined; + + /// @var {Bool} If `true` then the state is currently active. + /// @readonly + IsActive = false; + + /// @var {Real} + /// @private + __activeSince = 0; + + /// @func get_duration() + /// + /// @desc Retrieves how long (in milliseconds) has the state been active for. + /// + /// @return {Real} Number of milliseconds for which has the state been active. + static get_duration = function () { + gml_pragma("forceinline"); + return (current_time - __activeSince); + }; +} diff --git a/scripts/BBMOD_State/BBMOD_State.yy b/scripts/BBMOD_State/BBMOD_State.yy new file mode 100644 index 000000000..afe354a1b --- /dev/null +++ b/scripts/BBMOD_State/BBMOD_State.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_State", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "StateMachine", + "path": "folders/_Extensions/BBMOD/StateMachine.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_StateMachine/BBMOD_StateMachine.gml b/scripts/BBMOD_StateMachine/BBMOD_StateMachine.gml new file mode 100644 index 000000000..4980cb1e2 --- /dev/null +++ b/scripts/BBMOD_StateMachine/BBMOD_StateMachine.gml @@ -0,0 +1,206 @@ +/// @func BBMOD_StateMachine() +/// +/// @extends BBMOD_Class +/// +/// @desc A state machine. +/// +/// @see BBMOD_State +function BBMOD_StateMachine() + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Array} An array of sates. + /// @private + __stateArray = []; + + /// @var {Bool} If `false` then the state machine has not yet entered its + /// initial state. + /// @readonly + /// @see BBMOD_StateMachine.start + Started = false; + + /// @var {Bool} If `true` then the state machine has reached its final state. + /// @readonly + /// @see BBMOD_StateMachine.finish + Finished = false; + + /// @var {Struct.BBMOD_State} The current state or `undefined`. + /// @readonly + State = undefined; + + /// @var {Function} A function executed on the start of the state of the + /// state machine. It should take the state machine as the first argument. + /// Default value is `undefined`. + OnEnter = undefined; + + /// @var {Function} A function executed in the update method *before* + /// the current state is updated. It should take the state machine as the + /// first argument and delta time as the second argument. Default value is + /// `undefined`. + OnPreUpdate = undefined; + + /// @var {Function} A function executed when the state changes. It should + /// take the state machine as the first argument and its previous state as + /// the second argument. Default value is `undefined`. + OnStateChange = undefined; + + /// @var {Function} A function executed in the update method *after* + /// the current state is updated. It should take the state machine as the + /// first argument and delta time as the second argument. Default value is + /// `undefined`. + OnPostUpdate = undefined; + + /// @var {Function} A function executed on the end of the state machine. + /// It should take the state machine as the first argument. Default value is + /// `undefined`. + OnExit = undefined; + + /// @func start() + /// + /// @desc Enters the initial state of the state machine. + /// + /// @return {Struct.BBMOD_StateMachine} Returns `self`. + static start = function () { + gml_pragma("forceinline"); + Started = true; + Finished = false; + if (OnEnter != undefined) + { + OnEnter(self); + } + return self; + }; + + /// @func finish() + /// + /// @desc Enters the exit state of the state machine. + /// + /// @return {Struct.BBMOD_StateMachine} Returns `self`. + static finish = function () { + gml_pragma("forceinline"); + Finished = true; + if (OnExit != undefined) + { + OnExit(self); + } + return self; + }; + + /// @func add_state(_state) + /// + /// @desc Adds a state to the state machine. + /// + /// @param {Struct.BBMOD_State} _state The state to add. + /// + /// @return {Struct.BBMOD_StateMachine} Returns `self`. + static add_state = function (_state) { + gml_pragma("forceinline"); + _state.StateMachine = self; + array_push(__stateArray, _state); + return self; + }; + + /// @func change_state(_state) + /// + /// @desc Changes the state of the state machine and executes + /// {@link BBMOD_StateMachine.OnStateChange}. + /// + /// @param {Struct.BBMOD_State} _state The new state. + /// + /// @return {Struct.BBMOD_StateMachine} Returns itself. + /// + /// @throws {BBMOD_Exception} If an invalid state is passed. + static change_state = function (_state) { + gml_pragma("forceinline"); + + // Check if the state is valid + if (_state.StateMachine != self) + { + throw new BBMOD_Exception("Invalid state \"" + string(_state.Name) + "\"!"); + } + + // Exit current state + var _statePrev = State; + + if (_statePrev != undefined) + { + if (_statePrev.OnExit != undefined) + { + _statePrev.IsActive = false; + _statePrev.OnExit(_statePrev); + } + } + + // Enter new state + State = _state; + State.IsActive = true; + State.__activeSince = current_time; + + if (State.OnEnter != undefined) + { + State.OnEnter(State); + } + + // Trigger OnStateChange + if (OnStateChange != undefined) + { + OnStateChange(self, _statePrev); + } + + return self; + }; + + /// @func update(_deltaTime) + /// + /// @desc Executes function for the current state of the state machine + /// (if defined). + /// + /// @param {Real} _deltaTime How much time has passed since the last frame + /// (in microseconds). + /// + /// @return {Struct.BBMOD_StateMachine} Returns `self`. + /// + /// @note This function does not do anything if the state machine has not + /// started yet or if it has already reached its final state. + /// + /// @see BBMOD_StateMachine.start + /// @see BBMOD_StateMachine.finish + static update = function (_deltaTime) { + gml_pragma("forceinline"); + + if (!Started || Finished) + { + return self; + } + + if (OnPreUpdate != undefined) + { + OnPreUpdate(self, _deltaTime); + } + + if (State != undefined && State.OnUpdate != undefined) + { + State.OnUpdate(State); + } + + if (OnPostUpdate != undefined) + { + OnPostUpdate(self, _deltaTime); + } + + return self; + }; + + static destroy = function () { + Class_destroy(); + for (var i = array_length(__stateArray) - 1; i >= 0; --i) + { + __stateArray[i].destroy(); + } + __stateArray = []; + return undefined; + }; +} diff --git a/scripts/BBMOD_StateMachine/BBMOD_StateMachine.yy b/scripts/BBMOD_StateMachine/BBMOD_StateMachine.yy new file mode 100644 index 000000000..61612ab01 --- /dev/null +++ b/scripts/BBMOD_StateMachine/BBMOD_StateMachine.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_StateMachine", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "StateMachine", + "path": "folders/_Extensions/BBMOD/StateMachine.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_StaticBatch/BBMOD_StaticBatch.gml b/scripts/BBMOD_StaticBatch/BBMOD_StaticBatch.gml new file mode 100644 index 000000000..e577a5cc6 --- /dev/null +++ b/scripts/BBMOD_StaticBatch/BBMOD_StaticBatch.gml @@ -0,0 +1,173 @@ +/// @func BBMOD_StaticBatch(_vformat) +/// +/// @extends BBMOD_Class +/// +/// @desc A static batch is a structure that allows you to compose static models +/// into a single one. Compared to {@link BBMOD_Model.submit}, this drastically +/// reduces draw calls and increases performance, but requires more memory. +/// Current limitation is that the added models must use the same single +/// material. +/// +/// @param {Struct.BBMOD_VertexFormat} _vformat The vertex format of the static +/// batch. +/// All models added to the same static batch must have the same vertex format. +/// This vertex format must not contain bone data! +/// +/// @example +/// ```gml +/// modTree = new BBMOD_Model("Tree.bbmod"); +/// var _vformat = modTree.get_vertex_format(); +/// batch = new BBMOD_StaticBatch(_vformat); +/// batch.start(); +/// with (OTree) +/// { +/// var _transform = matrix_build(x, y, z, 0, 0, direction, 1, 1, 1); +/// other.batch.add(other.modTree, _transform); +/// } +/// batch.finish(); +/// batch.freeze(); +/// ``` +/// +/// @see BBMOD_Model.get_vertex_format +/// @see BBMOD_DynamicBatch +/// +/// @deprecated +function BBMOD_StaticBatch(_vformat) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Id.__vertexBuffer} A vertex buffer. + /// @private + __vertexBuffer = vertex_create_buffer(); + + /// @var {Struct.BBMOD_VertexFormat} The format of the vertex buffer. + /// @private + __vertexFormat = _vformat; + + /// @var {Constant.__primitiveType} The primitive type of the batch. + /// @private + __primitiveType = undefined; + + /// @func start() + /// + /// @desc Begins adding models into the static batch. + /// + /// @see BBMOD_StaticBatch.add + /// @see BBMOD_StaticBatch.finish + /// + /// @return {Struct.BBMOD_StaticBatch} Returns `self`. + static start = function () { + gml_pragma("forceinline"); + vertex_begin(__vertexBuffer, __vertexFormat.Raw); + return self; + }; + + /// @func add(_model, _transform) + /// + /// @desc Adds a model to the static batch. + /// + /// @param {Struct.BBMOD_Model} _model The model. + /// @param {Array} _transform A transformation matrix of the model. + /// + /// @return {Struct.BBMOD_StaticBatch} Returns `self`. + /// + /// @example + /// ```gml + /// modTree = new BBMOD_Model("Tree.bbmod"); + /// var _vformat = modTree.get_vertex_format(); + /// batch = new BBMOD_StaticBatch(_vformat); + /// batch.start(); + /// with (OTree) + /// { + /// var _transform = matrix_build(x, y, z, 0, 0, direction, 1, 1, 1); + /// other.batch.add(other.modTree, _transform); + /// } + /// batch.finish(); + /// batch.freeze(); + /// ``` + /// + /// @note You must first call {@link BBMOD_StaticBatch.begin} before using + /// this function! + /// + /// @see BBMOD_StaticBatch.finish + static add = function (_model, _transform) { + gml_pragma("forceinline"); + _model.__to_static_batch(self, _transform); + return self; + }; + + /// @func finish() + /// + /// @desc Ends adding models into the static batch. + /// + /// @return {Struct.BBMOD_StaticBatch} Returns `self`. + /// + /// @see BBMOD_StaticBatch.start + static finish = function () { + gml_pragma("forceinline"); + vertex_end(__vertexBuffer); + return self; + }; + + /// @func freeze() + /// + /// @desc Freezes the static batch. This makes it render faster, but disables + /// adding more models. + /// + /// @return {Struct.BBMOD_StaticBatch} Returns `self`. + static freeze = function () { + gml_pragma("forceinline"); + vertex_freeze(__vertexBuffer); + return self; + }; + + /// @func submit(_material) + /// + /// @desc Immediately submits the static batch for rendering. + /// + /// @param {Struct.BBMOD_BaseMaterial} _material A material. + /// + /// @return {Struct.BBMOD_StaticBatch} Returns `self`. + /// + /// @note The static batch is *not* submitted if the material used is not + /// compatible with the current render pass! + /// + /// @see BBMOD_StaticBatch.render + /// @see BBMOD_BaseMaterial + /// @see BBMOD_ERenderPass + static submit = function (_material) { + gml_pragma("forceinline"); + if (!_material.apply(__vertexFormat)) + { + return self; + } + vertex_submit(__vertexBuffer, __primitiveType, _material.BaseOpacity); + return self; + }; + + /// @func render(_material) + /// + /// @desc Enqueues the static batch for rendering. + /// + /// @param {Struct.BBMOD_BaseMaterial} _material A material. + /// + /// @return {Struct.BBMOD_StaticBatch} Returns `self`. + /// + /// @see BBMOD_StaticBatch.submit + /// @see BBMOD_BaseMaterial + static render = function (_material) { + gml_pragma("forceinline"); + _material.RenderQueue.draw_mesh( + __vertexBuffer, __vertexFormat, __primitiveType, -1, _material, matrix_get(matrix_world)); + return self; + }; + + static destroy = function () { + Class_destroy(); + vertex_delete_buffer(__vertexBuffer); + return undefined; + }; +} diff --git a/scripts/BBMOD_StaticBatch/BBMOD_StaticBatch.yy b/scripts/BBMOD_StaticBatch/BBMOD_StaticBatch.yy new file mode 100644 index 000000000..5eabb7c12 --- /dev/null +++ b/scripts/BBMOD_StaticBatch/BBMOD_StaticBatch.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_StaticBatch", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Batching", + "path": "folders/_Extensions/BBMOD/Core/Batching.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Terrain/BBMOD_Terrain.gml b/scripts/BBMOD_Terrain/BBMOD_Terrain.gml new file mode 100644 index 000000000..0dbaada45 --- /dev/null +++ b/scripts/BBMOD_Terrain/BBMOD_Terrain.gml @@ -0,0 +1,782 @@ +/// @func BBMOD_Terrain([_heightmap[, _subimage]]) +/// +/// @extends BBMOD_Class +/// +/// @desc A heightmap based terrain with five material layers controlled through +/// a splatmap. +/// +/// @param {Asset.GMSprite} [_heightmap] The heightmap to make the terrain +/// from. If `undefined`, then you will need to build the terrain mesh yourself +/// later using the terrain's methods. +/// @param {Real} [_subimage] The sprite subimage to use for the heightmap. +/// Defaults to 0. +function BBMOD_Terrain(_heightmap=undefined, _subimage=0) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + static Class_destroy = destroy; + + /// @var {Struct.BBMOD_RenderQueue} Render queue for terrain layers. + /// @readonly + static RenderQueue = new BBMOD_RenderQueue("Terrain", -$FFFFFFFE); + + /// @var {Array} Array of five material layers. Use + /// `undefined` instead of a material to disable certain layer. + Layer = array_create(5, undefined); + + /// @var {Pointer.Texture} A texture that controls visibility of individual + /// layers. The first layer is always visible (if the material is not + /// `undefined`), the red channel of the splatmap controls visibility of the + /// second layer, the green channel controls the third layer etc. + Splatmap = pointer_null; + + /// @var {Id.DsGrid} + /// @private + __splatmapGrid = ds_grid_create(1, 1); + ds_grid_clear(__splatmapGrid, 0); + + /// @var {Struct.BBMOD_Vec2} Controls material texture repeat over the + /// terrain mesh. + TextureRepeat = new BBMOD_Vec2(1.0); + + /// @var {Struct.BBMOD_Vec3} The position of the terrain in the world. + Position = new BBMOD_Vec3(); + + /// @var {Struct.BBMOD_Vec2} The width and height of the terrain in world + /// units. + /// @readonly + Size = new BBMOD_Vec2(); + + /// @var {Struct.BBMOD_Vec3} The scale of the terrain. + Scale = new BBMOD_Vec3(1.0, 1.0, 1.0); + + /// @var {Id.DsGrid} __height of individual vertices (on the z axis). + /// @private + __height = ds_grid_create(1, 1); + + /// @var {Id.DsGrid} Normal vector's X component of vertices. + /// @private + __normalX = ds_grid_create(1, 1); + ds_grid_clear(__normalX, 0); + + /// @var {Id.DsGrid} Normal vector's Y component of vertices. + /// @private + __normalY = ds_grid_create(1, 1); + ds_grid_clear(__normalY, 0); + + /// @var {Id.DsGrid} Normal vector's Z component of vertices. + /// @private + __normalZ = ds_grid_create(1, 1); + ds_grid_clear(__normalZ, 1); + + /// @var {Id.DsGrid} Smooth normal vector's X component of vertices. + /// @private + __normalSmoothX = ds_grid_create(1, 1); + ds_grid_clear(__normalSmoothX, 0); + + /// @var {Id.DsGrid} Smooth normal vector's Y component of vertices. + /// @private + __normalSmoothY = ds_grid_create(1, 1); + ds_grid_clear(__normalSmoothY, 0); + + /// @var {Id.DsGrid} Smooth normal vector's Z component of vertices. + /// @private + __normalSmoothZ = ds_grid_create(1, 1); + ds_grid_clear(__normalSmoothZ, 1); + + /// @var {Struct.BBMOD_VertexFormat} The vertex format used by the terrain + /// mesh. + /// @readonly + VertexFormat = BBMOD_VFORMAT_DEFAULT; + + /// @var {Id.VertexBuffer} The vertex buffer or `undefined` if the terrain + /// was not built yet. + /// @readonly + /// @see BBMOD_Terrain.build_mesh + VertexBuffer = undefined; + + /// @func in_bounds(_x, _y) + /// + /// @desc Checks whether the coordinate is within the terrain's bounds. + /// + /// @param {Real} _x The x coordinate to check. + /// @param {Real} _y The y coordinate to check. + /// + /// @return {Bool} Returns `true` if the coordinate is within the terrain's + /// bounds. + static in_bounds = function (_x, _y) { + gml_pragma("forceinline"); + return (_x >= Position.X && _x <= Position.X + (Size.X * Scale.X) + && _y >= Position.Y && _y <= Position.Y + (Size.Y * Scale.Y)); + }; + + /// @func get_random_position() + /// + /// @desc Retrieves a random position on the terrain. + /// + /// @return {Struct.BBMOD_Vec3} A random position on the terrain. + static get_random_position = function () { + gml_pragma("forceinline"); + var _x = Position.X + (random(Size.X) * Scale.X); + var _y = Position.Y + (random(Size.Y) * Scale.Y); + var _z = get_height(_x, _y); + return new BBMOD_Vec3(_x, _y, _z); + }; + + /// @func from_heightmap(_sprite[, _subimage]) + /// + /// @desc Initializes terrain height from a sprite. + /// + /// @param {Asset.GMSprite} _sprite The heightmap sprite. + /// @param {Real} [_subimage] The subimage to use for the heightmap. + /// Defaults to 0. + /// + /// @return {Struct.BBMOD_Terrain} Returns `self`. + static from_heightmap = function (_sprite, _subimage=0) { + var _spriteWidth = sprite_get_width(_sprite); + var _spriteHeight = sprite_get_height(_sprite); + + Size.X = _spriteWidth; + Size.Y = _spriteHeight; + + ds_grid_resize(__height, _spriteWidth, _spriteHeight); + ds_grid_clear(__height, 0); + + ds_grid_resize(__normalX, _spriteWidth, _spriteHeight); + ds_grid_clear(__normalX, 0); + + ds_grid_resize(__normalY, _spriteWidth, _spriteHeight); + ds_grid_clear(__normalY, 0); + + ds_grid_resize(__normalZ, _spriteWidth, _spriteHeight); + ds_grid_clear(__normalZ, 1); + + ds_grid_resize(__normalSmoothX, _spriteWidth, _spriteHeight); + ds_grid_clear(__normalSmoothX, 0); + + ds_grid_resize(__normalSmoothY, _spriteWidth, _spriteHeight); + ds_grid_clear(__normalSmoothY, 0); + + ds_grid_resize(__normalSmoothZ, _spriteWidth, _spriteHeight); + ds_grid_clear(__normalSmoothZ, 1); + + gpu_push_state(); + gpu_set_state(bbmod_gpu_get_default_state()); + + var _surface = surface_create(_spriteWidth, _spriteHeight); + surface_set_target(_surface); + draw_sprite(_sprite, _subimage, 0, 0); + surface_reset_target(); + + gpu_pop_state(); + + var _buffer = buffer_create(_spriteWidth * _spriteHeight * 4, buffer_fast, 1); + buffer_get_surface(_buffer, _surface, 0); + surface_free(_surface); + // Offset to the second byte, just in case the format was ARGB for example. + buffer_seek(_buffer, buffer_seek_start, 1); + + var _j = 0; + repeat (_spriteHeight) + { + var _i = 0; + repeat (_spriteWidth) + { + __height[# _i++, _j] = buffer_read(_buffer, buffer_u8); + buffer_seek(_buffer, buffer_seek_relative, 3); + } + ++_j; + } + + buffer_delete(_buffer); + + return self; + }; + + /// @func smooth_height() + /// + /// @desc Smoothens out the terrain's height. + /// + /// @return {Struct.BBMOD_Terrain} Returns `self`. + static smooth_height = function () { + var _width = ds_grid_width(__height); + var _height = ds_grid_height(__height); + var _heightSmooth = ds_grid_create(_width, _height); + + for (var x1 = 0; x1 < _width; ++x1) + { + for (var y1 = 0; y1 < _height; ++y1) + { + _heightSmooth[# x1, y1] = ds_grid_get_mean(__height, x1 - 1, y1 - 1, x1 + 1, y1 + 1); + } + } + + ds_grid_copy(__height, _heightSmooth); + ds_grid_destroy(_heightSmooth); + + return self; + }; + + /// @func get_height_index(_i, _j) + /// + /// @desc Retrieves terrain's height at given index. + /// + /// @param {Real} _i The X coordinate in the terrain's height grid. + /// @param {Real} _j The Y coordinate in the terrain's height grid. + /// + /// @return {Real} The terrain's height at given index. + /// + /// @see BBMOD_Terrain.__height + static get_height_index = function (_i, _j) { + gml_pragma("forceinline"); + return __height[# + clamp(_i, 0, ds_grid_width(__height) - 1), + clamp(_j, 0, ds_grid_height(__height) - 1) + ]; + }; + + /// @func get_height(_x, _y) + /// + /// @desc Retrieves terrain's height at given coordinate. + /// + /// @param {Real} _x The x position to get the height at. + /// @param {Real} _y The y position to get the height at. + /// + /// @return {Real} The terrain's height at given coordinate or `undefined` + /// if the coordinate is outside of the terrain. + static get_height = function (_x, _y) { + gml_pragma("forceinline"); + var _xScaled = (_x - Position.X) / Scale.X; + var _yScaled = (_y - Position.Y) / Scale.Y; + if (_xScaled < 0.0 || _xScaled > Size.X + || _yScaled < 0.0 || _yScaled > Size.Y) + { + return undefined; + } + var _imax = ds_grid_width(__height) - 1; + var _jmax = ds_grid_height(__height) - 1; + var _i1 = floor(_xScaled); + var _j1 = floor(_yScaled); + var _h1 = __height[# clamp(_i1, 0, _imax), clamp(_j1, 0, _jmax)]; + var _h2 = __height[# clamp(_i1 + 1, 0, _imax), clamp(_j1, 0, _jmax)]; + var _h3 = __height[# clamp(_i1 + 1, 0, _imax), clamp(_j1 + 1, 0, _jmax)]; + var _h4 = __height[# clamp(_i1, 0, _imax), clamp(_j1 + 1, 0, _jmax)]; + var _offsetX = frac(_xScaled); + var _offsetY = frac(_yScaled); + // TODO: Optimize retrieving terrain height + if (_offsetX <= _offsetY) + { + return Position.Z + (_h4 + (_h1-_h4)*(1.0-_offsetY) + (_h3-_h4)*(_offsetX)) * Scale.Z; + } + return Position.Z + (_h2 + (_h1-_h2)*(1.0-_offsetX) + (_h3-_h2)*(_offsetY)) * Scale.Z; + }; + + /// @func get_normal(_x, _y) + /// + /// @desc Retrieves terrain's normal at given coordinate. + /// + /// @param {Real} _x The x position to get the normal at. + /// @param {Real} _y The y position to get the normal at. + /// + /// @return {Struct.BBMOD_Vec3} The terrain's normal at given coordinate or + /// `undefined` if the coordinate is outside of the terrain. + static get_normal = function (_x, _y) { + gml_pragma("forceinline"); + var _xScaled = (_x - Position.X) / Scale.X; + var _yScaled = (_y - Position.Y) / Scale.Y; + if (_xScaled < 0.0 || _xScaled > Size.X + || _yScaled < 0.0 || _yScaled > Size.Y) + { + return undefined; + } + var _imax = ds_grid_width(__height) - 1; + var _jmax = ds_grid_height(__height) - 1; + var _i1 = floor(_xScaled); + var _j1 = floor(_yScaled); + var _h1 = __height[# clamp(_i1, 0, _imax), clamp(_j1, 0, _jmax)]; + var _h2 = __height[# clamp(_i1 + 1, 0, _imax), clamp(_j1, 0, _jmax)]; + var _h3 = __height[# clamp(_i1 + 1, 0, _imax), clamp(_j1 + 1, 0, _jmax)]; + var _h4 = __height[# clamp(_i1, 0, _imax), clamp(_j1 + 1, 0, _jmax)]; + // TODO: Optimize retrieving terrain normal + if (frac(_xScaled) <= frac(_yScaled)) + { + return (new BBMOD_Vec3(0.0, -Scale.Y, (_h1 - _h4) * Scale.Z)) + .Cross(new BBMOD_Vec3(Scale.X, 0.0, (_h3 - _h4) * Scale.Z)) + .Normalize(); + } + return (new BBMOD_Vec3(0.0, Scale.Y, (_h3 - _h2) * Scale.Z)) + .Cross(new BBMOD_Vec3(-Scale.X, 0.0, (_h1 - _h2) * Scale.Z)) + .Normalize(); + }; + + /// @func get_layer(_x, _y[, _threshold]) + /// + /// @desc Retrieves the topmost splatmap layer at given coordinate. + /// + /// @param {Real} _x The x coordinate to retrieve the layer at. + /// @param {Real} _y The y coordinate to retrieve the layer at. + /// @param {Real} [_threshold] The minimum required opacity. Defaults to 0.5. + /// + /// @return {Real} The topmost splatmap layer at given coordinate. Returns + /// `undefined` if the coordinate is outside of the terrain or if no layer + /// was found! + /// + /// @note Method {@link BBMOD_Terrain.build_layer_index} needs to be called + /// first! + static get_layer = function (_x, _y, _threshold=0.5) { + gml_pragma("forceinline"); + var _xScaled = (_x - Position.X) / Scale.X; + var _yScaled = (_y - Position.Y) / Scale.Y; + if (_xScaled < 0.0 || _xScaled > Size.X + || _yScaled < 0.0 || _yScaled > Size.Y) + { + return undefined; + } + var _i = floor((_xScaled / Size.X) * ds_grid_width(__splatmapGrid)); + var _j = floor((_yScaled / Size.Y) * ds_grid_height(__splatmapGrid)); + var _rgba = __splatmapGrid[# _i, _j]; + // TODO: Could be a loop + if (Layer[4] != undefined && (((_rgba & $FF) >> 0) / 255.0) >= _threshold) + { + return 4; + } + if (Layer[3] != undefined && (((_rgba & $FF00) >> 8) / 255.0) >= _threshold) + { + return 3; + } + if (Layer[2] != undefined && (((_rgba & $FF0000) >> 16) / 255.0) >= _threshold) + { + return 2; + } + if (Layer[1] != undefined && (((_rgba & $FF000000) >> 24) / 255.0) >= _threshold) + { + return 1; + } + return ((Layer[0] != undefined) ? 0 : undefined); + }; + + /// @func build_normals() + /// + /// @desc Rebuilds normal vectors. + /// + /// @return {Struct.BBMOD_Terrain} Returns `self`. + static build_normals = function () { + var _i = 0; + repeat (ds_grid_width(__height)) + { + var _j = 0; + repeat (ds_grid_height(__height)) + { + var _nx = get_height_index(_i - 1, _j) - get_height_index(_i + 1, _j); + var _ny = get_height_index(_i, _j - 1) - get_height_index(_i, _j + 1); + var _nz = 2.0; + var _r = sqrt((_nx * _nx) + (_ny * _ny) + (_nz * _nz)); + _nx /= _r; + _ny /= _r; + _nz /= _r; + __normalX[# _i, _j] = _nx; + __normalY[# _i, _j] = _ny; + __normalZ[# _i, _j] = _nz; + ++_j; + } + ++_i; + } + return self; + }; + + /// @func build_smooth_normals() + /// + /// @desc Rebuilds smooth normals. + /// + /// @return {Struct.BBMOD_Terrain} Returns `self`. + /// + /// @note {@link BBMOD_Terrain.build_normals} should be called first! + static build_smooth_normals = function () { + var _width = ds_grid_width(__height); + var _height = ds_grid_height(__height); + for (var x1 = 0; x1 < _width; ++x1) + { + for (var y1 = 0; y1 < _height; ++y1) + { + var _nx = ds_grid_get_mean(__normalX, x1 - 1, y1 - 1, x1 + 1, y1 + 1); + var _ny = ds_grid_get_mean(__normalY, x1 - 1, y1 - 1, x1 + 1, y1 + 1); + var _nz = ds_grid_get_mean(__normalZ, x1 - 1, y1 - 1, x1 + 1, y1 + 1); + var _r = sqrt((_nx * _nx) + (_ny * _ny) + (_nz * _nz)); + _nx /= _r; + _ny /= _r; + _nz /= _r; + __normalSmoothX[# x1, y1] = _nx; + __normalSmoothY[# x1, y1] = _ny; + __normalSmoothZ[# x1, y1] = _nz; + } + } + return self; + }; + + /// @func build_layer_index() + /// + /// @desc Builds an index of layers using the current splatmap. + /// + /// @return {Struct.BBMOD_Terrain} Returns `self`. + static build_layer_index = function () { + var _width = 1.0 / texture_get_texel_width(Splatmap); + var _height = 1.0 / texture_get_texel_height(Splatmap); + var _buffer = array_create(4); + var _surface = surface_create(_width, _height); + + gpu_push_state(); + gpu_set_state(bbmod_gpu_get_default_state()); + gpu_set_blendenable(false); + + for (var i = 0; i < 4; ++i) + { + shader_set(BBMOD_ShExtractSplatmapLayer); + texture_set_stage(shader_get_sampler_index(BBMOD_ShExtractSplatmapLayer, "bbmod_Splatmap"), Splatmap); + shader_set_uniform_i(shader_get_uniform(BBMOD_ShExtractSplatmapLayer, "bbmod_SplatmapIndex"), i); + + surface_set_target(_surface); + draw_clear_alpha(0, 0); + // We just need something that has UVs 0..1 + draw_sprite_stretched(BBMOD_SprDefaultBaseOpacity, 0, 0, 0, _width, _height); + surface_reset_target(); + + shader_reset(); + + _buffer[@ i] = buffer_create(_width * _height * 4, buffer_fast, 1); + buffer_get_surface(_buffer[i], _surface, 0); + // Offset to the second byte, just in case the format was ARGB for example. + buffer_seek(_buffer[i], buffer_seek_start, 1); + } + + gpu_pop_state(); + surface_free(_surface); + + ds_grid_resize(__splatmapGrid, _width, _height); + ds_grid_clear(__splatmapGrid, 0); + + var _j = 0; + repeat (_height) + { + var _i = 0; + repeat (_width) + { + __splatmapGrid[# _i, _j] = (0 + | (buffer_read(_buffer[0], buffer_u8) << 24) + | (buffer_read(_buffer[1], buffer_u8) << 16) + | (buffer_read(_buffer[2], buffer_u8) << 8) + | buffer_read(_buffer[3], buffer_u8)); + buffer_seek(_buffer[0], buffer_seek_relative, 3); + buffer_seek(_buffer[1], buffer_seek_relative, 3); + buffer_seek(_buffer[2], buffer_seek_relative, 3); + buffer_seek(_buffer[3], buffer_seek_relative, 3); + ++_i; + } + ++_j; + } + + buffer_delete(_buffer[0]); + buffer_delete(_buffer[1]); + buffer_delete(_buffer[2]); + buffer_delete(_buffer[3]); + + return self; + }; + + /// @func build_mesh() + /// + /// @desc Rebuilds the terrain's mesh. + /// + /// @return {Struct.BBMOD_Terrain} Returns `self`. + static build_mesh = function () { + if (VertexBuffer != undefined) + { + vertex_delete_buffer(VertexBuffer); + } + var _height = __height; + var _rows = ds_grid_width(_height); + var _cols = ds_grid_height(_height); + var _vbuffer = vertex_create_buffer(); + vertex_begin(_vbuffer, VertexFormat.Raw); + var _i = 0; + repeat (_rows - 1) + { + var _j = 0; + repeat (_cols - 1) + { + var _z1 = _height[# _i, _j]; + var _z2 = _height[# _i + 1, _j]; + var _z3 = _height[# _i + 1, _j + 1]; + var _z4 = _height[# _i, _j + 1]; + + var _x1 = _i; + var _y1 = _j; + var _x2 = _i + 1; + var _y2 = _j; + var _x3 = _i + 1; + var _y3 = _j + 1; + var _x4 = _i; + var _y4 = _j + 1; + + var _u1 = _i / _rows; + var _v1 = _j / _cols; + var _u2 = (_i + 1) / _rows; + var _v2 = _j / _cols; + var _u3 = (_i + 1) / _rows; + var _v3 = (_j + 1) / _cols; + var _u4 = _i / _rows; + var _v4 = (_j + 1) / _cols; + + var _n1X = __normalSmoothX[# _i, _j]; + var _n1Y = __normalSmoothY[# _i, _j]; + var _n1Z = __normalSmoothZ[# _i, _j]; + + var _n2X = __normalSmoothX[# _i + 1, _j]; + var _n2Y = __normalSmoothY[# _i + 1, _j]; + var _n2Z = __normalSmoothZ[# _i + 1, _j]; + + var _n3X = __normalSmoothX[# _i + 1, _j + 1]; + var _n3Y = __normalSmoothY[# _i + 1, _j + 1]; + var _n3Z = __normalSmoothZ[# _i + 1, _j + 1]; + + var _n4X = __normalSmoothX[# _i, _j + 1]; + var _n4Y = __normalSmoothY[# _i, _j + 1]; + var _n4Z = __normalSmoothZ[# _i, _j + 1]; + + //var _t1X = - _n1X * _n1Y; + //var _t1Y = _n1X * _n1X - (-_n1Z) * _n1Z; + //var _t1Z = (-_n1Z) * _n1Y; + + //var _t2X = - _n2X * _n2Y; + //var _t2Y = _n2X * _n2X - (-_n2Z) * _n2Z; + //var _t2Z = (-_n2Z) * _n2Y; + + //var _t3X = - _n3X * _n3Y; + //var _t3Y = _n3X * _n3X - (-_n3Z) * _n3Z; + //var _t3Z = (-_n3Z) * _n3Y; + + //var _t4X = - _n4X * _n4Y; + //var _t4Y = _n4X * _n4X - (-_n4Z) * _n4Z; + //var _t4Z = (-_n4Z) * _n4Y; + + // 1 + // |\ + // 4-3 + var pos1x = _x1; + var pos1y = _y1; + var pos1z = _z1; + var pos2x = _x3; + var pos2y = _y3; + var pos2z = _z3; + var pos3x = _x4; + var pos3y = _y4; + var pos3z = _z4; + var uv1x = _u1; + var uv1y = _v1; + var uv2x = _u3; + var uv2y = _v3; + var uv3x = _u4; + var uv3y = _v4; + var edge1x = pos2x - pos1x; + var edge1y = pos2y - pos1y; + var edge1z = pos2z - pos1z; + var edge2x = pos3x - pos1x; + var edge2y = pos3y - pos1y; + var edge2z = pos3z - pos1z; + var deltaUV1x = uv2x - uv1x; + var deltaUV1y = uv2y - uv1y; + var deltaUV2x = uv3x - uv1x; + var deltaUV2y = uv3y - uv1y; + var f = 1.0 / (deltaUV1x * deltaUV2y - deltaUV2x * deltaUV1y); + var tangent1x = f * (deltaUV2y * edge1x - deltaUV1y * edge2x); + var tangent1y = f * (deltaUV2y * edge1y - deltaUV1y * edge2y); + var tangent1z = f * (deltaUV2y * edge1z - deltaUV1y * edge2z); + var bitangent1x = f * (-deltaUV2x * edge1x + deltaUV1x * edge2x); + var bitangent1y = f * (-deltaUV2x * edge1y + deltaUV1x * edge2y); + var bitangent1z = f * (-deltaUV2x * edge1z + deltaUV1x * edge2z); + var _dot = (new BBMOD_Vec3(_n1X, _n1Y, _n1Z)) + .Cross(new BBMOD_Vec3(tangent1x, tangent1y, tangent1z)) + .Dot(new BBMOD_Vec3(bitangent1x, bitangent1y, bitangent1z)); + var _sign = (_dot < 0.0) ? -1.0 : 1.0; + + vertex_position_3d(_vbuffer, _x1, _y1, _z1); + vertex_normal(_vbuffer, _n1X, _n1Y, _n1Z); + vertex_texcoord(_vbuffer, _u1, _v1); + vertex_float4(_vbuffer, tangent1x, tangent1y, tangent1z, _sign); + + vertex_position_3d(_vbuffer, _x3, _y3, _z3); + vertex_normal(_vbuffer, _n3X, _n3Y, _n3Z); + vertex_texcoord(_vbuffer, _u3, _v3); + vertex_float4(_vbuffer, tangent1x, tangent1y, tangent1z, _sign); + + vertex_position_3d(_vbuffer, _x4, _y4, _z4); + vertex_normal(_vbuffer, _n4X, _n4Y, _n4Z); + vertex_texcoord(_vbuffer, _u4, _v4); + vertex_float4(_vbuffer, tangent1x, tangent1y, tangent1z, _sign); + + // 1-2 + // \| + // 3 + var pos1x = _x1; + var pos1y = _y1; + var pos1z = _z1; + var pos2x = _x2; + var pos2y = _y2; + var pos2z = _z2; + var pos3x = _x3; + var pos3y = _y3; + var pos3z = _z3; + var uv1x = _u1; + var uv1y = _v1; + var uv2x = _u2; + var uv2y = _v2; + var uv3x = _u3; + var uv3y = _v3; + var edge1x = pos2x - pos1x; + var edge1y = pos2y - pos1y; + var edge1z = pos2z - pos1z; + var edge2x = pos3x - pos1x; + var edge2y = pos3y - pos1y; + var edge2z = pos3z - pos1z; + var deltaUV1x = uv2x - uv1x; + var deltaUV1y = uv2y - uv1y; + var deltaUV2x = uv3x - uv1x; + var deltaUV2y = uv3y - uv1y; + var f = 1.0 / (deltaUV1x * deltaUV2y - deltaUV2x * deltaUV1y); + var tangent1x = f * (deltaUV2y * edge1x - deltaUV1y * edge2x); + var tangent1y = f * (deltaUV2y * edge1y - deltaUV1y * edge2y); + var tangent1z = f * (deltaUV2y * edge1z - deltaUV1y * edge2z); + var bitangent1x = f * (-deltaUV2x * edge1x + deltaUV1x * edge2x); + var bitangent1y = f * (-deltaUV2x * edge1y + deltaUV1x * edge2y); + var bitangent1z = f * (-deltaUV2x * edge1z + deltaUV1x * edge2z); + var _dot = (new BBMOD_Vec3(_n1X, _n1Y, _n1Z)) + .Cross(new BBMOD_Vec3(tangent1x, tangent1y, tangent1z)) + .Dot(new BBMOD_Vec3(bitangent1x, bitangent1y, bitangent1z)); + var _sign = (_dot < 0.0) ? -1.0 : 1.0; + + vertex_position_3d(_vbuffer, _x1, _y1, _z1); + vertex_normal(_vbuffer, _n1X, _n1Y, _n1Z); + vertex_texcoord(_vbuffer, _u1, _v1); + vertex_float4(_vbuffer, tangent1x, tangent1y, tangent1z, _sign); + + vertex_position_3d(_vbuffer, _x2, _y2, _z2); + vertex_normal(_vbuffer, _n2X, _n2Y, _n2Z); + vertex_texcoord(_vbuffer, _u2, _v2); + vertex_float4(_vbuffer, tangent1x, tangent1y, tangent1z, _sign); + + vertex_position_3d(_vbuffer, _x3, _y3, _z3); + vertex_normal(_vbuffer, _n3X, _n3Y, _n3Z); + vertex_texcoord(_vbuffer, _u3, _v3); + vertex_float4(_vbuffer, tangent1x, tangent1y, tangent1z, _sign); + + ++_j; + } + ++_i; + } + vertex_end(_vbuffer); + vertex_freeze(_vbuffer); + VertexBuffer = _vbuffer; + return self; + }; + + /// @func submit() + /// + /// @desc Immediately submits the terrain mesh for rendering. + /// + /// @return {Struct.BBMOD_Terrain} Returns `self`. + static submit = function () { + var _matrix = matrix_build(Position.X, Position.Y, Position.Z, 0, 0, 0, Scale.X, Scale.Y, Scale.Z); + var _normalMatrix = bbmod_matrix_build_normalmatrix(_matrix); + matrix_set(matrix_world, _matrix); + var i = 0; + repeat (5) + { + var _mat = Layer[i]; + if (_mat != undefined && _mat.apply(VertexFormat)) + { + var _shaderCurrent = shader_current(); + var _uSplatmap = shader_get_sampler_index(_shaderCurrent, "bbmod_Splatmap"); + var _uSplatmapIndex = shader_get_uniform(_shaderCurrent, "bbmod_SplatmapIndex"); + var _uTextureScale = shader_get_uniform(_shaderCurrent, "bbmod_TextureScale"); + var _uNormalMatrix = shader_get_uniform(_shaderCurrent, "bbmod_NormalMatrix"); + texture_set_stage(_uSplatmap, Splatmap); + shader_set_uniform_i(_uSplatmapIndex, i - 1); + shader_set_uniform_f(_uTextureScale, TextureRepeat.X, TextureRepeat.Y); + shader_set_uniform_matrix_array(_uNormalMatrix, _normalMatrix); + vertex_submit(VertexBuffer, pr_trianglelist, _mat.BaseOpacity); + } + ++i; + } + return self; + }; + + /// @func render() + /// + /// @desc Enqueues the terrain mesh for rendering. + /// + /// @return {Struct.BBMOD_Terrain} Returns `self`. + static render = function () { + var _matrix = matrix_build(Position.X, Position.Y, Position.Z, 0, 0, 0, Scale.X, Scale.Y, Scale.Z); + var _normalMatrix = bbmod_matrix_build_normalmatrix(_matrix); + var i = 0; + repeat (5) + { + var _mat = Layer[i]; + if (_mat != undefined) + { + RenderQueue + .apply_material(_mat, VertexFormat, (i == 0) ? ~0 : (1 << BBMOD_ERenderPass.Forward)) + .begin_conditional_block(); + + if (i == 0) + { + RenderQueue.set_gpu_blendenable(false); + } + else + { + RenderQueue.set_gpu_colorwriteenable(true, true, true, false); + } + + RenderQueue + .set_gpu_zwriteenable(i == 0) + .set_gpu_zfunc((i == 0) ? cmpfunc_lessequal : cmpfunc_equal) + .set_sampler("bbmod_Splatmap", Splatmap) + .set_uniform_i("bbmod_SplatmapIndex", i - 1) + .set_uniform_f2("bbmod_TextureScale", TextureRepeat.X, TextureRepeat.Y) + .set_uniform_matrix_array("bbmod_NormalMatrix", _normalMatrix) + .set_world_matrix(_matrix) + .submit_vertex_buffer(VertexBuffer, pr_trianglelist, _mat.BaseOpacity) + .reset_material() + .end_conditional_block(); + } + ++i; + } + return self; + }; + + static destroy = function () { + Class_destroy(); + ds_grid_destroy(__splatmapGrid); + ds_grid_destroy(__height); + ds_grid_destroy(__normalX); + ds_grid_destroy(__normalY); + ds_grid_destroy(__normalZ); + ds_grid_destroy(__normalSmoothX); + ds_grid_destroy(__normalSmoothY); + ds_grid_destroy(__normalSmoothZ); + if (VertexBuffer != undefined) + { + vertex_delete_buffer(VertexBuffer); + } + return undefined; + }; + + if (_heightmap != undefined) + { + from_heightmap(_heightmap, _subimage); + smooth_height(); + build_normals(); + build_smooth_normals(); + build_mesh(); + } +} diff --git a/scripts/BBMOD_Terrain/BBMOD_Terrain.yy b/scripts/BBMOD_Terrain/BBMOD_Terrain.yy new file mode 100644 index 000000000..06faef38e --- /dev/null +++ b/scripts/BBMOD_Terrain/BBMOD_Terrain.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Terrain", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Terrain", + "path": "folders/_Extensions/BBMOD/Terrain.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_TerrainCollisionModule/BBMOD_TerrainCollisionModule.gml b/scripts/BBMOD_TerrainCollisionModule/BBMOD_TerrainCollisionModule.gml new file mode 100644 index 000000000..336a0a087 --- /dev/null +++ b/scripts/BBMOD_TerrainCollisionModule/BBMOD_TerrainCollisionModule.gml @@ -0,0 +1,60 @@ +/// @func BBMOD_TerrainCollisionModule([_terrain]) +/// +/// @extends BBMOD_ParticleModule +/// +/// @desc A particle module that handles collisions with a terrain. +/// +/// @param {Struct.BBMOD_Terrain} [_terrain] The terrain to collide with. +/// Defaults to `undefined`. +function BBMOD_TerrainCollisionModule(_terrain=undefined) + : BBMOD_ParticleModule() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_Terrain} The terrain to collide with. Default value + /// is `undefined`. + Terrain = _terrain; + + static on_update = function (_emitter, _deltaTime) { + var _terrain = Terrain; + if (!_terrain) + { + return; + } + var _particles = _emitter.Particles; + var _particleIndex = 0; + repeat (_emitter.ParticlesAlive) + { + var _positionX = _particles[# BBMOD_EParticle.PositionX, _particleIndex]; + var _positionY = _particles[# BBMOD_EParticle.PositionY, _particleIndex]; + var _terrainZ = _terrain.get_height(_positionX, _positionY); + if (_terrainZ != undefined) + { + var _positionZ = _particles[# BBMOD_EParticle.PositionZ, _particleIndex]; + if (_positionZ < _terrainZ) + { + _particles[# BBMOD_EParticle.PositionZ, _particleIndex] = _terrainZ; + var _normal = _terrain.get_normal(_positionX, _positionY); + var _velocityX = _particles[# BBMOD_EParticle.VelocityX, _particleIndex]; + var _velocityY = _particles[# BBMOD_EParticle.VelocityY, _particleIndex]; + var _velocityZ = _particles[# BBMOD_EParticle.VelocityZ, _particleIndex]; + var _dot2 = ( + _velocityX * _normal.X + + _velocityY * _normal.Y + + _velocityZ * _normal.Z + ) * 2.0; + var _bounce = _particles[# BBMOD_EParticle.Bounce, _particleIndex]; + _particles[# BBMOD_EParticle.VelocityX, _particleIndex] = + (_velocityX - (_dot2 * _normal.X)) * _bounce; + _particles[# BBMOD_EParticle.VelocityY, _particleIndex] = + (_velocityY - (_dot2 * _normal.Y)) * _bounce; + _particles[# BBMOD_EParticle.VelocityZ, _particleIndex] = + (_velocityZ - (_dot2 * _normal.Z)) * _bounce; + _particles[# BBMOD_EParticle.HasCollided, _particleIndex] = + true; + } + } + ++_particleIndex; + } + }; +} diff --git a/scripts/BBMOD_TerrainCollisionModule/BBMOD_TerrainCollisionModule.yy b/scripts/BBMOD_TerrainCollisionModule/BBMOD_TerrainCollisionModule.yy new file mode 100644 index 000000000..289f10fdf --- /dev/null +++ b/scripts/BBMOD_TerrainCollisionModule/BBMOD_TerrainCollisionModule.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_TerrainCollisionModule", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Collision", + "path": "folders/_Extensions/BBMOD/Particles/Modules/Collision.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Vec2/BBMOD_Vec2.gml b/scripts/BBMOD_Vec2/BBMOD_Vec2.gml new file mode 100644 index 000000000..79caa0503 --- /dev/null +++ b/scripts/BBMOD_Vec2/BBMOD_Vec2.gml @@ -0,0 +1,625 @@ +/// @func BBMOD_Vec2([_x[, _y]]) +/// +/// @desc A 2D vector. +/// +/// @param {Real} [_x] The first component of the vector. Defaults to 0. +/// @param {Real} [_y] The second component of the vector. Defaults to `_x`. +/// +/// @see BBMOD_Vec3 +/// @see BBMOD_Vec4 +function BBMOD_Vec2(_x=0.0, _y=_x) constructor +{ + /// @var {Real} The first component of the vector. + X = _x; + + /// @var {Real} The second component of the vector. + Y = _y; + + /// @func Abs() + /// + /// @desc Creates a new vector where each component is equal to the absolute + /// value of the original component. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec2(-1.0, 2.0).Abs() // => BBMOD_Vec2(1.0, 2.0) + /// ``` + static Abs = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + abs(X), + abs(Y) + ); + }; + + /// @func Add(_v) + /// + /// @desc Adds vectors and returns the result as a new vector. + /// + /// @param {Struct.BBMOD_Vec2} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + static Add = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + X + _v.X, + Y + _v.Y + ); + }; + + /// @func Ceil() + /// + /// @desc Applies function `ceil` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec2(0.2, 1.6).Ceil() // => BBMOD_Vec2(1.0, 2.0) + /// ``` + static Ceil = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + ceil(X), + ceil(Y) + ); + }; + + /// @func Clamp(_min, _max) + /// + /// @desc Clamps each component of the vector between corresponding + /// components of `_min` and `_max` and returns the result as a new vector. + /// + /// @param {Struct.BBMOD_Vec2} _min A vector with minimum components. + /// @param {Struct.Struct.BBMOD_Vec2} _max A vector with maximum components. + /// + /// @return {Struct.BBMOD_Vec2} The resulting vector. + static Clamp = function (_min, _max) { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + clamp(X, _min.X, _max.X), + clamp(Y, _min.Y, _max.Y) + ); + }; + + /// @func ClampLength(_min, _max) + /// + /// @desc Clamps the length of the vector between `_min` and `_max` and + /// returns the result as a new vector. + /// + /// @param {Real} _min The minimum length of the vector. + /// @param {Real} _max The maximum length of the vector. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec2(3.0, 0.0).ClampLength(1.0, 5.0) // => BBMOD_Vec2(3.0, 0.0) + /// new BBMOD_Vec2(3.0, 0.0).ClampLength(4.0, 5.0) // => BBMOD_Vec2(4.0, 0.0) + /// new BBMOD_Vec2(3.0, 0.0).ClampLength(1.0, 2.0) // => BBMOD_Vec2(2.0, 0.0) + /// ``` + static ClampLength = function (_min, _max) { + gml_pragma("forceinline"); + var _length = sqrt( + X * X + + Y * Y + ); + var _newLength = clamp(_length, _min, _max); + return new BBMOD_Vec2( + (X / _length) * _newLength, + (Y / _length) * _newLength + ); + }; + + /// @func Clone() + /// + /// @desc Creates a clone of the vector. + /// + /// @return {Struct.BBMOD_Vec2} The creted vector. + static Clone = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + X, + Y + ); + }; + + /// @func Copy(_dest) + /// + /// @desc Copies components of the vector to the `_dest` vector. + /// + /// @param {Struct.BBMOD_Vec2} _dest The destination vector. + /// + /// @return {Struct.BBMOD_Vec2} Returns `self`. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec2(1.0, 2.0); + /// var _v2 = new BBMOD_Vec2(3.0, 4.0); + /// show_debug_message(_v2) // Prints { X: 3.0, Y: 4.0 } + /// _v1.Copy(_v2); + /// show_debug_message(_v2) // Prints { X: 1.0, Y: 2.0 } + /// ``` + static Copy = function (_dest) { + gml_pragma("forceinline"); + _dest.X = X; + _dest.Y = Y; + return self; + }; + + /// @func Dot(_v) + /// + /// @desc Computes the dot product of this vector and vector `_v`. + /// + /// @param {Struct.BBMOD_Vec2} _v The other vector. + /// + /// @return {Real} The dot product of this vector and vector `_v`. + static Dot = function (_v) { + gml_pragma("forceinline"); + return ( + X * _v.X + + Y * _v.Y + ); + }; + + /// @func Equals(_v) + /// + /// @desc Checks whether this vectors equals to vector `_v`. + /// + /// @param {Struct.BBMOD_Vec2} _v The vector to compare to. + /// + /// @return {Bool} Returns `true` if the two vectors are equal. + static Equals = function (_v) { + gml_pragma("forceinline"); + return ( + X == _v.X + && Y == _v.Y + ); + }; + + /// @func Floor() + /// + /// @desc Applies function `floor` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec2(0.2, 1.6).Floor() // => BBMOD_Vec2(0.0, 1.0) + /// ``` + static Floor = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + floor(X), + floor(Y) + ); + }; + + /// @func Frac() + /// + /// @desc Applies function `frac` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec2(0.2, 1.6).Frac() // => BBMOD_Vec2(0.2, 0.6) + /// ``` + static Frac = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + frac(X), + frac(Y) + ); + }; + + /// @func FromArray(_array[, _index]) + /// + /// @desc Loads vector components from an array. + /// + /// @param {Array} _array The array to read the components from. + /// @param {Real} [_index] The index to start reading the vector components + /// from. Defaults to 0. + /// + /// @return {Struct.BBMOD_Vec2} Returns `self`. + static FromArray = function (_array, _index=0) { + gml_pragma("forceinline"); + X = _array[_index]; + Y = _array[_index + 1]; + return self; + }; + + /// @func FromBarycentric(_v1, _v2, _v3, _f, _g) + /// + /// @desc Computes the vector components using a formula + /// `_v1 + _f * (_v2 - _v1) + _g * (_v3 - _v1)`. + /// + /// @param {Struct.BBMOD_Vec2} _v1 The first point of a triangle. + /// @param {Struct.BBMOD_Vec2} _v2 The second point of a triangle. + /// @param {Struct.BBMOD_Vec2} _v3 The third point of a triangle. + /// @param {Real} _f The weighting factor between `_v1` and `_v2`. + /// @param {Real} _g The weighting factor between `_v1` and `_v3`. + /// + /// @return {Struct.BBMOD_Vec2} Returns `self`. + static FromBarycentric = function (_v1, _v2, _v3, _f, _g) { + gml_pragma("forceinline"); + var _v1X = _v1.X; + var _v1Y = _v1.Y; + X = _v1X + _f * (_v2.X - _v1X) + _g * (_v3.X - _v1X); + Y = _v1Y + _f * (_v2.Y - _v1Y) + _g * (_v3.Y - _v1Y); + return self; + }; + + /// @func FromBuffer(_buffer, _type) + /// + /// @desc Loads vector components from a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to read the components from. + /// @param {Constant.BufferDataType} _type The type of each component. + /// + /// @return {Struct.BBMOD_Vec2} Returns `self`. + static FromBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + X = buffer_read(_buffer, _type); + Y = buffer_read(_buffer, _type); + return self; + }; + + /// @func Length() + /// + /// @desc Computes the length of the vector. + /// + /// @return {Real} The length of the vector. + static Length = function () { + gml_pragma("forceinline"); + return sqrt( + X * X + + Y * Y + ); + }; + + /// @func LengthSqr() + /// + /// @desc Computes a squared length of the vector. + /// + /// @return {Real} The squared length of the vector. + static LengthSqr = function () { + gml_pragma("forceinline"); + return ( + X * X + + Y * Y + ); + }; + + /// @func Lerp(_v, _amount) + /// + /// @desc Linearly interpolates between vector `_v` by the given amount. + /// + /// @param {Struct.BBMOD_Vec2} _v The vector to interpolate with. + /// @param {Real} _amount The interpolation factor. + static Lerp = function (_v, _amount) { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + lerp(X, _v.X, _amount), + lerp(Y, _v.Y, _amount) + ); + }; + + /// @func MaxComponent() + /// + /// @desc Computes the greatest component of the vector. + /// + /// @return {Real} The greates component of the vector. + static MaxComponent = function () { + gml_pragma("forceinline"); + return max( + X, + Y, + ); + }; + + /// @func Maximize(_v) + /// + /// @desc Creates a new vector where each component is the maximum component + /// from this vector and vector `_v`. + /// + /// @param {Struct.BBMOD_Vec2} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec2(1.0, 4.0); + /// var _v2 = new BBMOD_Vec2(2.0, 3.0); + /// var _vMax = _v1.Maximize(_v2); // Equals to BBMOD_Vec2(2.0, 4.0) + /// ``` + static Maximize = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + max(X, _v.X), + max(Y, _v.Y) + ); + }; + + /// @func MinComponent() + /// + /// @desc Computes the smallest component of the vector. + /// + /// @return {Real} The smallest component of the vector. + static MinComponent = function () { + gml_pragma("forceinline"); + return min( + X, + Y, + ); + }; + + /// @func Minimize(_v) + /// + /// @desc Creates a new vector where each component is the minimum component + /// from this vector and vector `_v`. + /// + /// @param {Struct.BBMOD_Vec2} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec2(1.0, 4.0); + /// var _v2 = new BBMOD_Vec2(2.0, 3.0); + /// var _vMin = _v1.Minimize(_v2); // Equals to BBMOD_Vec2(1.0, 3.0) + /// ``` + static Minimize = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + min(X, _v.X), + min(Y, _v.Y) + ); + }; + + /// @func Mul(_v) + /// + /// @desc Multiplies the vector with vector `_v` and returns the result + /// as a new vector. + /// + /// @param {Struct.BBMOD_Vec2} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + static Mul = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + X * _v.X, + Y * _v.Y + ); + }; + + /// @func Normalize() + /// + /// @desc Normalizes the vector and returns the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + static Normalize = function () { + gml_pragma("forceinline"); + var _lengthSqr = ( + X * X + + Y * Y + ); + if (_lengthSqr >= math_get_epsilon()) + { + var _n = 1.0 / sqrt(_lengthSqr); + return new BBMOD_Vec2( + X * _n, + Y * _n + ); + } + return new BBMOD_Vec2( + X, + Y + ); + }; + + /// @func Reflect(_v) + /// + /// @desc Reflects the vector from vector `_v` and returns the result + /// as a new vector. + /// + /// @param {Struct.BBMOD_Vec2} _v The vector to reflect from. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + static Reflect = function (_v) { + gml_pragma("forceinline"); + var _dot2 = ( + X * _v.X + + Y * _v.Y + ) * 2.0; + return new BBMOD_Vec2( + X - (_dot2 * _v.X), + Y - (_dot2 * _v.Y) + ); + }; + + /// @func Round() + /// + /// @desc Applies function `round` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec2(0.2, 1.6).Round() // => BBMOD_Vec2(0.0, 2.0) + /// ``` + static Round = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec2( + round(X), + round(Y) + ); + }; + + /// @func Scale(_s) + /// + /// @desc Scales each component of the vector by `_s` and returns the result + /// as a new vector. + /// + /// @param {Real} _s The value to scale the components by. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec2(1.0, 2.0).Scale(2.0) // => BBMOD_Vec2(2.0, 4.0) + /// ``` + static Scale = function (_s) { + gml_pragma("forceinline") + return new BBMOD_Vec2( + X * _s, + Y * _s + ); + }; + + /// @func Get(_index) + /// + /// @desc Retrieves vector component at given index (0 is X, 1 is Y, etc.). + /// + /// @param {Real} _index The index of the component. + /// + /// @return {Real} The value of the vector component at given index. + /// + /// @throws {BBMOD_OutOfRangeException} If an invalid index is passed. + static Get = function (_index) { + gml_pragma("forceinline"); + switch (_index) + { + case 0: + return X; + + case 1: + return Y; + } + throw new BBMOD_OutOfRangeException(); + }; + + /// @func Set([_x[, _y]]) + /// + /// @desc Sets vector components in-place. + /// + /// @param {Real} [_x] The new value of the first component. Defaults to 0. + /// @param {Real} [_y] The new value of the second component. Defaults to `_x`. + /// + /// @return {Struct.BBMOD_Vec2} Returns `self`. + static Set = function (_x=0.0, _y=undefined) { + gml_pragma("forceinline"); + X = _x; + Y = _y ?? X; + return self; + }; + + /// @func SetIndex(_index, _value) + /// + /// @desc Sets vector component in-place. + /// + /// @param {Real} _index The index of the component, starting at 0. + /// @param {Real} _value The new value of the component. + /// + /// @return {Struct.BBMOD_Vec2} Returns `self`. + /// + /// @throws {BBMOD_OutOfRangeException} If the given index is out of range + /// of possible values. + static SetIndex = function (_index, _value) { + gml_pragma("forceinline"); + switch (_index) + { + case 0: + X = _value; + break; + + case 1: + Y = _value; + break; + + default: + throw new BBMOD_OutOfRangeException(); + break; + } + return self; + }; + + /// @func Sub(_v) + /// + /// @desc Subtracts vector `_v` from this vector and returns the result + /// as a new vector. + /// + /// @param {Struct.BBMOD_Vec2} _v The vector to subtract from this one. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec2(1.0, 2.0); + /// var _v2 = new BBMOD_Vec2(3.0, 4.0); + /// var _v3 = _v1.Sub(_v); // Equals to BBMOD_Vec2(-2.0, -2.0) + /// ``` + static Sub = function (_v) { + gml_pragma("forceinline") + return new BBMOD_Vec2( + X - _v.X, + Y - _v.Y + ); + }; + + /// @func ToArray([_array[, _index]]) + /// + /// @desc Writes the components of the vector into the target array. + /// + /// @param {Array} [_array] The array to write to. If `undefined` a + /// new one of required size is created. + /// @param {Real} [_index] The starting index within the target array. + /// Defaults to 0. + /// + /// @return {Array} The target array. + static ToArray = function (_array=undefined, _index=0) { + gml_pragma("forceinline"); + _array ??= array_create(2, 0.0); + _array[@ _index] = X; + _array[@ _index + 1] = Y; + return _array; + }; + + /// @func ToBuffer(_buffer, _type) + /// + /// @desc Writes the components of the vector into the buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to write to. + /// @param {Constant.BufferDataType} _type The type of the components. + /// + /// @return {Struct.BBMOD_Vec2} Returns `self`. + static ToBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + buffer_write(_buffer, _type, X); + buffer_write(_buffer, _type, Y); + return self; + }; + + /// @func Transform(_matrix) + /// + /// @desc Transforms vector `(X, Y, 0.0, 1.0)` by a matrix and returns the + /// result as a new vector. + /// + /// @param {Array} _matrix The matrix to transform the vector by. + /// + /// @return {Struct.BBMOD_Vec2} The created vector. + static Transform = function (_matrix) { + gml_pragma("forceinline") + var _res = matrix_transform_vertex(_matrix, X, Y, 0.0); + return new BBMOD_Vec2( + _res[0], + _res[1] + ); + }; +} diff --git a/scripts/BBMOD_Vec2/BBMOD_Vec2.yy b/scripts/BBMOD_Vec2/BBMOD_Vec2.yy new file mode 100644 index 000000000..b2098c626 --- /dev/null +++ b/scripts/BBMOD_Vec2/BBMOD_Vec2.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Vec2", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Math", + "path": "folders/_Extensions/BBMOD/Core/Math.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Vec3/BBMOD_Vec3.gml b/scripts/BBMOD_Vec3/BBMOD_Vec3.gml new file mode 100644 index 000000000..807716181 --- /dev/null +++ b/scripts/BBMOD_Vec3/BBMOD_Vec3.gml @@ -0,0 +1,737 @@ +/// @macro {Struct.BBMOD_Vec3} A shorthand for `new BBMOD_Vec3(1, 0, 0)`. +/// @see BBMOD_VEC3_RIGHT +/// @see BBMOD_VEC3_UP +/// @see BBMOD_Vec3 +#macro BBMOD_VEC3_FORWARD new BBMOD_Vec3(1.0, 0.0, 0.0) + +/// @macro {Struct.BBMOD_Vec3} A shorthand for `new BBMOD_Vec3(0, 1, 0)`. +/// @see BBMOD_VEC3_FORWARD +/// @see BBMOD_VEC3_UP +/// @see BBMOD_Vec3 +#macro BBMOD_VEC3_RIGHT new BBMOD_Vec3(0.0, 1.0, 0.0) + +/// @macro {Struct.BBMOD_Vec3} A shorthand for `new BBMOD_Vec3(0, 0, 1)`. +/// @see BBMOD_VEC3_RIGHT +/// @see BBMOD_VEC3_FORWARD +/// @see BBMOD_Vec3 +#macro BBMOD_VEC3_UP new BBMOD_Vec3(0.0, 0.0, 1.0) + +/// @func BBMOD_Vec3([_x[, _y, _z]]) +/// +/// @desc A 3D vector. +/// +/// @param {Real} [_x] The first component of the vector. Defaults to 0. +/// @param {Real} [_y] The second component of the vector. Defaults to `_x`. +/// @param {Real} [_z] The third component of the vector. Defaults to `_x`. +/// +/// @see BBMOD_Vec2 +/// @see BBMOD_Vec4 +function BBMOD_Vec3(_x=0.0, _y=_x, _z=_x) constructor +{ + /// @var {Real} The first component of the vector. + X = _x; + + /// @var {Real} The second component of the vector. + Y = _y; + + /// @var {Real} The third component of the vector. + Z = _z; + + /// @func Abs() + /// + /// @desc Creates a new vector where each component is equal to the absolute + /// value of the original component. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec3(-1.0, 2.0, -3.0).Abs() // => BBMOD_Vec3(1.0, 2.0, 3.0) + /// ``` + static Abs = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + abs(X), + abs(Y), + abs(Z) + ); + }; + + /// @func Add(_v) + /// + /// @desc Adds vectors and returns the result as a new vector. + /// + /// @param {Struct.BBMOD_Vec3} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + static Add = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + X + _v.X, + Y + _v.Y, + Z + _v.Z + ); + }; + + /// @func Ceil() + /// + /// @desc Applies function `ceil` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec3(0.2, 1.6, 2.4).Ceil() // => BBMOD_Vec3(1.0, 2.0, 3.0) + /// ``` + static Ceil = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + ceil(X), + ceil(Y), + ceil(Z) + ); + }; + + /// @func Clamp(_min, _max) + /// + /// @desc Clamps each component of the vector between corresponding + /// components of `_min` and `_max` and returns the result as a new vector. + /// + /// @param {Struct.BBMOD_Vec3} _min A vector with minimum components. + /// @param {Struct.BBMOD_Vec3} _max A vector with maximum components. + /// + /// @return {Struct.BBMOD_Vec3} The resulting vector. + static Clamp = function (_min, _max) { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + clamp(X, _min.X, _max.X), + clamp(Y, _min.Y, _max.Y), + clamp(Z, _min.Z, _max.Z) + ); + }; + + /// @func ClampLength(_min, _max) + /// + /// @desc Clamps the length of the vector between `_min` and `_max` and + /// returns the result as a new vector. + /// + /// @param {Real} _min The minimum length of the vector. + /// @param {Real} _max The maximum length of the vector. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + /// + /// @example + /// ```gml + /// // => BBMOD_Vec3(3.0, 0.0, 0.0): + /// new BBMOD_Vec3(3.0, 0.0, 0.0).ClampLength(1.0, 5.0) + /// // => BBMOD_Vec3(4.0, 0.0, 0.0): + /// new BBMOD_Vec3(3.0, 0.0, 0.0).ClampLength(4.0, 5.0) + /// // => BBMOD_Vec3(2.0, 0.0, 0.0): + /// new BBMOD_Vec3(3.0, 0.0, 0.0).ClampLength(1.0, 2.0) + /// ``` + static ClampLength = function (_min, _max) { + gml_pragma("forceinline"); + var _length = sqrt( + X * X + + Y * Y + + Z * Z + ); + var _newLength = clamp(_length, _min, _max); + return new BBMOD_Vec3( + (X / _length) * _newLength, + (Y / _length) * _newLength, + (Z / _length) * _newLength + ); + }; + + /// @func Clone() + /// + /// @desc Creates a clone of the vector. + /// + /// @return {Struct.BBMOD_Vec3} The creted vector. + static Clone = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + X, + Y, + Z + ); + }; + + /// @func Copy(_dest) + /// + /// @desc Copies components of the vector to the `_dest` vector. + /// + /// @param {Struct.BBMOD_Vec3} _dest The destination vector. + /// + /// @return {Struct.BBMOD_Vec3} Returns `self`. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec3(1.0, 2.0, 3.0); + /// var _v2 = new BBMOD_Vec3(4.0, 5.0, 6.0); + /// show_debug_message(_v2) // Prints { X: 4.0, Y: 5.0, Z: 6.0 } + /// _v1.Copy(_v2); + /// show_debug_message(_v2) // Prints { X: 1.0, Y: 2.0, Z: 3.0 } + /// ``` + static Copy = function (_dest) { + gml_pragma("forceinline"); + _dest.X = X; + _dest.Y = Y; + _dest.Z = Z; + return self; + }; + + /// @func Cross(_v) + /// + /// @desc Computes a cross product of this vector and vector `_v` and returns + /// the result as a new vector. + /// + /// @param {Struct.BBMOD_Vec3} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + static Cross = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + Y * _v.Z - Z * _v.Y, + Z * _v.X - X * _v.Z, + X * _v.Y - Y * _v.X + ); + }; + + /// @func Dot(_v) + /// + /// @desc Computes the dot product of this vector and vector `_v`. + /// + /// @param {Struct.BBMOD_Vec3} _v The other vector. + /// + /// @return {Real} The dot product of this vector and vector `_v`. + static Dot = function (_v) { + gml_pragma("forceinline"); + return ( + X * _v.X + + Y * _v.Y + + Z * _v.Z + ); + }; + + /// @func Equals(_v) + /// + /// @desc Checks whether this vectors equals to vector `_v`. + /// + /// @param {Struct.BBMOD_Vec3} _v The vector to compare to. + /// + /// @return {Bool} Returns `true` if the two vectors are equal. + static Equals = function (_v) { + gml_pragma("forceinline"); + return ( + X == _v.X + && Y == _v.Y + && Z == _v.Z + ); + }; + + /// @func Floor() + /// + /// @desc Applies function `floor` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec3(0.2, 1.6, 2.4).Floor() // => BBMOD_Vec3(0.0, 1.0, 2.0) + /// ``` + static Floor = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + floor(X), + floor(Y), + floor(Z) + ); + }; + + /// @func Frac() + /// + /// @desc Applies function `frac` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec3(0.2, 1.6, 2.4).Frac() // => BBMOD_Vec3(0.2, 0.6, 0.4) + /// ``` + static Frac = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + frac(X), + frac(Y), + frac(Z) + ); + }; + + /// @func FromArray(_array[, _index]) + /// + /// @desc Loads vector components from an array. + /// + /// @param {Array} _array The array to read the components from. + /// @param {Real} [_index] The index to start reading the vector components + /// from. Defaults to 0. + /// + /// @return {Struct.BBMOD_Vec3} Returns `self`. + static FromArray = function (_array, _index=0) { + gml_pragma("forceinline"); + X = _array[_index]; + Y = _array[_index + 1]; + Z = _array[_index + 2]; + return self; + }; + + /// @func FromBarycentric(_v1, _v2, _v3, _f, _g) + /// + /// @desc Computes the vector components using a formula + /// `_v1 + _f * (_v2 - _v1) + _g * (_v3 - _v1)`. + /// + /// @param {Struct.BBMOD_Vec3} _v1 The first point of a triangle. + /// @param {Struct.BBMOD_Vec3} _v2 The second point of a triangle. + /// @param {Struct.BBMOD_Vec3} _v3 The third point of a triangle. + /// @param {Real} _f The weighting factor between `_v1` and `_v2`. + /// @param {Real} _g The weighting factor between `_v1` and `_v3`. + /// + /// @return {Struct.BBMOD_Vec3} Returns `self`. + static FromBarycentric = function (_v1, _v2, _v3, _f, _g) { + gml_pragma("forceinline"); + var _v1X = _v1.X; + var _v1Y = _v1.Y; + var _v1Z = _v1.Z; + X = _v1X + _f * (_v2.X - _v1X) + _g * (_v3.X - _v1X); + Y = _v1Y + _f * (_v2.Y - _v1Y) + _g * (_v3.Y - _v1Y); + Z = _v1Z + _f * (_v2.Z - _v1Z) + _g * (_v3.Z - _v1Z); + return self; + }; + + /// @func FromBuffer(_buffer, _type) + /// + /// @desc Loads vector components from a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to read the components from. + /// @param {Constant.BufferDataType} _type The type of each component. + /// + /// @return {Struct.BBMOD_Vec3} Returns `self`. + static FromBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + X = buffer_read(_buffer, _type); + Y = buffer_read(_buffer, _type); + Z = buffer_read(_buffer, _type); + return self; + }; + + /// @func Length() + /// + /// @desc Computes the length of the vector. + /// + /// @return {Real} The length of the vector. + static Length = function () { + gml_pragma("forceinline"); + return sqrt( + X * X + + Y * Y + + Z * Z + ); + }; + + /// @func LengthSqr() + /// + /// @desc Computes a squared length of the vector. + /// + /// @return {Real} The squared length of the vector. + static LengthSqr = function () { + gml_pragma("forceinline"); + return ( + X * X + + Y * Y + + Z * Z + ); + }; + + /// @func Lerp(_v, _amount) + /// + /// @desc Linearly interpolates between vector `_v` by the given amount. + /// + /// @param {Struct.BBMOD_Vec3} _v The vector to interpolate with. + /// @param {Real} _amount The interpolation factor. + static Lerp = function (_v, _amount) { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + lerp(X, _v.X, _amount), + lerp(Y, _v.Y, _amount), + lerp(Z, _v.Z, _amount) + ); + }; + + /// @func MaxComponent() + /// + /// @desc Computes the greatest component of the vector. + /// + /// @return {Real} The greates component of the vector. + static MaxComponent = function () { + gml_pragma("forceinline"); + return max( + X, + Y, + Z, + ); + }; + + /// @func Maximize(_v) + /// + /// @desc Creates a new vector where each component is the maximum component + /// from this vector and vector `_v`. + /// + /// @param {Struct.BBMOD_Vec3} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec3(1.0, 4.0, 5.0); + /// var _v2 = new BBMOD_Vec3(2.0, 3.0, 6.0); + /// var _vMax = _v1.Maximize(_v2); // Equals to BBMOD_Vec3(2.0, 4.0, 6.0) + /// ``` + static Maximize = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + max(X, _v.X), + max(Y, _v.Y), + max(Z, _v.Z) + ); + }; + + /// @func MinComponent() + /// + /// @desc Computes the smallest component of the vector. + /// + /// @return {Real} The smallest component of the vector. + static MinComponent = function () { + gml_pragma("forceinline"); + return min( + X, + Y, + Z, + ); + }; + + /// @func Minimize(_v) + /// + /// @desc Creates a new vector where each component is the minimum component + /// from this vector and vector `_v`. + /// + /// @param {Struct.BBMOD_Vec3} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec3(1.0, 4.0, 5.0); + /// var _v2 = new BBMOD_Vec3(2.0, 3.0, 6.0); + /// var _vMin = _v1.Minimize(_v2); // Equals to BBMOD_Vec3(1.0, 3.0, 5.0) + /// ``` + static Minimize = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + min(X, _v.X), + min(Y, _v.Y), + min(Z, _v.Z) + ); + }; + + /// @func Mul(_v) + /// + /// @desc Multiplies the vector with vector `_v` and returns the result + /// as a new vector. + /// + /// @param {Struct.BBMOD_Vec3} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + static Mul = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + X * _v.X, + Y * _v.Y, + Z * _v.Z + ); + }; + + /// @func Normalize() + /// + /// @desc Normalizes the vector and returns the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + static Normalize = function () { + gml_pragma("forceinline"); + var _lengthSqr = ( + X * X + + Y * Y + + Z * Z + ); + if (_lengthSqr >= math_get_epsilon()) + { + var _n = 1.0 / sqrt(_lengthSqr); + return new BBMOD_Vec3( + X * _n, + Y * _n, + Z * _n + ); + } + return new BBMOD_Vec3( + X, + Y, + Z + ); + }; + + /// @func Orthonormalize(_v) + /// + /// @desc Orthonormalizes the vectors in-place using the Gram–Schmidt process. + /// + /// @param {Struct.BBMOD_Vec3} _v The other vector. + /// + /// @return {Bool} Returns `true` if the vectors were orthonormalized. + static Orthonormalize = function (_v) { + gml_pragma("forceinline"); + + var _v1 = Normalize(); + var _proj = _v1.Scale(_v.Dot(_v1)); + var _v2 = _v.Sub(_proj); + + if (_v2.Length() <= 0.0) + { + return false; + } + + _v1.Copy(self); + _v2.Normalize().Copy(_v); + + return true; + }; + + /// @func Reflect(_v) + /// + /// @desc Reflects the vector from vector `_v` and returns the result + /// as a new vector. + /// + /// @param {Struct.BBMOD_Vec3} _v The vector to reflect from. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + static Reflect = function (_v) { + gml_pragma("forceinline"); + var _dot2 = ( + X * _v.X + + Y * _v.Y + + Z * _v.Z + ) * 2.0; + return new BBMOD_Vec3( + X - (_dot2 * _v.X), + Y - (_dot2 * _v.Y), + Z - (_dot2 * _v.Z) + ); + }; + + /// @func Round() + /// + /// @desc Applies function `round` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec3(0.2, 1.6, 2.4).Round() // => BBMOD_Vec3(0.0, 2.0, 2.0) + /// ``` + static Round = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec3( + round(X), + round(Y), + round(Z) + ); + }; + + /// @func Scale(_s) + /// + /// @desc Scales each component of the vector by `_s` and returns the result + /// as a new vector. + /// + /// @param {Real} _s The value to scale the components by. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + /// + /// @example + /// ```gml + /// new BBMOD_Vec3(1.0, 2.0, 3.0).Scale(2.0) // => BBMOD_Vec3(2.0, 4.0, 6.0) + /// ``` + static Scale = function (_s) { + gml_pragma("forceinline") + return new BBMOD_Vec3( + X * _s, + Y * _s, + Z * _s + ); + }; + + /// @func Get(_index) + /// + /// @desc Retrieves vector component at given index (0 is X, 1 is Y, etc.). + /// + /// @param {Real} _index The index of the component. + /// + /// @return {Real} The value of the vector component at given index. + /// + /// @throws {BBMOD_OutOfRangeException} If an invalid index is passed. + static Get = function (_index) { + gml_pragma("forceinline"); + switch (_index) + { + case 0: + return X; + + case 1: + return Y; + + case 2: + return Z; + } + throw new BBMOD_OutOfRangeException(); + }; + + /// @func Set([_x[, _y, _z]]) + /// + /// @desc Sets vector components in-place. + /// + /// @param {Real} [_x] The new value of the first component. Defaults to 0. + /// @param {Real} [_y] The new value of the second component. Defaults to `_x`. + /// @param {Real} [_z] The new value of the third component. Defaults to `_x`. + /// + /// @return {Struct.BBMOD_Vec3} Returns `self`. + static Set = function (_x=0.0, _y=_x, _z=_x) { + gml_pragma("forceinline"); + X = _x; + Y = _y; + Z = _z; + return self; + }; + + /// @func SetIndex(_index, _value) + /// + /// @desc Sets vector component in-place. + /// + /// @param {Real} _index The index of the component, starting at 0. + /// @param {Real} _value The new value of the component. + /// + /// @return {Struct.BBMOD_Vec3} Returns `self`. + /// + /// @throws {BBMOD_OutOfRangeException} If the given index is out of range + /// of possible values. + static SetIndex = function (_index, _value) { + gml_pragma("forceinline"); + switch (_index) + { + case 0: + X = _value; + break; + + case 1: + Y = _value; + break; + + case 2: + Z = _value; + break; + + default: + throw new BBMOD_OutOfRangeException(); + break; + } + return self; + }; + + /// @func Sub(_v) + /// + /// @desc Subtracts vector `_v` from this vector and returns the result + /// as a new vector. + /// + /// @param {Struct.BBMOD_Vec3} _v The vector to subtract from this one. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec3(1.0, 2.0, 3.0); + /// var _v2 = new BBMOD_Vec3(4.0, 5.0, 6.0); + /// var _v3 = _v1.Sub(_v2); // Equals to BBMOD_Vec3(-3.0, -3.0, -3.0) + /// ``` + static Sub = function (_v) { + gml_pragma("forceinline") + return new BBMOD_Vec3( + X - _v.X, + Y - _v.Y, + Z - _v.Z + ); + }; + + /// @func ToArray([_array[, _index]]) + /// + /// @desc Writes the components of the vector into the target array. + /// + /// @param {Array} [_array] The array to write to. If `undefined` a + /// new one of required size is created. + /// + /// @param {Real} [_index] The starting index within the target array. + /// Defaults to 0. + /// + /// @return {Array} The target array. + static ToArray = function (_array=undefined, _index=0) { + gml_pragma("forceinline"); + _array ??= array_create(3, 0.0); + _array[@ _index] = X; + _array[@ _index + 1] = Y; + _array[@ _index + 2] = Z; + return _array; + }; + + /// @func ToBuffer(_buffer, _type) + /// + /// @desc Writes the components of the vector into the buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to write to. + /// @param {Constant.BufferDataType} _type The type of the components. + /// + /// @return {Struct.BBMOD_Vec3} Returns `self`. + static ToBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + buffer_write(_buffer, _type, X); + buffer_write(_buffer, _type, Y); + buffer_write(_buffer, _type, Z); + return self; + }; + + /// @func Transform(_matrix) + /// + /// @desc Transforms vector `(X, Y, Z, 1.0)` by a matrix and returns the result + /// as a new vector. + /// + /// @param {Array} _matrix The matrix to transform the vector by. + /// + /// @return {Struct.BBMOD_Vec3} The created vector. + static Transform = function (_matrix) { + gml_pragma("forceinline") + var _res = matrix_transform_vertex(_matrix, X, Y, Z); + return new BBMOD_Vec3( + _res[0], + _res[1], + _res[2] + ); + }; +} diff --git a/scripts/BBMOD_Vec3/BBMOD_Vec3.yy b/scripts/BBMOD_Vec3/BBMOD_Vec3.yy new file mode 100644 index 000000000..83ddd8752 --- /dev/null +++ b/scripts/BBMOD_Vec3/BBMOD_Vec3.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Vec3", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Math", + "path": "folders/_Extensions/BBMOD/Core/Math.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Vec4/BBMOD_Vec4.gml b/scripts/BBMOD_Vec4/BBMOD_Vec4.gml new file mode 100644 index 000000000..610651a26 --- /dev/null +++ b/scripts/BBMOD_Vec4/BBMOD_Vec4.gml @@ -0,0 +1,734 @@ +/// @func BBMOD_Vec4([_x[, _y, _z, _w]]) +/// +/// @desc A 4D vector. +/// +/// @param {Real} [_x] The first component of the vector. Defaults to 0. +/// @param {Real} [_y] The second component of the vector. Defaults to `_x`. +/// @param {Real} [_z] The third component of the vector. Defaults to `_x`. +/// @param {Real} [_w] The fourth component of the vector. Defaults to `_x`. +/// +/// @see BBMOD_Vec2 +/// @see BBMOD_Vec3 +function BBMOD_Vec4(_x=0.0, _y=_x, _z=_x, _w=_x) constructor +{ + /// @var {Real} The first component of the vector. + X = _x; + + /// @var {Real} The second component of the vector. + Y = _y; + + /// @var {Real} The third component of the vector. + Z = _z; + + /// @var {Real} The fourth component of the vector. + W = _w; + + /// @func Abs() + /// + /// @desc Creates a new vector where each component is equal to the absolute + /// value of the original component. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + /// + /// @example + /// ```gml + /// // => BBMOD_Vec4(1.0, 2.0, 3.0, 4.0): + /// new BBMOD_Vec4(-1.0, 2.0, -3.0, 4.0).Abs() + /// ``` + static Abs = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + abs(X), + abs(Y), + abs(Z), + abs(W) + ); + }; + + /// @func Add(_v) + /// + /// @desc Adds vectors and returns the result as a new vector. + /// + /// @param {Struct.BBMOD_Vec4} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + static Add = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + X + _v.X, + Y + _v.Y, + Z + _v.Z, + W + _v.W + ); + }; + + /// @func Ceil() + /// + /// @desc Applies function `ceil` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + /// + /// @example + /// ```gml + /// // => BBMOD_Vec4(1.0, 2.0, 3.0, 4.0): + /// new BBMOD_Vec4(0.2, 1.6, 2.4, 3.1).Ceil() + /// ``` + static Ceil = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + ceil(X), + ceil(Y), + ceil(Z), + ceil(W) + ); + }; + + /// @func Clamp(_min, _max) + /// + /// @desc Clamps each component of the vector between corresponding + /// components of `_min` and `_max` and returns the result as a new vector. + /// + /// @param {Struct.BBMOD_Vec4} _min A vector with minimum components. + /// @param {Struct.BBMOD_Vec4} _max A vector with maximum components. + /// + /// @return {Struct.BBMOD_Vec4} The resulting vector. + static Clamp = function (_min, _max) { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + clamp(X, _min.X, _max.X), + clamp(Y, _min.Y, _max.Y), + clamp(Z, _min.Z, _max.Z), + clamp(W, _min.W, _max.W) + ); + }; + + /// @func ClampLength(_min, _max) + /// + /// @desc Clamps the length of the vector between `_min` and `_max` and + /// returns the result as a new vector. + /// + /// @param {Real} _min The minimum length of the vector. + /// @param {Real} _max The maximum length of the vector. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + /// + /// @example + /// ```gml + /// // => BBMOD_Vec4(3.0, 0.0, 0.0, 0.0): + /// new BBMOD_Vec4(3.0, 0.0, 0.0, 0.0).ClampLength(1.0, 5.0) + /// // => BBMOD_Vec4(4.0, 0.0, 0.0, 0.0): + /// new BBMOD_Vec4(3.0, 0.0, 0.0, 0.0).ClampLength(4.0, 5.0) + /// // => BBMOD_Vec4(2.0, 0.0, 0.0, 0.0): + /// new BBMOD_Vec4(3.0, 0.0, 0.0, 0.0).ClampLength(1.0, 2.0) + /// ``` + static ClampLength = function (_min, _max) { + gml_pragma("forceinline"); + var _length = sqrt( + X * X + + Y * Y + + Z * Z + + W * W + ); + var _newLength = clamp(_length, _min, _max); + return new BBMOD_Vec4( + (X / _length) * _newLength, + (Y / _length) * _newLength, + (Z / _length) * _newLength, + (W / _length) * _newLength + ); + }; + + /// @func Clone() + /// + /// @desc Creates a clone of the vector. + /// + /// @return {Struct.BBMOD_Vec4} The creted vector. + static Clone = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + X, + Y, + Z, + W + ); + }; + + /// @func Copy(_dest) + /// + /// @desc Copies components of the vector to the `_dest` vector. + /// + /// @param {Struct.BBMOD_Vec4} _dest The destination vector. + /// + /// @return {Struct.BBMOD_Vec4} Returns `self`. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec4(1.0, 2.0, 3.0, 4.0); + /// var _v2 = new BBMOD_Vec4(5.0, 6.0, 7.0, 8.0); + /// show_debug_message(_v2) // Prints { X: 5.0, Y: 6.0, Z: 7.0, W: 8.0 } + /// _v1.Copy(_v2); + /// show_debug_message(_v2) // Prints { X: 1.0, Y: 2.0, Z: 3.0, W: 4.0 } + /// ``` + static Copy = function (_dest) { + gml_pragma("forceinline"); + _dest.X = X; + _dest.Y = Y; + _dest.Z = Z; + _dest.W = W; + return self; + }; + + /// @func Dot(_v) + /// + /// @desc Computes the dot product of this vector and vector `_v`. + /// + /// @param {Struct.BBMOD_Vec4} _v The other vector. + /// + /// @return {Real} The dot product of this vector and vector `_v`. + static Dot = function (_v) { + gml_pragma("forceinline"); + return ( + X * _v.X + + Y * _v.Y + + Z * _v.Z + + W * _v.W + ); + }; + + /// @func Equals(_v) + /// + /// @desc Checks whether this vectors equals to vector `_v`. + /// + /// @param {Struct.BBMOD_Vec4} _v The vector to compare to. + /// + /// @return {Bool} Returns `true` if the two vectors are equal. + static Equals = function (_v) { + gml_pragma("forceinline"); + return ( + X == _v.X + && Y == _v.Y + && Z == _v.Z + && W == _v.W + ); + }; + + /// @func Floor() + /// + /// @desc Applies function `floor` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + /// + /// @example + /// ```gml + /// // => BBMOD_Vec4(0.0, 1.0, 2.0, 3.0): + /// new BBMOD_Vec4(0.2, 1.6, 2.4, 3.1).Floor() + /// ``` + static Floor = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + floor(X), + floor(Y), + floor(Z), + floor(W) + ); + }; + + /// @func Frac() + /// + /// @desc Applies function `frac` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + /// + /// @example + /// ```gml + /// // => BBMOD_Vec4(0.2, 0.6, 0.4, 0.1) + /// new BBMOD_Vec4(0.2, 1.6, 2.4, 3.1).Frac() + /// ``` + static Frac = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + frac(X), + frac(Y), + frac(Z), + frac(W) + ); + }; + + /// @func FromArray(_array[, _index]) + /// + /// @desc Loads vector components from an array. + /// + /// @param {Array} _array The array to read the components from. + /// + /// @param {Real} [_index] The index to start reading the vector components + /// from. Defaults to 0. + /// + /// @return {Struct.BBMOD_Vec4} Returns `self`. + static FromArray = function (_array, _index=0) { + gml_pragma("forceinline"); + X = _array[_index]; + Y = _array[_index + 1]; + Z = _array[_index + 2]; + W = _array[_index + 3]; + return self; + }; + + /// @func FromBarycentric(_v1, _v2, _v3, _f, _g) + /// + /// @desc Computes the vector components using a formula + /// `_v1 + _f * (_v2 - _v1) + _g * (_v3 - _v1)`. + /// + /// @param {Struct.BBMOD_Vec4} _v1 The first point of a triangle. + /// @param {Struct.BBMOD_Vec4} _v2 The second point of a triangle. + /// @param {Struct.BBMOD_Vec4} _v3 The third point of a triangle. + /// @param {Real} _f The weighting factor between `_v1` and `_v2`. + /// @param {Real} _g The weighting factor between `_v1` and `_v3`. + /// + /// @return {Struct.BBMOD_Vec4} Returns `self`. + static FromBarycentric = function (_v1, _v2, _v3, _f, _g) { + gml_pragma("forceinline"); + var _v1X = _v1.X; + var _v1Y = _v1.Y; + var _v1Z = _v1.Z; + var _v1W = _v1.W; + X = _v1X + _f * (_v2.X - _v1X) + _g * (_v3.X - _v1X); + Y = _v1Y + _f * (_v2.Y - _v1Y) + _g * (_v3.Y - _v1Y); + Z = _v1Z + _f * (_v2.Z - _v1Z) + _g * (_v3.Z - _v1Z); + W = _v1W + _f * (_v2.W - _v1W) + _g * (_v3.W - _v1W); + return self; + }; + + /// @func FromBuffer(_buffer, _type) + /// + /// @desc Loads vector components from a buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to read the components from. + /// @param {Constant.BufferDataType} _type The type of each component. + /// + /// @return {Struct.BBMOD_Vec4} Returns `self`. + static FromBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + X = buffer_read(_buffer, _type); + Y = buffer_read(_buffer, _type); + Z = buffer_read(_buffer, _type); + W = buffer_read(_buffer, _type); + return self; + }; + + /// @func Length() + /// + /// @desc Computes the length of the vector. + /// + /// @return {Real} The length of the vector. + static Length = function () { + gml_pragma("forceinline"); + return sqrt( + X * X + + Y * Y + + Z * Z + + W * W + ); + }; + + /// @func LengthSqr() + /// + /// @desc Computes a squared length of the vector. + /// + /// @return {Real} The squared length of the vector. + static LengthSqr = function () { + gml_pragma("forceinline"); + return ( + X * X + + Y * Y + + Z * Z + + W * W + ); + }; + + /// @func Lerp(_v, _amount) + /// + /// @desc Linearly interpolates between vector `_v` by the given amount. + /// + /// @param {Struct.BBMOD_Vec4} _v The vector to interpolate with. + /// @param {Real} _amount The interpolation factor. + static Lerp = function (_v, _amount) { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + lerp(X, _v.X, _amount), + lerp(Y, _v.Y, _amount), + lerp(Z, _v.Z, _amount), + lerp(W, _v.W, _amount) + ); + }; + + /// @func MaxComponent() + /// + /// @desc Computes the greatest component of the vector. + /// + /// @return {Real} The greates component of the vector. + static MaxComponent = function () { + gml_pragma("forceinline"); + return max( + X, + Y, + Z, + W, + ); + }; + + /// @func Maximize(_v) + /// + /// @desc Creates a new vector where each component is the maximum component + /// from this vector and vector `_v`. + /// + /// @param {Struct.BBMOD_Vec4} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec4(1.0, 4.0, 5.0, 8.0); + /// var _v2 = new BBMOD_Vec4(2.0, 3.0, 6.0, 7.0); + /// var _vMax = _v1.Maximize(_v2); // Equals to BBMOD_Vec4(2.0, 4.0, 6.0, 8.0) + /// ``` + static Maximize = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + max(X, _v.X), + max(Y, _v.Y), + max(Z, _v.Z), + max(W, _v.W) + ); + }; + + /// @func MinComponent() + /// + /// @desc Computes the smallest component of the vector. + /// + /// @return {Real} The smallest component of the vector. + static MinComponent = function () { + gml_pragma("forceinline"); + return min( + X, + Y, + Z, + W, + ); + }; + + /// @func Minimize(_v) + /// + /// @desc Creates a new vector where each component is the minimum component + /// from this vector and vector `_v`. + /// + /// @param {Struct.BBMOD_Vec4} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec4(1.0, 4.0, 5.0, 8.0); + /// var _v2 = new BBMOD_Vec4(2.0, 3.0, 6.0, 7.0); + /// var _vMin = _v1.Minimize(_v2); // Equals to BBMOD_Vec4(1.0, 3.0, 5.0, 7.0) + /// ``` + static Minimize = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + min(X, _v.X), + min(Y, _v.Y), + min(Z, _v.Z), + min(W, _v.W) + ); + }; + + /// @func Mul(_v) + /// + /// @desc Multiplies the vector with vector `_v` and returns the result + /// as a new vector. + /// + /// @param {Struct.BBMOD_Vec4} _v The other vector. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + static Mul = function (_v) { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + X * _v.X, + Y * _v.Y, + Z * _v.Z, + W * _v.W + ); + }; + + /// @func Normalize() + /// + /// @desc Normalizes the vector and returns the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + static Normalize = function () { + gml_pragma("forceinline"); + var _lengthSqr = ( + X * X + + Y * Y + + Z * Z + + W * W + ); + if (_lengthSqr >= math_get_epsilon()) + { + var _n = 1.0 / sqrt(_lengthSqr); + return new BBMOD_Vec4( + X * _n, + Y * _n, + Z * _n, + W * _n + ); + } + return new BBMOD_Vec4( + X, + Y, + Z, + W + ); + }; + + /// @func Reflect(_v) + /// + /// @desc Reflects the vector from vector `_v` and returns the result + /// as a new vector. + /// + /// @param {Struct.BBMOD_Vec4} _v The vector to reflect from. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + static Reflect = function (_v) { + gml_pragma("forceinline"); + var _dot2 = ( + X * _v.X + + Y * _v.Y + + Z * _v.Z + + W * _v.W + ) * 2.0; + return new BBMOD_Vec4( + X - (_dot2 * _v.X), + Y - (_dot2 * _v.Y), + Z - (_dot2 * _v.Z), + W - (_dot2 * _v.W) + ); + }; + + /// @func Round() + /// + /// @desc Applies function `round` to each component of the vector and returns + /// the result as a new vector. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + /// + /// @example + /// ```gml + /// // => BBMOD_Vec4(0.0, 2.0, 2.0, 3.0): + /// new BBMOD_Vec4(0.2, 1.6, 2.4, 3.1).Round() + /// ``` + static Round = function () { + gml_pragma("forceinline"); + return new BBMOD_Vec4( + round(X), + round(Y), + round(Z), + round(W) + ); + }; + + /// @func Scale(_s) + /// + /// @desc Scales each component of the vector by `_s` and returns the result + /// as a new vector. + /// + /// @param {Real} _s The value to scale the components by. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + /// + /// @example + /// ```gml + /// // => BBMOD_Vec4(2.0, 4.0, 6.0, 8.0): + /// new BBMOD_Vec4(1.0, 2.0, 3.0, 4.0).Scale(2.0) + /// ``` + static Scale = function (_s) { + gml_pragma("forceinline") + return new BBMOD_Vec4( + X * _s, + Y * _s, + Z * _s, + W * _s + ); + }; + + /// @func Get(_index) + /// + /// @desc Retrieves vector component at given index (0 is X, 1 is Y, etc.). + /// + /// @param {Real} _index The index of the component. + /// + /// @return {Real} The value of the vector component at given index. + /// + /// @throws {BBMOD_OutOfRangeException} If an invalid index is passed. + static Get = function (_index) { + gml_pragma("forceinline"); + switch (_index) + { + case 0: + return X; + + case 1: + return Y; + + case 2: + return Z; + + case 3: + return W; + } + throw new BBMOD_OutOfRangeException(); + }; + + /// @func Set([_x[, _y, _z, _w]]) + /// + /// @desc Sets vector components in-place. + /// + /// @param {Real} [_x] The new value of the first component. Defaults to 0. + /// @param {Real} [_y] The new value of the second component. Defaults to `_x`. + /// @param {Real} [_z] The new value of the third component. Defaults to `_x`. + /// @param {Real} [_w] The new value of the fourth component. Defaults to `_x`. + /// + /// @return {Struct.BBMOD_Vec4} Returns `self`. + static Set = function (_x=0.0, _y=_x, _z=_x, _w=_x) { + gml_pragma("forceinline"); + X = _x; + Y = _y; + Z = _z; + W = _w; + return self; + }; + + /// @func SetIndex(_index, _value) + /// + /// @desc Sets vector component in-place. + /// + /// @param {Real} _index The index of the component, starting at 0. + /// @param {Real} _value The new value of the component. + /// + /// @return {Struct.BBMOD_Vec4} Returns `self`. + /// + /// @throws {BBMOD_OutOfRangeException} If the given index is out of range + /// of possible values. + static SetIndex = function (_index, _value) { + gml_pragma("forceinline"); + switch (_index) + { + case 0: + X = _value; + break; + + case 1: + Y = _value; + break; + + case 2: + Z = _value; + break; + + case 3: + W = _value; + break; + + default: + throw new BBMOD_OutOfRangeException(); + break; + } + return self; + }; + + /// @func Sub(_v) + /// + /// @desc Subtracts vector `_v` from this vector and returns the result + /// as a new vector. + /// + /// @param {Struct.BBMOD_Vec4} _v The vector to subtract from this one. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + /// + /// @example + /// ```gml + /// var _v1 = new BBMOD_Vec4(1.0, 2.0, 3.0, 4.0); + /// var _v2 = new BBMOD_Vec4(5.0, 6.0, 7.0, 8.0); + /// var _v3 = _v1.Sub(_v2); // Equals to BBMOD_Vec4(-4.0, -4.0, -4.0, -4.0) + /// ``` + static Sub = function (_v) { + gml_pragma("forceinline") + return new BBMOD_Vec4( + X - _v.X, + Y - _v.Y, + Z - _v.Z, + W - _v.W + ); + }; + + /// @func ToArray([_array[, _index]]) + /// + /// @desc Writes the components of the vector into the target array. + /// + /// @param {Array} [_array] The array to write to. If `undefined` a + /// new one of required size is created. + /// @param {Real} [_index] The starting index within the target array. + /// Defaults to 0. + /// + /// @return {Array} The target array. + static ToArray = function (_array=undefined, _index=0) { + gml_pragma("forceinline"); + _array ??= array_create(4, 0.0); + _array[@ _index] = X; + _array[@ _index + 1] = Y; + _array[@ _index + 2] = Z; + _array[@ _index + 3] = W; + return _array; + }; + + /// @func ToBuffer(_buffer, _type) + /// + /// @desc Writes the components of the vector into the buffer. + /// + /// @param {Id.Buffer} _buffer The buffer to write to. + /// @param {Constant.BufferDataType} _type The type of the components. + /// + /// @return {Struct.BBMOD_Vec4} Returns `self`. + static ToBuffer = function (_buffer, _type) { + gml_pragma("forceinline"); + buffer_write(_buffer, _type, X); + buffer_write(_buffer, _type, Y); + buffer_write(_buffer, _type, Z); + buffer_write(_buffer, _type, W); + return self; + }; + + /// @func Transform(_m) + /// + /// @desc Transforms the vector by a matrix and returns the result + /// as a new vector. + /// + /// @param {Array} _m The matrix to transform the vector by. + /// + /// @return {Struct.BBMOD_Vec4} The created vector. + static Transform = function (_m) { + gml_pragma("forceinline") + var _x = X; + var _y = Y; + var _z = Z; + var _w = W; + return new BBMOD_Vec4( + _m[0] * _x + _m[4] * _y + _m[ 8] * _z + _m[12] * _w, + _m[1] * _x + _m[5] * _y + _m[ 9] * _z + _m[13] * _w, + _m[2] * _x + _m[6] * _y + _m[10] * _z + _m[14] * _w, + _m[3] * _x + _m[7] * _y + _m[11] * _z + _m[15] * _w + ); + }; +} diff --git a/scripts/BBMOD_Vec4/BBMOD_Vec4.yy b/scripts/BBMOD_Vec4/BBMOD_Vec4.yy new file mode 100644 index 000000000..b0ac63ea9 --- /dev/null +++ b/scripts/BBMOD_Vec4/BBMOD_Vec4.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Vec4", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Math", + "path": "folders/_Extensions/BBMOD/Core/Math.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_Vertex/BBMOD_Vertex.gml b/scripts/BBMOD_Vertex/BBMOD_Vertex.gml new file mode 100644 index 000000000..a0a3e2258 --- /dev/null +++ b/scripts/BBMOD_Vertex/BBMOD_Vertex.gml @@ -0,0 +1,141 @@ +/// @func BBMOD_Vertex(_vertexFormat) +/// +/// @extends BBMOD_Class +/// +/// @param {Struct.BBMOD_VertexFormat} _vertexFormat The format of the vertex. +/// This drives which properties of the vertex should be defined. +/// +/// @example +/// Following code shows how the constructor fills in the vertex properties to +/// default values if they are enabled in the vertex format. +/// ```gml +/// var _vformat = new BBMOD_VertexFormat(true); +/// var _vertex = new BBMOD_Vertex(_vformat); +/// show_debug_message(_vertex.Normal); // Prints undefined +/// +/// var _vformatWithNormals = new BBMOD_VertexFormat(true, true); +/// var _vertexWithNormal = new BBMOD_Vertex(_vformatWithNormals); +/// show_debug_message(_vertex.Normal); // Prints array [0, 0, 0] +/// ``` +/// @see BBMOD_VertexFormat +function BBMOD_Vertex(_vertexFormat) + : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + /// @var {Struct.BBMOD_VertexFormat} The vertex format. This drives which + /// properties of the vertex should be defined. + VertexFormat = _vertexFormat; + + /// @var {Struct.BBMOD_Vec3} The 3D position of the vertex or `undefined` if + /// the vertex format does not have positions. + /// @see BBMOD_VertexFormat.Vertices + Position = VertexFormat.Vertices ? new BBMOD_Vec3() : undefined; + + /// @var {Struct.BBMOD_Vec3} The normal vector of the vertex or `undefined` + /// if the vertex format does not have normals. + /// @see BBMOD_VertexFormat.Normals + Normal = VertexFormat.Normals ? BBMOD_VEC3_UP : undefined; + + /// @var {Struct.BBMOD_Vec2} The texture coordinates of the vertex or + /// `undefined` if the vertex format does not have texture coordinates. + /// @see BBMOD_VertexFormat.TextureCoords + TextureCoord = VertexFormat.TextureCoords ? new BBMOD_Vec2() : undefined; + + /// @var {Real} The ARGB color of the vertex or `undefined` if the vertex + /// format does not have colors. + /// @see BBMOD_VertexFormat.Colors + Color = VertexFormat.Colors ? 0 : undefined; + + /// @var {Struct.BBMOD_Vec4} The tangent vector & bitangent sign of the + /// vertex or `undefined` if the vertex format does not have tangents & + /// bitangents. + /// @see BBMOD_VertexFormat.TangentW + TangentW = VertexFormat.TangentW + ? new BBMOD_Vec4(1.0, 0.0, 0.0, 1.0) + : undefined; + + /// @var {Struct.BBMOD_Vec4} The bone ids of the vertex or `undefined` if + /// the vertex format does not have bones. + /// @see BBMOD_VertexFormat.Bones + Bones = VertexFormat.Bones ? new BBMOD_Vec4() : undefined; + + /// @var {Struct.BBMOD_Vec4} The bone weights of the vertex or `undefined` + /// if the vertex format does not have bones. + /// @see BBMOD_VertexFormat.Bones + Weights = VertexFormat.Bones ? new BBMOD_Vec4() : undefined; + + /// @var {Real} The id of the model in a dynamic batch or `undefined` if the + /// vertex format does not have ids. + /// @see BBMOD_VertexFormat.Ids + /// @see BBMOD_DynamicBatch + Id = VertexFormat.Ids ? 0 : undefined; + + /// @func to_vertex_buffer(_vbuffer[, _vformat]) + /// + /// @desc Adds the vertex to the vertex buffer. + /// + /// @param {Id.VertexBuffer} _vbuffer The vertex buffer to add the vertex to. + /// @param {Struct.BBMOD_VertexFormat} [_vformat] The vertex format of the + /// vertex buffer. Defaults to the format of the vertex if `undefined`. + /// + /// @return {Struct.BBMOD_Vertex} Returns `self`. + /// + /// @throws {BBMOD_Exception} If the format of the vertex and the format of + /// the buffer are not compatible. + static to_vertex_buffer = function (_vbuffer, _vformat=undefined) { + var _checkFormat = (_vformat == undefined); + + _vformat = _checkFormat ? VertexFormat : _vformat; + + if (_checkFormat + && ((_vformat.Vertices && !VertexFormat.Vertices) + || (_vformat.Normals && !VertexFormat.Normals) + || (_vformat.TextureCoords && !VertexFormat.TextureCoords) + || (_vformat.Colors && !VertexFormat.Colors) + || (_vformat.TangentW && !VertexFormat.TangentW) + || (_vformat.Bones && !VertexFormat.Bones) + || (_vformat.Ids && !VertexFormat.Ids))) + { + throw new BBMOD_Exception("Vertex formats are not compatible!"); + } + + if (_vformat.Vertices) + { + vertex_position_3d(_vbuffer, Position.X, Position.Y, Position.Z); + } + + if (_vformat.Normals) + { + vertex_normal(_vbuffer, Normal.X, Normal.Y, Normal.Z); + } + + if (_vformat.TextureCoords) + { + vertex_texcoord(_vbuffer, TextureCoord.X, TextureCoord.Y); + } + + if (_vformat.Colors) + { + vertex_color(_vbuffer, Color & $FFFFFF, ((Color & $FF000000) >> 24) / 255.0); + } + + if (_vformat.TangentW) + { + vertex_float4(_vbuffer, TangentW.X, TangentW.Y, TangentW.Z, TangentW.W); + } + + if (_vformat.Bones) + { + vertex_float4(_vbuffer, Bones.X, Bones.Y, Bones.Z, Bones.W); + vertex_float4(_vbuffer, Weights.X, Weights.Y, Weights.Z, Weights.W); + } + + if (_vformat.Ids) + { + vertex_float1(_vbuffer, Id); + } + + return self; + }; +} diff --git a/scripts/BBMOD_Vertex/BBMOD_Vertex.yy b/scripts/BBMOD_Vertex/BBMOD_Vertex.yy new file mode 100644 index 000000000..0593ad5c8 --- /dev/null +++ b/scripts/BBMOD_Vertex/BBMOD_Vertex.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_Vertex", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Model", + "path": "folders/_Extensions/BBMOD/Core/Model.yy", + }, +} \ No newline at end of file diff --git a/scripts/BBMOD_VertexFormat/BBMOD_VertexFormat.gml b/scripts/BBMOD_VertexFormat/BBMOD_VertexFormat.gml new file mode 100644 index 000000000..89a104219 --- /dev/null +++ b/scripts/BBMOD_VertexFormat/BBMOD_VertexFormat.gml @@ -0,0 +1,298 @@ +/// @macro {Struct.BBMOD_VertexFormat} The default vertex format for static +/// models. +/// @see BBMOD_VertexFormat +#macro BBMOD_VFORMAT_DEFAULT __bbmod_vformat_default() + +/// @macro {Struct.BBMOD_VertexFormat} The default vertex format for animated +/// models. +/// @see BBMOD_VertexFormat +#macro BBMOD_VFORMAT_DEFAULT_ANIMATED __bbmod_vformat_default_animated() + +/// @macro {Struct.BBMOD_VertexFormat} The default vertex format for dynamically +/// batched models. +/// @see BBMOD_VertexFormat +/// @see BBMOD_DynamicBatch +#macro BBMOD_VFORMAT_DEFAULT_BATCHED __bbmod_vformat_default_batched() + +/// @func BBMOD_VertexFormat([_confOrVertices[, _normals[, _uvs[, _colors[, _tangentw[, _bones[, _ids]]]]]]]) +/// +/// @extends BBMOD_Class +/// +/// @desc A wrapper of a raw GameMaker vertex format. +/// +/// @param {Struct, Bool} [_confOrVertices] Either a struct with keys called +/// after properties of `BBMOD_VertexFormat` and values `true` or `false`, +/// depending on whether the vertex format should have the property, or `true`, +/// since every vertex format must have vertex positions. +/// @param {Bool} [_normals] If `true` then the vertex format must have normal +/// vectors. Defaults to `false`. Used only if the first argument is not a +/// struct. +/// @param {Bool} [_uvs] If `true` then the vertex format must have texture +/// coordinates. Defaults to `false`. Used only if the first argument is not a +/// struct. +/// @param {Bool} [_colors] If `true` then the vertex format must have vertex +/// colors. Defaults to `false`. Used only if the first argument is not a +/// struct. +/// @param {Bool} [_tangentw] If `true` then the vertex format must have tangent +/// vectors and bitangent signs. Defaults to `false`. Used only if the first +/// argument is not a struct. +/// @param {Bool} [_bones] If `true` then the vertex format must have vertex +/// weights and bone indices. Defaults to `false`. Used only if the first +/// argument is not a struct. +/// @param {Bool} [_ids] If `true` then the vertex format must have ids for +/// dynamic batching. Defaults to `false`. Used only if the first argument +/// is not a struct. +function BBMOD_VertexFormat( + _confOrVertices=true, + _normals=false, + _uvs=false, + _colors=false, + _tangentw=false, + _bones=false, + _ids=false +) : BBMOD_Class() constructor +{ + BBMOD_CLASS_GENERATED_BODY; + + var _isConf = is_struct(_confOrVertices); + + /// @var {Bool} If `true` then the vertex format has vertices. Should always + /// be `true`! + /// @readonly + Vertices = _isConf + ? (_confOrVertices[$ "Vertices"] ?? true) + : _confOrVertices; + + /// @var {Bool} If `true` then the vertex format has normal vectors. + /// @readonly + Normals = _isConf + ? (_confOrVertices[$ "Normals"] ?? false) + : _normals; + + /// @var {Bool} If `true` then the vertex format has texture coordinates. + /// @readonly + TextureCoords = _isConf + ? (_confOrVertices[$ "TextureCoords"] ?? false) + : _uvs; + + /// @var {Bool} If `true` then the vertex format has a second texture + /// coordinates layer. + /// @readonly + TextureCoords2 = _isConf + ? (_confOrVertices[$ "TextureCoords2"] ?? false) + : false; + + /// @var {Bool} If `true` then the vertex format has vertex colors. + /// @readonly + Colors = _isConf + ? (_confOrVertices[$ "Colors"] ?? false) + : _colors; + + /// @var {Bool} If `true` then the vertex format has tangent vectors and + /// bitangent sign. + /// @readonly + TangentW = _isConf + ? (_confOrVertices[$ "TangentW"] ?? false) + : _tangentw; + + /// @var {Bool} If `true` then the vertex format has vertex weights and bone + /// indices. + Bones = _isConf + ? (_confOrVertices[$ "Bones"] ?? false) + : _bones; + + /// @var {Bool} If `true` then the vertex format has ids for dynamic + /// batching. + /// @readonly + Ids = _isConf + ? (_confOrVertices[$ "Ids"] ?? false) + : _ids; + + /// @var {Id.VertexFormat} The raw vertex format. + /// @readonly + Raw = undefined; + + /// @var {Ds.Map} A map of existing raw vertex formats (`Real`s to + /// `Id.VertexFormat`s). + /// @private + static __formats = ds_map_create(); + + /// @func get_hash() + /// + /// @desc Makes a hash based on the vertex format properties. Vertex buffers + /// with same propereties will have the same hash. + /// + /// @return {Real} The hash. + static get_hash = function () { + return (0 + | (Vertices << 0) + | (Normals << 1) + | (TextureCoords << 2) + | (TextureCoords2 << 3) + | (Colors << 4) + | (TangentW << 5) + | (Bones << 6) + | (Ids << 7) + ); + }; + + /// @func get_byte_size() + /// + /// @desc Retrieves the size of a single vertex using the vertex format in + /// bytes. + /// + /// @return {Real} The byte size of a single vertex using the vertex format. + static get_byte_size = function () { + gml_pragma("forceinline"); + return (0 + + (buffer_sizeof(buffer_f32) * 3 * Vertices) + + (buffer_sizeof(buffer_f32) * 3 * Normals) + + (buffer_sizeof(buffer_f32) * 2 * TextureCoords) + + (buffer_sizeof(buffer_f32) * 2 * TextureCoords2) + + (buffer_sizeof(buffer_u32) * 1 * Colors) + + (buffer_sizeof(buffer_f32) * 4 * TangentW) + + (buffer_sizeof(buffer_f32) * 8 * Bones) + + (buffer_sizeof(buffer_f32) * 1 * Ids) + ); + }; + + var _hash = get_hash(); + + if (ds_map_exists(__formats, _hash)) + { + Raw = __formats[? _hash]; + } + else + { + vertex_format_begin(); + + if (Vertices) + { + vertex_format_add_position_3d(); + } + + if (Normals) + { + vertex_format_add_normal(); + } + + if (TextureCoords) + { + vertex_format_add_texcoord(); + } + + if (TextureCoords2) + { + vertex_format_add_texcoord(); + } + + if (Colors) + { + vertex_format_add_colour(); + } + + if (TangentW) + { + vertex_format_add_custom(vertex_type_float4, vertex_usage_texcoord); + } + + if (Bones) + { + vertex_format_add_custom(vertex_type_float4, vertex_usage_texcoord); + vertex_format_add_custom(vertex_type_float4, vertex_usage_texcoord); + } + + if (Ids) + { + vertex_format_add_custom(vertex_type_float1, vertex_usage_texcoord); + } + + Raw = vertex_format_end(); + __formats[? _hash] = Raw; + } +} + +/// @func __bbmod_vertex_format_save(_vertexFormat, _buffer[, _versionMinor]) +/// +/// @desc Saves a vertex format to a buffer. +/// +/// @param {Struct.BBMOD_VertexFormat} _vertexFormat The vertex format to save. +/// @param {Id.Buffer} _buffer The buffer to save the vertex format to. +/// @param {Real} [_versionMinor] The minor version of the BBMOD file format. +/// Defaults to {@link BBMOD_VERSION_MINOR}. +/// +/// @private +function __bbmod_vertex_format_save(_vertexFormat, _buffer, _versionMinor=BBMOD_VERSION_MINOR) +{ + with (_vertexFormat) + { + buffer_write(_buffer, buffer_bool, Vertices); + buffer_write(_buffer, buffer_bool, Normals); + buffer_write(_buffer, buffer_bool, TextureCoords); + if (_versionMinor >= 3) + { + buffer_write(_buffer, buffer_bool, TextureCoords2); + } + buffer_write(_buffer, buffer_bool, Colors); + buffer_write(_buffer, buffer_bool, TangentW); + buffer_write(_buffer, buffer_bool, Bones); + buffer_write(_buffer, buffer_bool, Ids); + } +} + +/// @func __bbmod_vertex_format_load(_buffer[, _versionMinor]) +/// +/// @desc Loads a vertex format from a buffer. +/// +/// @param {Id.Buffer} _buffer The buffer to load the vertex format from. +/// @param {Real} _versionMinor The minor version of the BBMOD file format. +/// Defaults to {@link BBMOD_VERSION_MINOR}. +/// +/// @return {Struct.BBMOD_VertexFormat} The loaded vetex format. +/// +/// @private +function __bbmod_vertex_format_load(_buffer, _versionMinor=BBMOD_VERSION_MINOR) +{ + var _vertices = buffer_read(_buffer, buffer_bool); + var _normals = buffer_read(_buffer, buffer_bool); + var _textureCoords = buffer_read(_buffer, buffer_bool); + var _textureCoords2 = (_versionMinor >= 3) + ? buffer_read(_buffer, buffer_bool) + : false; + var _colors = buffer_read(_buffer, buffer_bool); + var _tangentW = buffer_read(_buffer, buffer_bool); + var _bones = buffer_read(_buffer, buffer_bool); + var _ids = buffer_read(_buffer, buffer_bool); + + return new BBMOD_VertexFormat({ + "Vertices": _vertices, + "Normals": _normals, + "TextureCoords": _textureCoords, + "TextureCoords2": _textureCoords2, + "Colors": _colors, + "TangentW": _tangentW, + "Bones": _bones, + "Ids": _ids, + }); +} + +function __bbmod_vformat_default() +{ + static _vformat = new BBMOD_VertexFormat( + true, true, true, false, true, false, false); + return _vformat; +} + +function __bbmod_vformat_default_animated() +{ + static _vformat = new BBMOD_VertexFormat( + true, true, true, false, true, true, false); + return _vformat; +} + +function __bbmod_vformat_default_batched() +{ + static _vformat = new BBMOD_VertexFormat( + true, true, true, false, true, false, true); + return _vformat; +} diff --git a/scripts/BBMOD_VertexFormat/BBMOD_VertexFormat.yy b/scripts/BBMOD_VertexFormat/BBMOD_VertexFormat.yy new file mode 100644 index 000000000..861099a4a --- /dev/null +++ b/scripts/BBMOD_VertexFormat/BBMOD_VertexFormat.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "BBMOD_VertexFormat", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Model", + "path": "folders/_Extensions/BBMOD/Core/Model.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_array/__bbmod_array.gml b/scripts/__bbmod_array/__bbmod_array.gml new file mode 100644 index 000000000..6a54d50ad --- /dev/null +++ b/scripts/__bbmod_array/__bbmod_array.gml @@ -0,0 +1,51 @@ +/// @func bbmod_array_clone(_array) +/// +/// @desc Creates a shallow clone of an array. +/// +/// @param {Array} _array The array to create a clone of. +/// +/// @return {Array} The created clone. +function bbmod_array_clone(_array) +{ + gml_pragma("forceinline"); + var _arrayLength = array_length(_array); + var _clone = array_create(_arrayLength); + array_copy(_clone, 0, _array, 0, _arrayLength); + return _clone; +} + +/// @func bbmod_array_to_buffer(_buffer, _type) +/// +/// @desc Writes an array into a buffer. +/// +/// @param {Array} _array The array to write to the buffer. +/// @param {Id.Buffer} _buffer The buffer to write the data to. +/// @param {Constant.BufferDataType} _type The value type. +function bbmod_array_to_buffer(_array, _buffer, _type) +{ + var i = 0; + repeat (array_length(_array)) + { + buffer_write(_buffer, _type, _array[i++]); + } +} + +/// @func bbmod_array_from_buffer(_buffer, _type, _size) +/// +/// @desc Creates an array with values from a buffer. +/// +/// @param {Id.Buffer} _buffer The buffer to load the data from. +/// @param {Constant.BufferDataType} _type The value type. +/// @param {Real} _size The number of values to load. +/// +/// @return {Array} The created array. +function bbmod_array_from_buffer(_buffer, _type, _size) +{ + var _array = array_create(_size, 0); + var i = 0; + repeat (_size) + { + _array[@ i++] = buffer_read(_buffer, _type); + } + return _array; +} diff --git a/scripts/__bbmod_array/__bbmod_array.yy b/scripts/__bbmod_array/__bbmod_array.yy new file mode 100644 index 000000000..f5f60fd30 --- /dev/null +++ b/scripts/__bbmod_array/__bbmod_array.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_array", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Utils", + "path": "folders/_Extensions/BBMOD/Core/Utils.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_async/__bbmod_async.gml b/scripts/__bbmod_async/__bbmod_async.gml new file mode 100644 index 000000000..63a532d19 --- /dev/null +++ b/scripts/__bbmod_async/__bbmod_async.gml @@ -0,0 +1,175 @@ +/// @var {Id.DsMap} +/// @private +global.__bbmodAsyncCallback = ds_map_create(); + +/// @var {Id.DsMap} +/// @private +global.__bbmodSpriteCallback = ds_map_create(); + +/// @func bbmod_empty_callback(_err[, _res]) +/// +/// @desc An empty callback function. Does nothing. +/// +/// @param {Struct.BBMOD_Exception} _err An error or `undefined`. +/// @param {Any} [_res] A return value. Should be `undefined` if there is an +/// error. +function bbmod_empty_callback(_err, _res=undefined) +{ +} + +/// @func bbmod_buffer_load_async(_file, _callback) +/// +/// @desc Asynchronnously loads a buffer from a file. +/// +/// @param {String} _file The path to the file to load the buffer from. +/// @param {Function} _callback The function to execute when the buffer is +/// loaded or if an error occurs. It must take the error as the first argument +/// and the buffer as the second argument. If no error occurs, then `undefined` +/// is passed. If an error does occur, then buffer is `undefined`. +/// +/// @example +/// ```gml +/// bbmod_buffer_load_async("buffer.bin", function (_error, _buffer) { +/// if (_error != undefined) +/// { +/// // Handle error here... +/// return; +/// } +/// // Use the loaded buffer here... +/// }); +/// ``` +/// +/// @note You must call {@link bbmod_async_save_load_update} in an appropriate +/// event for this function to work! +function bbmod_buffer_load_async(_file, _callback) +{ + var _buffer = buffer_create(1, buffer_grow, 1); + var _id = buffer_load_async(_buffer, _file, 0, -1); + global.__bbmodAsyncCallback[? _id] = { + Buffer: _buffer, + Callback: _callback, + }; +} + +/// @func bbmod_async_save_load_update(_asyncLoad) +/// +/// @desc This function must be called in the "Async - Save/Load" event if +/// you use {@link bbmod_buffer_load_async} to asynchronnously load a buffer! +/// +/// @param {Id.DsMap} _asyncLoad The `async_load` map. +/// +/// @example +/// ```gml +/// /// @desc Create event +/// bbmod_buffer_load_async("buffer.bin", function (_err, _buffer) { +/// if (!_err) +/// { +/// // Use the loaded buffer here... +/// } +/// }); +/// +/// /// @desc Async - Save/Load event +/// bbmod_async_save_load_update(async_load); +/// ``` +/// +/// @see bbmod_buffer_load_async +function bbmod_async_save_load_update(_asyncLoad) +{ + var _map = global.__bbmodAsyncCallback; + var _id = _asyncLoad[? "id"]; + var _data = _map[? _id]; + + if (_asyncLoad[? "status"] == false) + { + buffer_delete(_data.Buffer); + _data.Callback(new BBMOD_Exception("Async load failed!")); + } + else + { + var _buffer = _data.Buffer; + buffer_seek(_buffer, buffer_seek_start, 0); + _data.Callback(undefined, _buffer); + } + + ds_map_delete(_map, _id); +} + +/// @func bbmod_sprite_add_async(_file, _callback) +/// +/// @desc Asynchronnously loads a sprite from a file. +/// +/// @param {String} _file The path to the file to load the sprite from. +/// @param {Function} _callback The function to execute when the sprite is +/// loaded or if an error occurs. It must take the error as the first argument +/// and the sprite as the second argument. If no error occurs, then `undefined` +/// is passed. If an error does occur, then sprite is `undefined`. +/// +/// @example +/// ```gml +/// bbmod_sprite_add_async("sprite.png", function (_error, _sprite) { +/// if (_error != undefined) +/// { +/// // Handle error here... +/// return; +/// } +/// // Use the loaded sprite here... +/// }); +/// ``` +/// +/// @note You must call {@link bbmod_async_image_loaded_update} in an appropriate +/// event for this function to work! +function bbmod_sprite_add_async(_file, _callback) +{ + var _id = sprite_add(_file, 0, false, false, 0, 0); + + if (os_browser == browser_not_a_browser) + { + _callback(undefined, _id); + } + else + { + global.__bbmodSpriteCallback[? _id] = { + Callback: _callback, + }; + } +} + +/// @func bbmod_async_image_loaded_update(_asyncLoad) +/// +/// @desc This function must be called in the "Async - Image Loaded" event if +/// you use {@link bbmod_sprite_add_async} to asynchronnously load a sprite! +/// +/// @param {Id.DsMap} _asyncLoad The `async_load` map. +/// +/// @example +/// ```gml +/// /// @desc Create event +/// bbmod_sprite_add_async("sprite.png", function (_err, _sprite) { +/// if (!_err) +/// { +/// sprite_index = _sprite; +/// } +/// }); +/// +/// /// @desc Async - Image Loaded event +/// bbmod_async_image_loaded_update(async_load); +/// ``` +/// +/// @see bbmod_sprite_add_async +function bbmod_async_image_loaded_update(_asyncLoad) +{ + var _map = global.__bbmodSpriteCallback; + var _id = async_load[? "id"]; + var _data = _map[? _id]; + + if (async_load[? "status"] == false) + { + _data.Callback(new BBMOD_Exception("Async load failed!")); + } + else + { + _data.Callback(undefined, _id); + } + + ds_map_delete(_map, _id); +} diff --git a/scripts/__bbmod_async/__bbmod_async.yy b/scripts/__bbmod_async/__bbmod_async.yy new file mode 100644 index 000000000..4538eb62f --- /dev/null +++ b/scripts/__bbmod_async/__bbmod_async.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_async", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Utils", + "path": "folders/_Extensions/BBMOD/Core/Utils.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_camera/__bbmod_camera.gml b/scripts/__bbmod_camera/__bbmod_camera.gml new file mode 100644 index 000000000..df99bdfdd --- /dev/null +++ b/scripts/__bbmod_camera/__bbmod_camera.gml @@ -0,0 +1,89 @@ +/// @var {Struct.BBMOD_Vec3} +/// @private +global.__bbmodCameraPosition = new BBMOD_Vec3(); + +/// @var {Real} Distance to the far clipping plane. +/// @private +global.__bbmodZFar = 1.0; + +/// @var {Real} +/// @private +global.__bbmodCameraExposure = 1.0; + +/// @func bbmod_camera_get_position() +/// +/// @desc Retrieves the position of the camera that is passed to shaders. +/// +/// @return {Struct.BBMOD_Vec3} The camera position. +/// +/// @see bbmod_camera_set_position +function bbmod_camera_get_position() +{ + gml_pragma("forceinline"); + return global.__bbmodCameraPosition; +} + +/// @func bbmod_camera_set_position(_position) +/// +/// @desc Defines position of the camera passed to shaders. +/// +/// @param {Struct.BBMOD_Vec3} _position The new camera position. +/// +/// @see bbmod_camera_get_position +function bbmod_camera_set_position(_position) +{ + gml_pragma("forceinline"); + global.__bbmodCameraPosition = _position; +} + +/// @func bbmod_camera_get_zfar() +/// +/// @desc Retrieves distance to the far clipping plane passed to shaders. +/// +/// @return {Real} The distance to the far clipping plane. +/// +/// @see bbmod_camera_set_zfar +function bbmod_camera_get_zfar() +{ + gml_pragma("forceinline"); + return global.__bbmodZFar; +} + +/// @func bbmod_camera_set_zfar(_value) +/// +/// @desc Defines distance to the far clipping plane passed to shaders. +/// +/// @param {Real} _value The new distance to the far clipping plane. +/// +/// @see bbmod_camera_get_zfar +function bbmod_camera_set_zfar(_value) +{ + gml_pragma("forceinline"); + global.__bbmodZFar = _value; +} + +/// @func bbmod_camera_get_exposure() +/// +/// @desc Retrieves camera exposure value passed to shaders. +/// +/// @return {Real} The camera exposure value. +/// +/// @see bbmod_camera_set_exposure +function bbmod_camera_get_exposure() +{ + gml_pragma("forceinline"); + return global.__bbmodCameraExposure; +} + +/// @func bbmod_camera_set_exposure(_exposure) +/// +/// @desc Defines camera exposure value passed to shaders. +/// +/// @param {Real} _exposure The new camera exposure value. +/// +/// @see bbmod_camera_get_exposure +function bbmod_camera_set_exposure(_exposure) +{ + gml_pragma("forceinline"); + global.__bbmodCameraExposure = _exposure; +} diff --git a/scripts/__bbmod_camera/__bbmod_camera.yy b/scripts/__bbmod_camera/__bbmod_camera.yy new file mode 100644 index 000000000..12cc7112b --- /dev/null +++ b/scripts/__bbmod_camera/__bbmod_camera.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_camera", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_colmesh/__bbmod_colmesh.gml b/scripts/__bbmod_colmesh/__bbmod_colmesh.gml new file mode 100644 index 000000000..a4ba1033f --- /dev/null +++ b/scripts/__bbmod_colmesh/__bbmod_colmesh.gml @@ -0,0 +1,98 @@ +/// @func bbmod_mesh_to_colmesh(_mesh, _colmesh[, _transform]) +/// +/// @desc Adds a {@link BBMOD_Mesh} into a colmesh. +/// +/// @param {Struct.BBMOD_Mesh} _mesh The mesh to add. +/// @param {Struct.colmesh} _colmesh The colmesh to add the mesh to. +/// @param {Array} [_transform] A matrix to transform the mesh with before +/// it is added to the colmesh. Leave `undefined` if you do not wish to transform +/// the mesh. +/// +/// @see https://marketplace.yoyogames.com/assets/8130/colmesh +function bbmod_mesh_to_colmesh(_mesh, _colmesh, _transform=undefined) +{ + var _buffer = buffer_create_from_vertex_buffer(_mesh.VertexBuffer, buffer_fixed, 1); + var _bufferSize = buffer_get_size(_buffer); + var _vertexSize = _mesh.VertexFormat.get_byte_size(); + var _vertexStep = _vertexSize - 4 * 3; + var _vertexCount = _bufferSize / _vertexSize; + var _vertex = array_create(9, 0.0); + + buffer_copy_from_vertex_buffer(_mesh.VertexBuffer, 0, _vertexCount, _buffer, 0); + buffer_seek(_buffer, buffer_seek_start, 0); + + repeat (_vertexCount / 3) + { + _vertex[@ 0] = buffer_read(_buffer, buffer_f32); + _vertex[@ 1] = buffer_read(_buffer, buffer_f32); + _vertex[@ 2] = buffer_read(_buffer, buffer_f32); + buffer_seek(_buffer, buffer_seek_relative, _vertexStep); + + _vertex[@ 3] = buffer_read(_buffer, buffer_f32); + _vertex[@ 4] = buffer_read(_buffer, buffer_f32); + _vertex[@ 5] = buffer_read(_buffer, buffer_f32); + buffer_seek(_buffer, buffer_seek_relative, _vertexStep); + + _vertex[@ 6] = buffer_read(_buffer, buffer_f32); + _vertex[@ 7] = buffer_read(_buffer, buffer_f32); + _vertex[@ 8] = buffer_read(_buffer, buffer_f32); + buffer_seek(_buffer, buffer_seek_relative, _vertexStep); + + if (_transform != undefined) + { + array_copy(_vertex, 0, colmesh_matrix_transform_vertex(_transform, _vertex[0], _vertex[1], _vertex[2]), 0, 3); + array_copy(_vertex, 3, colmesh_matrix_transform_vertex(_transform, _vertex[3], _vertex[4], _vertex[5]), 0, 3); + array_copy(_vertex, 6, colmesh_matrix_transform_vertex(_transform, _vertex[6], _vertex[7], _vertex[8]), 0, 3); + } + + _colmesh.addTriangle(_vertex); + } + + buffer_delete(_buffer); +} + +/// @func bbmod_model_to_colmesh(_model, _colmesh[, _transform]) +/// +/// @desc Adds a {@link BBMOD_Model} into a colmesh. +/// +/// @param {Struct.BBMOD_Model} _model The model to add. +/// @param {Struct.colmesh} _colmesh The colmesh to add the model to. +/// @param {Array} [_transform] A matrix to transform the model with +/// before it is added to the colmesh. Leave `undefined` if you do not wish to +/// transform the model. +/// +/// @see https://marketplace.yoyogames.com/assets/8130/colmesh +function bbmod_model_to_colmesh(_model, _colmesh, _transform=undefined) +{ + static _stack = ds_stack_create(); + + var _meshes = _model.Meshes; + + ds_stack_push(_stack, _model.RootNode); + + while (!ds_stack_empty(_stack)) + { + var _node = ds_stack_pop(_stack); + + if (!_node.IsRenderable || !_node.Visible) + { + continue; + } + + var _meshIndices = _node.Meshes; + var _children = _node.Children; + var i = 0; + + repeat (array_length(_meshIndices)) + { + var _mesh = _meshes[_meshIndices[i++]]; + bbmod_mesh_to_colmesh(_mesh, _colmesh, _transform); + } + + i = 0; + repeat (array_length(_children)) + { + ds_stack_push(_stack, _children[i++]); + } + } +} diff --git a/scripts/__bbmod_colmesh/__bbmod_colmesh.yy b/scripts/__bbmod_colmesh/__bbmod_colmesh.yy new file mode 100644 index 000000000..7662836c5 --- /dev/null +++ b/scripts/__bbmod_colmesh/__bbmod_colmesh.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_colmesh", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "ColMesh", + "path": "folders/_Extensions/BBMOD/ColMesh.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_d3d11/__bbmod_d3d11.gml b/scripts/__bbmod_d3d11/__bbmod_d3d11.gml new file mode 100644 index 000000000..e75570d72 --- /dev/null +++ b/scripts/__bbmod_d3d11/__bbmod_d3d11.gml @@ -0,0 +1,58 @@ +/// @func __bbmod_d3d11_init() +/// +/// @return {Bool} Returns `true` if BBMOD.dll is available and D3D11 is +/// initialized. +/// +/// @private +function __bbmod_d3d11_init() +{ + gml_pragma("forceinline"); + static _isSupported = undefined; + if (_isSupported == undefined) + { + if (os_type == os_windows && file_exists(BBMOD_DLL_PATH)) + { + var _init = external_define( + BBMOD_DLL_PATH, "bbmod_d3d11_init", dll_cdecl, ty_real, 2, ty_string, ty_string); + var _osInfo = os_get_info(); + var _device = _osInfo[? "video_d3d11_device"]; + var _context = _osInfo[? "video_d3d11_context"]; + _isSupported = external_call(_init, _device, _context); + } + else + { + _isSupported = false; + } + } + return _isSupported; +} + +/// @func bbmod_texture_set_stage_vs(_slot, _texture) +/// +/// @desc Passes a texture to a vertex shader. On Windows this uses BBMOD.dll +/// (if available), oterwise GameMaker's built-in `texture_set_stage` is used, +/// which should work on OpenGL-based platforms. +/// +/// @param {Real} _slot The vertex texture slot index. Must be in range 0..7. +/// @param {Pointer.Texture} _texture The texture to pass. +/// +/// @note You can test if this function is supported with +/// {@link bbmod_vtf_is_supported}. +/// +/// @see bbmod_vtf_is_supported +function bbmod_texture_set_stage_vs(_slot, _texture) +{ + gml_pragma("forceinline"); + if (__bbmod_d3d11_init()) + { + static _fn = external_define( + BBMOD_DLL_PATH, "bbmod_d3d11_texture_set_stage_vs", dll_cdecl, ty_real, + 1, ty_real); + texture_set_stage(0, _texture); + external_call(_fn, _slot); + } + else + { + texture_set_stage(_slot, _texture); + } +} diff --git a/scripts/__bbmod_d3d11/__bbmod_d3d11.yy b/scripts/__bbmod_d3d11/__bbmod_d3d11.yy new file mode 100644 index 000000000..df42a0210 --- /dev/null +++ b/scripts/__bbmod_d3d11/__bbmod_d3d11.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_d3d11", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Utils", + "path": "folders/_Extensions/BBMOD/Core/Utils.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_debug/__bbmod_debug.gml b/scripts/__bbmod_debug/__bbmod_debug.gml new file mode 100644 index 000000000..c7079ba8b --- /dev/null +++ b/scripts/__bbmod_debug/__bbmod_debug.gml @@ -0,0 +1,15 @@ +/// @macro {Struct.BBMOD_VertexFormat} A vertex format useful for debugging +/// purposes, like drawing previews of colliders. +#macro BBMOD_VFORMAT_DEBUG __bbmod_vformat_debug() + +/// @var {Id.VertexBuffer} +/// @private +global.__bbmodVBufferDebug = vertex_create_buffer(); + +function __bbmod_vformat_debug() +{ + static _vformat = new BBMOD_VertexFormat({ + Colors: true, + }); + return _vformat; +} diff --git a/scripts/__bbmod_debug/__bbmod_debug.yy b/scripts/__bbmod_debug/__bbmod_debug.yy new file mode 100644 index 000000000..ce824d9ab --- /dev/null +++ b/scripts/__bbmod_debug/__bbmod_debug.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_debug", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Debug", + "path": "folders/_Extensions/BBMOD/Core/Debug.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_default_material/__bbmod_default_material.gml b/scripts/__bbmod_default_material/__bbmod_default_material.gml new file mode 100644 index 000000000..9eab14c59 --- /dev/null +++ b/scripts/__bbmod_default_material/__bbmod_default_material.gml @@ -0,0 +1,264 @@ +/// @macro {Struct.BBMOD_VertexFormat} A vertex format of lightmapped models +/// with two UV channels. +/// @see BBMOD_VertexFormat +#macro BBMOD_VFORMAT_DEFAULT_LIGHTMAP __bbmod_vformat_default_lightmap() + +/// @macro {Struct.BBMOD_VertexFormat} Vertex format of 2D sprites. +/// @see BBMOD_VertexFormat +#macro BBMOD_VFORMAT_DEFAULT_SPRITE __bbmod_vformat_default_sprite() + +/// @macro {Struct.BBMOD_DefaultShader} The default shader. +/// @see BBMOD_DefaultShader +#macro BBMOD_SHADER_DEFAULT __bbmod_shader_default() + +/// @macro {Struct.BBMOD_DefaultShader} The default shader for unlit objects. +/// @see BBMOD_DefaultShader +#macro BBMOD_SHADER_DEFAULT_UNLIT __bbmod_shader_default_unlit() + +/// @macro {Struct.BBMOD_BaseShader} Depth shader for static models. +/// +/// @example +/// Following code enables casting shadows for a custom material +/// (requires a {@link BBMOD_Renderer} with enabled shadows). +/// ```gml +/// material = BBMOD_MATERIAL_DEFAULT.clone() +/// .set_shader(BBMOD_ERenderPass.Shadows, BBMOD_SHADER_DEFAULT_DEPTH); +/// ``` +/// +/// @see BBMOD_ERenderPass.Shadows +/// @see BBMOD_BaseShader +#macro BBMOD_SHADER_DEFAULT_DEPTH __bbmod_shader_default_depth() + +/// @macro {Struct.BBMOD_LightmapShader} Shader for rendering lightmapped models +/// with two UV channels. +/// @note This shader does not support subsurface scattering! +/// @see BBMOD_LightmapShader +#macro BBMOD_SHADER_DEFAULT_LIGHTMAP __bbmod_shader_default_lightmap() + +/// @macro {Struct.BBMOD_DefaultSpriteShader} Shader for 2D sprites. +/// @see BBMOD_DefaultSpriteShader +#macro BBMOD_SHADER_DEFAULT_SPRITE __bbmod_shader_default_sprite() + +/// @macro {Struct.BBMOD_DefaultMaterial} The default material. +/// @see BBMOD_Material +#macro BBMOD_MATERIAL_DEFAULT __bbmod_material_default() + +/// @macro {Struct.BBMOD_DefaultMaterial} The default material for unlit objects. +/// @see BBMOD_Material +#macro BBMOD_MATERIAL_DEFAULT_UNLIT __bbmod_material_default_unlit() + +/// @macro {Struct.BBMOD_LightmapMaterial} Material for lightmapped models with +/// two UV channels. +/// @macro This material does not support subsurface scattering! +/// @see BBMOD_LightmapMaterial +#macro BBMOD_MATERIAL_DEFAULT_LIGHTMAP __bbmod_material_default_lightmap() + +/// @macro {Struct.BBMOD_DefaultMaterial} Material for 2D sprites. +/// @see BBMOD_DefaultMaterial +#macro BBMOD_MATERIAL_DEFAULT_SPRITE __bbmod_material_default_sprite() + +//////////////////////////////////////////////////////////////////////////////// +// Vertex formats + +function __bbmod_vformat_default_lightmap() +{ + static _vformat = new BBMOD_VertexFormat({ + "Vertices": true, + "Normals": true, + "TextureCoords": true, + "TextureCoords2": true, + "TangentW": true, + }); + return _vformat; +} + +function __bbmod_vformat_default_sprite() +{ + static _vformat = new BBMOD_VertexFormat( + true, false, true, true, false, false, false); + return _vformat; +} + +//////////////////////////////////////////////////////////////////////////////// +// Shaders + +function __bbmod_shader_default() +{ + static _shader = new BBMOD_DefaultShader( + BBMOD_ShDefault, BBMOD_VFORMAT_DEFAULT) + .add_variant(BBMOD_ShDefaultAnimated, BBMOD_VFORMAT_DEFAULT_ANIMATED) + .add_variant(BBMOD_ShDefaultBatched, BBMOD_VFORMAT_DEFAULT_BATCHED); + return _shader; +} + +function __bbmod_shader_default_unlit() +{ + static _shader = new BBMOD_DefaultShader( + BBMOD_ShDefaultUnlit, BBMOD_VFORMAT_DEFAULT) + .add_variant(BBMOD_ShDefaultUnlitAnimated, BBMOD_VFORMAT_DEFAULT_ANIMATED) + .add_variant(BBMOD_ShDefaultUnlitBatched, BBMOD_VFORMAT_DEFAULT_BATCHED); + return _shader; +} + +function __bbmod_shader_default_lightmap() +{ + static _shader = new BBMOD_DefaultLightmapShader( + BBMOD_ShDefaultLightmap, BBMOD_VFORMAT_DEFAULT_LIGHTMAP); + return _shader; +} + +function __bbmod_shader_default_sprite() +{ + static _shader = new BBMOD_DefaultSpriteShader( + BBMOD_ShDefaultSprite, BBMOD_VFORMAT_DEFAULT_SPRITE) + return _shader; +} + +function __bbmod_shader_default_depth() +{ + static _shader = new BBMOD_BaseShader( + BBMOD_ShDefaultDepth, BBMOD_VFORMAT_DEFAULT) + .add_variant(BBMOD_ShDefaultDepthAnimated, BBMOD_VFORMAT_DEFAULT_ANIMATED) + .add_variant(BBMOD_ShDefaultDepthBatched, BBMOD_VFORMAT_DEFAULT_BATCHED) + .add_variant(BBMOD_ShDefaultDepthLightmap, BBMOD_VFORMAT_DEFAULT_LIGHTMAP); + return _shader; +} + +//////////////////////////////////////////////////////////////////////////////// +// Materials + +function __bbmod_material_default() +{ + static _material = undefined; + if (_material == undefined) + { + _material = new BBMOD_DefaultMaterial(BBMOD_SHADER_DEFAULT); + _material.BaseOpacity = sprite_get_texture(BBMOD_SprDefaultBaseOpacity, 0); + } + return _material; +} + +function __bbmod_material_default_unlit() +{ + static _material = undefined; + if (_material == undefined) + { + _material = new BBMOD_DefaultMaterial(BBMOD_SHADER_DEFAULT_UNLIT); + _material.BaseOpacity = sprite_get_texture(BBMOD_SprDefaultBaseOpacity, 0); + } + return _material; +} + +function __bbmod_material_default_lightmap() +{ + static _material = new BBMOD_DefaultLightmapMaterial(BBMOD_SHADER_DEFAULT_LIGHTMAP); + return _material; +} + +function __bbmod_material_default_sprite() +{ + static _material = new BBMOD_DefaultMaterial(BBMOD_SHADER_DEFAULT_SPRITE); + return _material; +} + +//////////////////////////////////////////////////////////////////////////////// +// Register + +bbmod_shader_register("BBMOD_SHADER_DEFAULT", BBMOD_SHADER_DEFAULT); +bbmod_shader_register("BBMOD_SHADER_DEFAULT_DEPTH", BBMOD_SHADER_DEFAULT_DEPTH); +bbmod_shader_register("BBMOD_SHADER_DEFAULT_LIGHTMAP", BBMOD_SHADER_DEFAULT_LIGHTMAP); +bbmod_shader_register("BBMOD_SHADER_DEFAULT_SPRITE", BBMOD_SHADER_DEFAULT_SPRITE); + +bbmod_material_register("BBMOD_MATERIAL_DEFAULT", BBMOD_MATERIAL_DEFAULT); +bbmod_material_register("BBMOD_MATERIAL_DEFAULT_LIGHTMAP", BBMOD_MATERIAL_DEFAULT_LIGHTMAP); +bbmod_material_register("BBMOD_MATERIAL_DEFAULT_SPRITE", BBMOD_MATERIAL_DEFAULT_SPRITE); + +//////////////////////////////////////////////////////////////////////////////// +// DEPRECATED!!! + +/// @macro {Struct.BBMOD_VertexFormat} Vertex format of 2D sprites. +/// @see BBMOD_VertexFormat +/// @deprecated Please use {@link BBMOD_VFORMAT_DEFAULT_SPRITE} instead. +#macro BBMOD_VFORMAT_SPRITE BBMOD_VFORMAT_DEFAULT_SPRITE + +/// @macro {Struct.BBMOD_VertexFormat} A vertex format of lightmapped models +/// with two UV channels. +/// @see BBMOD_VertexFormat +/// @deprecated Please use {@link BBMOD_VFORMAT_DEFAULT_LIGHTMAP} instead. +#macro BBMOD_VFORMAT_LIGHTMAP BBMOD_VFORMAT_DEFAULT_LIGHTMAP + +/// @macro {Struct.BBMOD_DefaultShader} The default shader for animated models. +/// @see BBMOD_DefaultShader +/// @deprecated Please use {@link BBMOD_SHADER_DEFAULT} instead. +#macro BBMOD_SHADER_DEFAULT_ANIMATED BBMOD_SHADER_DEFAULT + +/// @macro {Struct.BBMOD_DefaultShader} The default shader for dynamically +/// batched models. +/// @see BBMOD_DefaultShader +/// @see BBMOD_DynamicBatch +/// @deprecated Please use {@link BBMOD_SHADER_DEFAULT} instead. +#macro BBMOD_SHADER_DEFAULT_BATCHED BBMOD_SHADER_DEFAULT + +/// @macro {Struct.BBMOD_BaseShader} Depth shader for static models. +/// @deprecated Please use {@link BBMOD_SHADER_DEFAULT_DEPTH} instead. +#macro BBMOD_SHADER_DEPTH BBMOD_SHADER_DEFAULT_DEPTH + +/// @macro {Struct.BBMOD_BaseShader} Depth shader for animated models with bones. +/// @deprecated Please use {@link BBMOD_SHADER_DEFAULT_DEPTH} instead. +#macro BBMOD_SHADER_DEPTH_ANIMATED BBMOD_SHADER_DEFAULT_DEPTH + +/// @macro {Struct.BBMOD_BaseShader} Depth shader for dynamically batched models. +/// @deprecated Please use {@link BBMOD_SHADER_DEFAULT_DEPTH} instead. +#macro BBMOD_SHADER_DEPTH_BATCHED BBMOD_SHADER_DEFAULT_DEPTH + +/// @macro {Struct.BBMOD_BaseShader} Depth shader for lightmapped models with +/// two UV channels. +/// @deprecated Please use {@link BBMOD_SHADER_DEFAULT_DEPTH} instead. +#macro BBMOD_SHADER_LIGHTMAP_DEPTH BBMOD_SHADER_DEFAULT_DEPTH + +/// @macro {Struct.BBMOD_LightmapShader} Shader for rendering lightmapped models +/// with two UV channels. +/// @deprecated Please use {@link BBMOD_SHADER_DEFAULT_LIGHTMAP} instead. +#macro BBMOD_SHADER_LIGHTMAP BBMOD_SHADER_DEFAULT_LIGHTMAP + +/// @macro {Struct.BBMOD_DefaultSpriteShader} Shader for 2D sprites. +/// @see BBMOD_DefaultSpriteShader +/// @deprecated Please use {@link BBMOD_SHADER_DEFAULT_SPRITE} instead. +#macro BBMOD_SHADER_SPRITE BBMOD_SHADER_DEFAULT_SPRITE + +/// @macro {Struct.BBMOD_DefaultMaterial} The default material for animated +/// models. +/// @see BBMOD_Material +/// @deprecated Please use {@link BBMOD_MATERIAL_DEFAULT} instead. +#macro BBMOD_MATERIAL_DEFAULT_ANIMATED BBMOD_MATERIAL_DEFAULT + +/// @macro {Struct.BBMOD_DefaultMaterial} The default material for dynamically +/// batched models. +/// @see BBMOD_Material +/// @see BBMOD_DynamicBatch +/// @deprecated Please use {@link BBMOD_MATERIAL_DEFAULT} instead. +#macro BBMOD_MATERIAL_DEFAULT_BATCHED BBMOD_MATERIAL_DEFAULT + +/// @macro {Struct.BBMOD_LightmapMaterial} Material for lightmapped models with +/// two UV channels. +/// @deprecated Please use {@link BBMOD_MATERIAL_DEFAULT_LIGHTMAP} instead. +#macro BBMOD_MATERIAL_LIGHTMAP BBMOD_MATERIAL_DEFAULT_LIGHTMAP + +/// @macro {Struct.BBMOD_DefaultMaterial} Material for 2D sprites. +/// @see BBMOD_DefaultMaterial +/// @deprecated Please use {@link BBMOD_MATERIAL_DEFAULT_SPRITE} instead. +#macro BBMOD_MATERIAL_SPRITE BBMOD_MATERIAL_DEFAULT_SPRITE + +bbmod_shader_register("BBMOD_SHADER_DEPTH", BBMOD_SHADER_DEPTH); +bbmod_shader_register("BBMOD_SHADER_DEPTH_ANIMATED", BBMOD_SHADER_DEPTH_ANIMATED); +bbmod_shader_register("BBMOD_SHADER_DEPTH_BATCHED", BBMOD_SHADER_DEPTH_BATCHED); +bbmod_shader_register("BBMOD_SHADER_LIGHTMAP", BBMOD_SHADER_LIGHTMAP); +bbmod_shader_register("BBMOD_SHADER_LIGHTMAP_DEPTH", BBMOD_SHADER_LIGHTMAP_DEPTH); +bbmod_shader_register("BBMOD_SHADER_SPRITE", BBMOD_SHADER_SPRITE); +bbmod_shader_register("BBMOD_SHADER_DEFAULT_ANIMATED", BBMOD_SHADER_DEFAULT_ANIMATED); +bbmod_shader_register("BBMOD_SHADER_DEFAULT_BATCHED", BBMOD_SHADER_DEFAULT_BATCHED); + +bbmod_material_register("BBMOD_MATERIAL_DEFAULT_ANIMATED", BBMOD_MATERIAL_DEFAULT_ANIMATED); +bbmod_material_register("BBMOD_MATERIAL_DEFAULT_BATCHED", BBMOD_MATERIAL_DEFAULT_BATCHED); +bbmod_material_register("BBMOD_MATERIAL_LIGHTMAP", BBMOD_MATERIAL_LIGHTMAP); +bbmod_material_register("BBMOD_MATERIAL_SPRITE", BBMOD_MATERIAL_SPRITE); diff --git a/scripts/__bbmod_default_material/__bbmod_default_material.yy b/scripts/__bbmod_default_material/__bbmod_default_material.yy new file mode 100644 index 000000000..246ed0596 --- /dev/null +++ b/scripts/__bbmod_default_material/__bbmod_default_material.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_default_material", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "DefaultRenderer", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_defines/__bbmod_defines.gml b/scripts/__bbmod_defines/__bbmod_defines.gml new file mode 100644 index 000000000..be6ac0bbb --- /dev/null +++ b/scripts/__bbmod_defines/__bbmod_defines.gml @@ -0,0 +1,23 @@ +/// @macro {Real} The supported major version of BBMOD and BBANIM files. +#macro BBMOD_VERSION_MAJOR 3 + +/// @macro {Real} The current minor version of BBMOD and BBANIM files. +#macro BBMOD_VERSION_MINOR 4 + +/// @macro {Real} A value used to tell that no normals should be generated +/// if the model does not have any. +/// @see BBMOD_NORMALS_FLAT +/// @see BBMOD_NORMALS_SMOOTH +#macro BBMOD_NORMALS_NONE 0 + +/// @macro {Real} A value used to tell that flat normals should be generated +/// if the model does not have any. +/// @see BBMOD_NORMALS_NONE +/// @see BBMOD_NORMALS_SMOOTH +#macro BBMOD_NORMALS_FLAT 1 + +/// @macro {Real} A value used to tell that smooth normals should be generated +/// if the model does not have any. +/// @see BBMOD_NORMALS_NONE +/// @see BBMOD_NORMALS_FLAT +#macro BBMOD_NORMALS_SMOOTH 2 diff --git a/scripts/__bbmod_defines/__bbmod_defines.yy b/scripts/__bbmod_defines/__bbmod_defines.yy new file mode 100644 index 000000000..e3fcf8ffd --- /dev/null +++ b/scripts/__bbmod_defines/__bbmod_defines.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_defines", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Core", + "path": "folders/_Extensions/BBMOD/Core.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_fog/__bbmod_fog.gml b/scripts/__bbmod_fog/__bbmod_fog.gml new file mode 100644 index 000000000..5c9e102f6 --- /dev/null +++ b/scripts/__bbmod_fog/__bbmod_fog.gml @@ -0,0 +1,214 @@ +/// @var {Struct.BBMOD_Color} +/// @private +global.__bbmodFogColor = BBMOD_C_WHITE; + +/// @var {Real} +/// @private +global.__bbmodFogIntensity = 0.0; + +/// @var {Real} +/// @private +global.__bbmodFogStart = 0.0; + +/// @var {Real} +/// @private +global.__bbmodFogEnd = 1.0; + +/// @func bbmod_fog_set(_color, _intensity, _start, _end) +/// +/// @desc Defines fog properties sent to shaders. +/// +/// @param {Struct.BBMOD_Color} _color The color of the fog. The default fog +/// color is white. +/// @param {Real} _intensity The intensity of the fog. Use values in range 0..1. +/// The default fog intensity is 0 (no fog). +/// @param {Real} _start The distance from the camera where the fog starts at. +/// The default fog start is 0. +/// @param {Real} _end The distance from the camera where the fog has the +/// maximum intensity. The default fog end is 1. +/// +/// @see bbmod_fog_get_color +/// @see bbmod_fog_set_color +/// @see bbmod_fog_get_intensity +/// @see bbmod_fog_set_intensity +/// @see bbmod_fog_get_start +/// @see bbmod_fog_set_start +/// @see bbmod_fog_get_end +/// @see bbmod_fog_set_end +/// @see BBMOD_Color +function bbmod_fog_set(_color, _intensity, _start, _end) +{ + gml_pragma("forceinline"); + global.__bbmodFogColor = _color; + global.__bbmodFogIntensity = _intensity; + global.__bbmodFogStart = _start; + global.__bbmodFogEnd = _end; +} + +/// @func bbmod_fog_get_color() +/// +/// @desc Retrieves the color of the fog that is sent to shaders. +/// +/// @return {Struct.BBMOD_Color} The color of the fog. +/// +/// @see bbmod_fog_set +/// @see bbmod_fog_set_color +/// @see bbmod_fog_get_intensity +/// @see bbmod_fog_set_intensity +/// @see bbmod_fog_get_start +/// @see bbmod_fog_set_start +/// @see bbmod_fog_get_end +/// @see bbmod_fog_set_end +/// @see BBMOD_Color +function bbmod_fog_get_color() +{ + gml_pragma("forceinline"); + return global.__bbmodFogColor; +} + +/// @func bbmod_fog_set_color(_color) +/// +/// @desc Defines the color of the fog that is sent to shaders. +/// +/// @param {Struct.BBMOD_Color} _color The new fog color. The default fog color +/// is white. +/// +/// @see bbmod_fog_set +/// @see bbmod_fog_get_color +/// @see bbmod_fog_get_intensity +/// @see bbmod_fog_set_intensity +/// @see bbmod_fog_get_start +/// @see bbmod_fog_set_start +/// @see bbmod_fog_get_end +/// @see bbmod_fog_set_end +/// @see BBMOD_Color +function bbmod_fog_set_color(_color) +{ + gml_pragma("forceinline"); + global.__bbmodFogColor = _color; +} + +/// @func bbmod_fog_get_intensity() +/// +/// @desc Retrieves the fog intensity that is sent to shaders. +/// +/// @return {Real} The fog intensity. +/// +/// @see bbmod_fog_set +/// @see bbmod_fog_get_color +/// @see bbmod_fog_set_color +/// @see bbmod_fog_set_intensity +/// @see bbmod_fog_get_start +/// @see bbmod_fog_set_start +/// @see bbmod_fog_get_end +/// @see bbmod_fog_set_end +function bbmod_fog_get_intensity() +{ + gml_pragma("forceinline"); + return global.__bbmodFogIntensity; +} + +/// @func bbmod_fog_set_intensity(_intensity) +/// +/// @desc Defines the fog intensity that is sent to shaders. +/// +/// @param {Real} _intensity The new fog intensity. The default intensity of the +/// fog is 0 (no fog). +/// +/// @see bbmod_fog_set +/// @see bbmod_fog_get_color +/// @see bbmod_fog_set_color +/// @see bbmod_fog_get_intensity +/// @see bbmod_fog_get_start +/// @see bbmod_fog_set_start +/// @see bbmod_fog_get_end +/// @see bbmod_fog_set_end +function bbmod_fog_set_intensity(_intensity) +{ + gml_pragma("forceinline"); + global.__bbmodFogIntensity = _intensity; +} + +/// @func bbmod_fog_get_start() +/// +/// @desc Retrieves the distance where the fog starts at, as it is defined to be +/// sent to shaders. +/// +/// @return {Real} The distance where the fog starts at. +/// +/// @see bbmod_fog_set +/// @see bbmod_fog_get_color +/// @see bbmod_fog_set_color +/// @see bbmod_fog_get_intensity +/// @see bbmod_fog_set_intensity +/// @see bbmod_fog_set_start +/// @see bbmod_fog_get_end +/// @see bbmod_fog_set_end +function bbmod_fog_get_start() +{ + gml_pragma("forceinline"); + return global.__bbmodFogStart; +} + +/// @func bbmod_fog_set_start(_start) +/// +/// @desc Defines distance where the fog starts at - to be sent to shaders. +/// +/// @param {Real} _start The new distance where the fog starts at. The default +/// value is 0. +/// +/// @see bbmod_fog_set +/// @see bbmod_fog_get_color +/// @see bbmod_fog_set_color +/// @see bbmod_fog_get_intensity +/// @see bbmod_fog_set_intensity +/// @see bbmod_fog_get_start +/// @see bbmod_fog_get_end +/// @see bbmod_fog_set_end +function bbmod_fog_set_start(_start) +{ + gml_pragma("forceinline"); + global.__bbmodFogStart = _start; +} + +/// @func bbmod_fog_get_end() +/// +/// @desc Retrieves the distance where the fog has the maximum intensity, as it +/// is defined to be sent to shaders. +/// +/// @return {Real} The distance where the fog has the maximum intensity. +/// +/// @see bbmod_fog_set +/// @see bbmod_fog_get_color +/// @see bbmod_fog_set_color +/// @see bbmod_fog_get_intensity +/// @see bbmod_fog_set_intensity +/// @see bbmod_fog_get_start +/// @see bbmod_fog_set_start +/// @see bbmod_fog_set_end +function bbmod_fog_get_end() +{ + gml_pragma("forceinline"); + return global.__bbmodFogEnd; +} + +/// @func bbmod_fog_set_end(_end) +/// +/// @desc Defines the distance where the fog has the maximum intensity - to be +/// sent to shaders. +/// +/// @param {Real} _end The distance where the fog has the maximum intensity. +/// +/// @see bbmod_fog_set +/// @see bbmod_fog_get_color +/// @see bbmod_fog_set_color +/// @see bbmod_fog_get_intensity +/// @see bbmod_fog_set_intensity +/// @see bbmod_fog_get_start +/// @see bbmod_fog_set_start +/// @see bbmod_fog_get_end +function bbmod_fog_set_end(_end) +{ + gml_pragma("forceinline"); + global.__bbmodFogEnd = _end; +} diff --git a/scripts/__bbmod_fog/__bbmod_fog.yy b/scripts/__bbmod_fog/__bbmod_fog.yy new file mode 100644 index 000000000..92c2d3cfa --- /dev/null +++ b/scripts/__bbmod_fog/__bbmod_fog.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_fog", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_gizmo/__bbmod_gizmo.gml b/scripts/__bbmod_gizmo/__bbmod_gizmo.gml new file mode 100644 index 000000000..b2c3312ca --- /dev/null +++ b/scripts/__bbmod_gizmo/__bbmod_gizmo.gml @@ -0,0 +1,37 @@ +/// @macro {Struct.BBMOD_BaseShader} A shader used when rendering instance IDs. +/// @see BBMOD_BaseShader +#macro BBMOD_SHADER_INSTANCE_ID __bbmod_shader_id() + +function __bbmod_shader_id() +{ + static _shader = new BBMOD_BaseShader( + BBMOD_ShInstanceID, BBMOD_VFORMAT_DEFAULT) + .add_variant(BBMOD_ShInstanceIDAnimated, BBMOD_VFORMAT_DEFAULT_ANIMATED) + .add_variant(BBMOD_ShInstanceIDBatched, BBMOD_VFORMAT_DEFAULT_BATCHED) + .add_variant(BBMOD_ShInstanceIDLightmap, BBMOD_VFORMAT_DEFAULT_LIGHTMAP); + return _shader; +} + +//////////////////////////////////////////////////////////////////////////////// +// DEPRECATED!!! + +/// @macro {Struct.BBMOD_BaseShader} A shader used when rendering instance IDs. +/// @see BBMOD_BaseShader +/// @deprecated Please use {@link BBMOD_SHADER_INSTANCE_ID} instead. +#macro BBMOD_SHADER_INSTANCE_ID_ANIMATED BBMOD_SHADER_INSTANCE_ID + +/// @macro {Struct.BBMOD_BaseShader} A shader used when rendering instance IDs. +/// @see BBMOD_BaseShader +/// @deprecated Please use {@link BBMOD_SHADER_INSTANCE_ID} instead. +#macro BBMOD_SHADER_INSTANCE_ID_BATCHED BBMOD_SHADER_INSTANCE_ID + +/// @macro {Struct.BBMOD_BaseShader} A shader used when rendering instance IDs +/// for lightmapped models. +/// @see BBMOD_BaseShader +/// @deprecated Please use {@link BBMOD_SHADER_INSTANCE_ID_LIGHTMAP} instead. +#macro BBMOD_SHADER_LIGHTMAP_INSTANCE_ID BBMOD_SHADER_INSTANCE_ID + +bbmod_shader_register("BBMOD_SHADER_INSTANCE_ID", BBMOD_SHADER_INSTANCE_ID); +bbmod_shader_register("BBMOD_SHADER_INSTANCE_ID_ANIMATED", BBMOD_SHADER_INSTANCE_ID_ANIMATED); +bbmod_shader_register("BBMOD_SHADER_INSTANCE_ID_BATCHED", BBMOD_SHADER_INSTANCE_ID_BATCHED); +bbmod_shader_register("BBMOD_SHADER_LIGHTMAP_INSTANCE_ID", BBMOD_SHADER_LIGHTMAP_INSTANCE_ID); diff --git a/scripts/__bbmod_gizmo/__bbmod_gizmo.yy b/scripts/__bbmod_gizmo/__bbmod_gizmo.yy new file mode 100644 index 000000000..ac9fb32c0 --- /dev/null +++ b/scripts/__bbmod_gizmo/__bbmod_gizmo.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_gizmo", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Gizmo", + "path": "folders/_Extensions/BBMOD/Gizmo.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_init/__bbmod_init.gml b/scripts/__bbmod_init/__bbmod_init.gml new file mode 100644 index 000000000..d339acbd3 --- /dev/null +++ b/scripts/__bbmod_init/__bbmod_init.gml @@ -0,0 +1,3 @@ +/// @var {Struct.BBMOD_Renderer} The last used renderer. Can be `undefined`. +/// @private +global.__bbmodRendererCurrent = undefined; diff --git a/scripts/__bbmod_init/__bbmod_init.yy b/scripts/__bbmod_init/__bbmod_init.yy new file mode 100644 index 000000000..a221a3a8e --- /dev/null +++ b/scripts/__bbmod_init/__bbmod_init.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_init", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Core", + "path": "folders/_Extensions/BBMOD/Core.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_light_ambient/__bbmod_light_ambient.gml b/scripts/__bbmod_light_ambient/__bbmod_light_ambient.gml new file mode 100644 index 000000000..bc53a11df --- /dev/null +++ b/scripts/__bbmod_light_ambient/__bbmod_light_ambient.gml @@ -0,0 +1,132 @@ +/// @var {Struct.BBMOD_Color} +/// @private +global.__bbmodAmbientLightUp = BBMOD_C_WHITE; + +/// @var {Struct.BBMOD_Color} +/// @private +global.__bbmodAmbientLightDown = BBMOD_C_GRAY; + +/// @var {Bool} +/// @private +global.__bbmodAmbientAffectLightmap = true; + +/// @func bbmod_light_ambient_set(_color) +/// +/// @desc Defines color of the ambient light passed to shaders. +/// +/// @param {Struct.BBMOD_Color} _color The new color of the ambient light (both +/// upper and lower hemisphere). +/// +/// @see bbmod_light_ambient_get_up +/// @see bbmod_light_ambient_set_up +/// @see bbmod_light_ambient_get_down +/// @see bbmod_light_ambient_set_down +/// @see BBMOD_Color +function bbmod_light_ambient_set(_color) +{ + gml_pragma("forceinline"); + global.__bbmodAmbientLightUp = _color; + global.__bbmodAmbientLightDown = _color; +} + +/// @func bbmod_light_ambient_get_up() +/// +/// @desc Retrieves color of the upper hemisphere of the ambient light passed +/// to shaders. +/// +/// @return {Struct.BBMOD_Color} The color of the upper hemisphere of the +/// ambient light. +/// +/// @see bbmod_light_ambient_set +/// @see bbmod_light_ambient_set_up +/// @see bbmod_light_ambient_get_down +/// @see bbmod_light_ambient_set_down +/// @see BBMOD_Color +function bbmod_light_ambient_get_up() +{ + gml_pragma("forceinline"); + return global.__bbmodAmbientLightUp; +} + +/// @func bbmod_light_ambient_set_up(_color) +/// +/// @desc Defines color of the upper hemisphere of the ambient light passed to +/// shaders. +/// +/// @param {Struct.BBMOD_Color} _color The new color of the upper hemisphere of +/// the ambient light. +/// +/// @see bbmod_light_ambient_set +/// @see bbmod_light_ambient_get_up +/// @see bbmod_light_ambient_get_down +/// @see bbmod_light_ambient_set_down +/// @see BBMOD_Color +function bbmod_light_ambient_set_up(_color) +{ + gml_pragma("forceinline"); + global.__bbmodAmbientLightUp = _color; +} + +/// @func bbmod_light_ambient_get_down() +/// +/// @desc Retrieves color of the lower hemisphere of the ambient light passed +/// to shaders. +/// +/// @return {Struct.BBMOD_Color} The color of the lower hemisphere of the +/// ambient light. +/// +/// @see bbmod_light_ambient_set +/// @see bbmod_light_ambient_get_up +/// @see bbmod_light_ambient_set_up +/// @see bbmod_light_ambient_set_down +/// @see BBMOD_Color +function bbmod_light_ambient_get_down() +{ + gml_pragma("forceinline"); + return global.__bbmodAmbientLightDown; +} + +/// @func bbmod_light_ambient_set_down(_color) +/// +/// @desc Defines color of the lower hemisphere of the ambient light passed to +/// shaders. +/// +/// @param {Struct.BBMOD_Color} _color The new color of the lower hemisphere of +/// the ambient light. +/// +/// @see bbmod_light_ambient_set +/// @see bbmod_light_ambient_get_up +/// @see bbmod_light_ambient_set_up +/// @see bbmod_light_ambient_get_down +/// @see BBMOD_Color +function bbmod_light_ambient_set_down(_color) +{ + gml_pragma("forceinline"); + global.__bbmodAmbientLightDown = _color; +} + +/// @func bbmod_light_ambient_get_affect_lightmaps() +/// +/// @desc Checks whether ambient light affects materials that use baked +/// lightmaps. +/// +/// @return {Bool} Returns `true` if ambient light affects materials that +/// use lightmaps. +function bbmod_light_ambient_get_affect_lightmaps() +{ + gml_pragma("forceinline"); + return global.__bbmodAmbientAffectLightmap; +} + +/// @func bbmod_light_ambient_set_affect_lightmaps(_enable) +/// +/// @desc Configures whether ambient light affects materials that use baked +/// lightmaps. +/// +/// @param {Bool} _enable Use `true` to enable ambient light affecting materials +/// that use baked lightmaps. +function bbmod_light_ambient_set_affect_lightmaps(_enable) +{ + gml_pragma("forceinline"); + global.__bbmodAmbientAffectLightmap = _enable; +} diff --git a/scripts/__bbmod_light_ambient/__bbmod_light_ambient.yy b/scripts/__bbmod_light_ambient/__bbmod_light_ambient.yy new file mode 100644 index 000000000..1edf33291 --- /dev/null +++ b/scripts/__bbmod_light_ambient/__bbmod_light_ambient.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_light_ambient", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Lights", + "path": "folders/_Extensions/BBMOD/Core/Lights.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_logging/__bbmod_logging.gml b/scripts/__bbmod_logging/__bbmod_logging.gml new file mode 100644 index 000000000..d7346a4b5 --- /dev/null +++ b/scripts/__bbmod_logging/__bbmod_logging.gml @@ -0,0 +1,99 @@ +/// @enum Enumeration of log levels. +/// @private +enum __BBMOD_ELogLevel +{ + /// @member Debug messages. Equals 0. + Debug, + /// @member Info messages. Equals 1. This is the default log level. + Info, + /// @member Warnings. Equals 2. + Warning, + /// @member Error messages. Equals 3. + Error, + /// @member Log messages disabled. Equals 4. + Disabled, +}; + +/// @func __bbmod_log_level_to_string(_logLevel) +/// +/// @param {Real} _logLevel Use values from {@link __BBMOD_ELogLevel}. +/// +/// @return {String} +/// +/// @private +function __bbmod_log_level_to_string(_logLevel) +{ + gml_pragma("forceinline"); + static _names = [ + "DEBUG", + "INFO", + "WARNING", + "ERROR", + ]; + return _names[_logLevel]; +} + +/// @func __bbmod_log(_level, _format[, _values]) +/// +/// @param {Real} _level Use values from {@link __BBMOD_ELogLevel}. +/// @param {String} _format +/// @param {Array} [_values] +/// +/// @private +function __bbmod_log(_level, _format, _values=[]) +{ + gml_pragma("forceinline"); + show_debug_message_ext( + "[" + date_time_string(date_current_datetime()) + "]" + + "[" + __bbmod_log_level_to_string(_level) + "]" + + " BBMOD: " + _format, + _values); +} + +/// @func __bbmod_debug(_format[, _values]) +/// +/// @param {String} _format +/// @param {Array} [_values] +/// +/// @private +function __bbmod_debug(_format, _values=[]) +{ + gml_pragma("forceinline"); + __bbmod_log(__BBMOD_ELogLevel.Debug, _format, _values); +} + +/// @func __bbmod_info(_format[, _values]) +/// +/// @param {String} _format +/// @param {Array} [_values] +/// +/// @private +function __bbmod_info(_format, _values=[]) +{ + gml_pragma("forceinline"); + __bbmod_log(__BBMOD_ELogLevel.Info, _format, _values); +} + +/// @func __bbmod_warning(_format[, _values]) +/// +/// @param {String} _format +/// @param {Array} [_values] +/// +/// @private +function __bbmod_warning(_format, _values=[]) +{ + gml_pragma("forceinline"); + __bbmod_log(__BBMOD_ELogLevel.Warning, _format, _values); +} + +/// @func __bbmod_error(_format[, _values]) +/// +/// @param {String} _format +/// @param {Array} [_values] +/// +/// @private +function __bbmod_error(_format, _values=[]) +{ + gml_pragma("forceinline"); + __bbmod_log(__BBMOD_ELogLevel.Error, _format, _values); +} diff --git a/scripts/__bbmod_logging/__bbmod_logging.yy b/scripts/__bbmod_logging/__bbmod_logging.yy new file mode 100644 index 000000000..60dfff19d --- /dev/null +++ b/scripts/__bbmod_logging/__bbmod_logging.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_logging", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Utils", + "path": "folders/_Extensions/BBMOD/Core/Utils.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_particles/__bbmod_particles.gml b/scripts/__bbmod_particles/__bbmod_particles.gml new file mode 100644 index 000000000..70c7d07de --- /dev/null +++ b/scripts/__bbmod_particles/__bbmod_particles.gml @@ -0,0 +1,153 @@ +/// @macro {Struct.BBMOD_VertexFormat} Vertex format of a single billboard +/// particle. +/// @see BBMOD_VertexFormat +#macro BBMOD_VFORMAT_PARTICLE __bbmod_vformat_particle() + +/// @macro {Struct.BBMOD_VertexFormat} Vertex format of dynamic batch of +/// billboard particles. +/// @see BBMOD_VertexFormat +#macro BBMOD_VFORMAT_PARTICLE_BATCHED __bbmod_vformat_particle_batched() + +/// @macro {Struct.BBMOD_BaseShader} Shader for rendering dynamic batches +/// of unlit billboard particles. +/// @see BBMOD_BaseShader +#macro BBMOD_SHADER_PARTICLE_UNLIT __bbmod_shader_particle_unlit() + +/// @macro {Struct.BBMOD_DefaultShader} Shader for rendering dynamic batches +/// of lit billboard particles. +/// @see BBMOD_DefaultShader +#macro BBMOD_SHADER_PARTICLE_LIT __bbmod_shader_particle_lit() + +/// @macro {Struct.BBMOD_DefaultShader} Shader for rendering dynamic batches +/// of billboard particles into depth buffers. +/// @see BBMOD_BaseShader +#macro BBMOD_SHADER_PARTICLE_DEPTH __bbmod_shader_particle_depth() + +/// @macro {Struct.BBMOD_DefaultMaterial} Default material for rendering dynamic +/// batches of unlit billboard particles. +/// @see BBMOD_DefaultMaterial +#macro BBMOD_MATERIAL_PARTICLE_UNLIT __bbmod_material_particle_unlit() + +/// @macro {Struct.BBMOD_DefaultMaterial} Default material for rendering dynamic +/// batches of lit billboard particles. +/// @see BBMOD_DefaultMaterial +#macro BBMOD_MATERIAL_PARTICLE_LIT __bbmod_material_particle_lit() + +/// @var {Struct.BBMOD_Model} A billboard particle model. +#macro BBMOD_MODEL_PARTICLE __bbmod_model_particle() + +function __bbmod_vformat_particle() +{ + static _vformat = new BBMOD_VertexFormat( + true, false, true, false, false, false, false); + return _vformat; +} + +function __bbmod_vformat_particle_batched() +{ + static _vformat = new BBMOD_VertexFormat( + true, false, true, false, false, false, true); + return _vformat; +} + +function __bbmod_shader_particle_unlit() +{ + static _shader = new BBMOD_ParticleShader( + BBMOD_ShParticleUnlit, BBMOD_VFORMAT_PARTICLE_BATCHED); + return _shader; +} + +function __bbmod_shader_particle_lit() +{ + static _shader = new BBMOD_ParticleShader( + BBMOD_ShParticleLit, BBMOD_VFORMAT_PARTICLE_BATCHED); + return _shader; +} + +function __bbmod_shader_particle_depth() +{ + static _shader = new BBMOD_BaseShader( + BBMOD_ShParticleDepth, BBMOD_VFORMAT_PARTICLE_BATCHED); + return _shader; +} + +function __bbmod_material_particle_unlit() +{ + static _material = undefined; + if (_material == undefined) + { + _material = new BBMOD_ParticleMaterial(BBMOD_SHADER_PARTICLE_UNLIT) + _material.BaseOpacity = sprite_get_texture(BBMOD_SprParticle, 0); + _material.AlphaTest = 0.01; + _material.AlphaBlend = true; + _material.ZWrite = false; + } + return _material; +} + +function __bbmod_material_particle_lit() +{ + static _material = undefined; + if (_material == undefined) + { + _material = BBMOD_MATERIAL_PARTICLE_UNLIT.clone(); + _material.set_shader(BBMOD_ERenderPass.Forward, BBMOD_SHADER_PARTICLE_LIT); + //_material.set_shader(BBMOD_ERenderPass.Shadows, BBMOD_SHADER_PARTICLE_DEPTH); + _material.ShadowmapBias = 0.01; + } + return _material; +} + +function __bbmod_model_particle() +{ + static _model = undefined; + if (_model == undefined) + { + var _model = new BBMOD_Model(); + var _node = new BBMOD_Node(_model); + var _mesh = new BBMOD_Mesh(BBMOD_VFORMAT_PARTICLE, _model); + + var _vbuffer = vertex_create_buffer(); + vertex_begin(_vbuffer, BBMOD_VFORMAT_PARTICLE.Raw); + // 1 + // |\ + // 3-2 + vertex_position_3d(_vbuffer, -0.5, -0.5, 0.0); + vertex_texcoord(_vbuffer, 0.0, 0.0); + vertex_position_3d(_vbuffer, +0.5, +0.5, 0.0); + vertex_texcoord(_vbuffer, 1.0, 1.0); + vertex_position_3d(_vbuffer, -0.5, +0.5, 0.0); + vertex_texcoord(_vbuffer, 0.0, 1.0); + // 1-2 + // \| + // 3 + vertex_position_3d(_vbuffer, -0.5, -0.5, 0.0); + vertex_texcoord(_vbuffer, 0.0, 0.0); + vertex_position_3d(_vbuffer, +0.5, -0.5, 0.0); + vertex_texcoord(_vbuffer, 1.0, 0.0); + vertex_position_3d(_vbuffer, +0.5, +0.5, 0.0); + vertex_texcoord(_vbuffer, 1.0, 1.0); + vertex_end(_vbuffer); + _mesh.VertexBuffer = _vbuffer; + + _node.Name = "Root"; + _node.Meshes = [0]; + _node.IsRenderable = true; + + _model.VertexFormat = BBMOD_VFORMAT_PARTICLE; + _model.Meshes = [_mesh]; + _model.NodeCount = 1; + _model.RootNode = _node; + _model.MaterialCount = 1; + _model.MaterialNames = ["Material"]; + _model.Materials = [BBMOD_MATERIAL_PARTICLE_UNLIT]; + } + return _model; +} + +bbmod_shader_register("BBMOD_SHADER_PARTICLE_DEPTH", BBMOD_SHADER_PARTICLE_DEPTH); +bbmod_shader_register("BBMOD_SHADER_PARTICLE_LIT", BBMOD_SHADER_PARTICLE_LIT); +bbmod_shader_register("BBMOD_SHADER_PARTICLE_UNLIT", BBMOD_SHADER_PARTICLE_UNLIT); + +bbmod_material_register("BBMOD_MATERIAL_PARTICLE_LIT", BBMOD_MATERIAL_PARTICLE_LIT); +bbmod_material_register("BBMOD_MATERIAL_PARTICLE_UNLIT", BBMOD_MATERIAL_PARTICLE_UNLIT); diff --git a/scripts/__bbmod_particles/__bbmod_particles.yy b/scripts/__bbmod_particles/__bbmod_particles.yy new file mode 100644 index 000000000..e0bb7cc9a --- /dev/null +++ b/scripts/__bbmod_particles/__bbmod_particles.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_particles", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Particles", + "path": "folders/_Extensions/BBMOD/Particles.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_path/__bbmod_path.gml b/scripts/__bbmod_path/__bbmod_path.gml new file mode 100644 index 000000000..db6b6be4b --- /dev/null +++ b/scripts/__bbmod_path/__bbmod_path.gml @@ -0,0 +1,172 @@ +/// @macro {String} Directory separator. +/// @private +#macro __BBMOD_PATH_SEPARATOR ((os_type == os_windows) ? "\\" : "/") + +/// @macro {String} The current directory in relative paths. +/// @private +#macro __BBMOD_PATH_CURRENT "." + +/// @macro {String} The parent directory in relative paths. +/// @private +#macro __BBMOD_PATH_PARENT ".." + +/// @func bbmod_path_normalize(_path) +/// +/// @desc Normalizes path for the current platform. +/// +/// @param {String} _path The path to normalize. +/// +/// @return {String} The normalized path. +function bbmod_path_normalize(_path) +{ + gml_pragma("forceinline"); + return string_replace_all(_path, + (os_type == os_windows) ? "/" : "\\", __BBMOD_PATH_SEPARATOR); +} + +/// @func bbmod_path_is_relative(_path) +/// +/// @desc Checks if a path is relative. +/// +/// @param {String} _path The path to check. +/// +/// @return {Bool} Returns `true` if the path is relative. +function bbmod_path_is_relative(_path) +{ + gml_pragma("forceinline"); + _path = bbmod_path_normalize(_path); + return (bbmod_string_starts_with(_path, __BBMOD_PATH_CURRENT + __BBMOD_PATH_SEPARATOR) + || bbmod_string_starts_with(_path, __BBMOD_PATH_PARENT + __BBMOD_PATH_SEPARATOR)); +} + + +/// @func bbmod_path_is_absolute(_path) +/// +/// @desc Checks if a path is absolute. +/// +/// @param {String} _path The path to check. +/// +/// @return {Bool} Returns `true` if the path is absolute. +function bbmod_path_is_absolute(_path) +{ + gml_pragma("forceinline"); + return !bbmod_path_is_relative(_path); +} + +/// @func bbmod_path_get_relative(_path[, _start]) +/// +/// @desc Retrieves a relative version of a path. +/// +/// @param {String} _path The path to get a relative version of. Must be +/// absolute! +/// @param {String} [_start] Which path should it be relative to. Must be +/// absolute! Defaults to the working directory. +/// +/// @return {String} The relative path. +/// +/// @note If given paths are not on the same drive then an unmodified path is +/// returned! +function bbmod_path_get_relative(_path, _start=working_directory) +{ + _path = bbmod_path_normalize(_path); + + var _pathExploded = []; + var _pathExplodedSize = bbmod_string_explode(_path, __BBMOD_PATH_SEPARATOR, _pathExploded); + + var _startExploded = []; + var _startExplodedSize = bbmod_string_explode(_start, __BBMOD_PATH_SEPARATOR, _startExploded); + + if (os_type == os_windows + && _pathExploded[0] != _startExploded[0]) + { + return _path; + } + + var _pathRelative = []; + var _levelStart = 0; + repeat (min(_startExplodedSize, _pathExplodedSize)) + { + if (_startExploded[_levelStart] != _pathExploded[_levelStart]) + { + break; + } + ++_levelStart; + } + + var _levelEnd = _pathExplodedSize; + var _levelCurrent = _startExplodedSize; + + if (_levelCurrent > _levelStart) + { + while (_levelCurrent > _levelStart) + { + array_push(_pathRelative, __BBMOD_PATH_PARENT); + --_levelCurrent; + } + } + else + { + array_push(_pathRelative, __BBMOD_PATH_CURRENT); + } + + while (_levelCurrent < _levelEnd) + { + array_push(_pathRelative, _pathExploded[_levelCurrent++]); + } + + return bbmod_string_join_array(__BBMOD_PATH_SEPARATOR, _pathRelative); +} + +/// @func bbmod_path_get_absolute(_path[, _start]) +/// +/// @desc Retrieves an absolute version of a path. +/// +/// @param {String} _path The relative path to turn into absolute. +/// @param {String} [_start] Which path is it relative to. Must be absolute! +/// Defaults to the working directory. +/// +/// @return {String} The absolute path. +/// +/// @note If the path is already absolute then an unmodified path is returned. +function bbmod_path_get_absolute(_path, _start=working_directory) +{ + _path = bbmod_path_normalize(_path); + + if (bbmod_path_is_absolute(_path)) + { + return _path; + } + + var _pathExploded = []; + var _pathExplodedSize = bbmod_string_explode(_path, __BBMOD_PATH_SEPARATOR, _pathExploded); + + var _startExploded = []; + var _startExplodedSize = bbmod_string_explode(_start, __BBMOD_PATH_SEPARATOR, _startExploded); + + var _pathRelative = []; + array_copy(_pathRelative, 0, _startExploded, 0, _startExplodedSize); + + var i = _startExplodedSize - 1; + var j = 0; + + repeat (_pathExplodedSize) + { + var _str = _pathExploded[j++]; + + switch (_str) + { + case __BBMOD_PATH_CURRENT: + break; + + case __BBMOD_PATH_PARENT: + array_delete(_pathRelative, i--, 1); + break; + + default: + array_push(_pathRelative, _str); + break; + } + } + + return bbmod_string_join_array(__BBMOD_PATH_SEPARATOR, _pathRelative); +} diff --git a/scripts/__bbmod_path/__bbmod_path.yy b/scripts/__bbmod_path/__bbmod_path.yy new file mode 100644 index 000000000..9f18c60d3 --- /dev/null +++ b/scripts/__bbmod_path/__bbmod_path.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_path", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Utils", + "path": "folders/_Extensions/BBMOD/Core/Utils.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_render_pass/__bbmod_render_pass.gml b/scripts/__bbmod_render_pass/__bbmod_render_pass.gml new file mode 100644 index 000000000..6e0a642aa --- /dev/null +++ b/scripts/__bbmod_render_pass/__bbmod_render_pass.gml @@ -0,0 +1,128 @@ +/// @enum Enumeration of render passes. +enum BBMOD_ERenderPass +{ + /// @member Render pass where shadow-casting are objects rendered into + /// shadow maps. + Shadows = 0, + /// @member Render pass where opaque are rendered into an off-screen depth + /// buffer when using {@link BBMOD_DefaultRenderer}. + /// @deprecated Please use {@link BBMOD_ERenderPass.DepthOnly} instead. + Deferred = 1, + /// @member Render pass where opaque objects are rendered into an off-screen + /// depth buffer. + DepthOnly = 1, + /// @member Render pass where opaque objects are rendered into a G-Buffer. + GBuffer, + /// @member Render pass where opaque objects are rendered into the frame + /// buffer. + Forward, + /// @member Render pass where alpha-blended objects are rendered. + Alpha, + /// @member Render pass where instance IDs are rendered into an off-screen + /// buffer. + Id, + /// @member Total number of members of this enum. + SIZE, +}; + +/// @var {Real} +/// @private +global.__bbmodRenderPass = BBMOD_ERenderPass.Forward; + +/// @func bbmod_render_pass_to_string(_pass) +/// +/// @desc Retrieves a name of a render pass. +/// +/// @param {Real} _pass The render pass to get the name of. Use values from +/// {@link BBMOD_ERenderPass}. +/// +/// @return {String} The name of the render pass. +function bbmod_render_pass_to_string(_pass) +{ + switch (_pass) + { + case BBMOD_ERenderPass.Shadows: + return "Shadows"; + + case BBMOD_ERenderPass.DepthOnly: + return "DepthOnly"; + + case BBMOD_ERenderPass.Forward: + return "Forward"; + + case BBMOD_ERenderPass.Alpha: + return "Alpha"; + + case BBMOD_ERenderPass.Id: + return "Id"; + + default: + return ""; + } +} + +/// @func bbmod_render_pass_from_string(_name) +/// +/// @desc Retrieves a render pass from its name. +/// +/// @param {String} _name The name of the render pass. +/// +/// @return {Real} One of the render passes defined in {@link BBMOD_ERenderPass}. +/// +/// @throws {BBMOD_Exception} If an invalid name is passed. +function bbmod_render_pass_from_string(_name) +{ + switch (_name) + { + case "Shadows": + return BBMOD_ERenderPass.Shadows; + + case "Deferred": + case "DepthOnly": + return BBMOD_ERenderPass.DepthOnly; + + case "Forward": + return BBMOD_ERenderPass.Forward; + + case "Alpha": + return BBMOD_ERenderPass.Alpha; + + case "Id": + return BBMOD_ERenderPass.Id; + + default: + throw new BBMOD_Exception("Invalid render pass \"" + _name + "\"!"); + } +} + +/// @func bbmod_render_pass_get() +/// +/// @desc Retrieves the current render pass. +/// +/// @return {Real} The current render pass. +/// +/// @see bbmod_render_pass_set +/// @see BBMOD_ERenderPass +function bbmod_render_pass_get() +{ + gml_pragma("forceinline"); + return global.__bbmodRenderPass; +} + +/// @func bbmod_render_pass_set(_pass) +/// +/// @desc Sets the current render pass. Only meshes with materials that have +/// a shader defined for the render pass will be rendered. +/// +/// @param {Real} _pass The render pass. Use values from {@link BBMOD_ERenderPass}. +/// By default this is set to {@link BBMOD_ERenderPass.Forward}. +/// +/// @see bbmod_render_pass_get +/// @see BBMOD_BaseMaterial.set_shader +/// @see BBMOD_ERenderPass +function bbmod_render_pass_set(_pass) +{ + gml_pragma("forceinline"); + bbmod_material_reset(); + global.__bbmodRenderPass = _pass; +} diff --git a/scripts/__bbmod_render_pass/__bbmod_render_pass.yy b/scripts/__bbmod_render_pass/__bbmod_render_pass.yy new file mode 100644 index 000000000..855aeb29f --- /dev/null +++ b/scripts/__bbmod_render_pass/__bbmod_render_pass.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_render_pass", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_save/__bbmod_save.gml b/scripts/__bbmod_save/__bbmod_save.gml new file mode 100644 index 000000000..5573f04ea --- /dev/null +++ b/scripts/__bbmod_save/__bbmod_save.gml @@ -0,0 +1,973 @@ +/// @macro {Real} +/// @private +#macro __BBMOD_SAVE_VERSION 0 + +/// @var {Id.DsMap>} +/// @private +global.__bbmodObjectProperties = ds_map_create(); + +/// @func bbmod_object_add_property(_object, _property) +/// +/// @desc Adds a serializable property to an object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {Struct.BBMOD_Property} _property The property to add. +/// +/// @see BBMOD_Property +function bbmod_object_add_property(_object, _property) +{ + if (!ds_map_exists(global.__bbmodObjectProperties, _object)) + { + ds_map_add_map(global.__bbmodObjectProperties, _object, ds_map_create()); + } + + global.__bbmodObjectProperties[? _object][? _property.Name] = _property; +} + +/// @func bbmod_object_add_bool(_object, _name) +/// +/// @desc Adds a {@link BBMOD_EPropertyType.Bool} property to an object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_bool(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.Bool); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_color(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.Color} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_color(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.Color); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_gmfont(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.GMFont} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_gmfont(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.GMFont); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_gmobject(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.GMObject} property to +/// an object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_gmobject(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.GMObject); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_gmpath(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.GMPath} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_gmpath(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.GMPath); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_gmroom(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.GMRoom} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_gmroom(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.GMRoom); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_gmscript(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.GMScript} property to +/// an object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_gmscript(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.GMScript); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_gmshader(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.GMShader} property to +/// an object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_gmshader(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.GMShader); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_gmsound(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.GMSound} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_gmsound(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.GMSound); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_gmsprite(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.GMSprite} property to +/// an object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_gmsprite(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.GMSprite); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_gmtileset(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.GMTileSet} property to +/// an object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_gmtileset(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.GMTileSet); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_gmtimeline(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.GMTimeline} property to +/// an object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_gmtimeline(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.GMTimeline); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_matrix(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.Matrix} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_matrix(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.Matrix); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_path(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.Path} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_path(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.Path); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_quaternion(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.Quaternion} property to +/// an object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_quaternion(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.Quaternion); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_real(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.Real} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_real(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.Real); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_real_array(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.RealArray} property to +/// an object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_real_array(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.RealArray); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_string(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.String} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_string(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.String); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_vec2(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.Vec2} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_vec2(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.Vec2); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_vec3(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.Vec3} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_vec3(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.Vec3); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_add_vec4(_object, _name) +/// +/// @desc Adds a serialiazble {@link BBMOD_EPropertyType.Vec4} property to an +/// object. +/// +/// @param {Asset.GMObject} _object The object to add the property to. +/// @param {String} _name The property name. +/// +/// @return {Struct.BBMOD_Property} The created property. +/// +/// @note This is just a shorthand for {@link bbmod_object_add_property}. +/// +/// @see bbmod_object_add_property +/// @see BBMOD_Property +function bbmod_object_add_vec4(_object, _name) +{ + gml_pragma("forceinline"); + var _property = new BBMOD_Property(_name, BBMOD_EPropertyType.Vec4); + bbmod_object_add_property(_object, _property); + return _property; +} + +/// @func bbmod_object_get_property_map(_object, _dest) +/// +/// @desc Retrieves a map of all serializable properties of an object. +/// +/// @param {Asset.GMObject} _object The object to get serializable properties of. +/// @param {Id.DsMap} _dest A map to store the +/// properties to. It is not automatically cleared before the properties are added! +/// +/// @return {Real} Number of serializable properties that the object has. +function bbmod_object_get_property_map(_object, _dest) +{ + var _count = 0; + var _current = _object; + + while (object_exists(_current)) + { + if (ds_map_exists(global.__bbmodObjectProperties, _current)) + { + var _properties = global.__bbmodObjectProperties[? _current]; + var _propertyName = ds_map_find_first(_properties); + + repeat (ds_map_size(_properties)) + { + if (!ds_map_exists(_dest, _propertyName)) + { + _dest[? _propertyName] = _properties[? _propertyName]; + ++_count; + } + + _propertyName = ds_map_find_next(_properties, _propertyName); + } + } + + _current = object_get_parent(_current); + } + + return _count; +} + +/// @func bbmod_object_get_property_array(_object, _dest) +/// +/// @desc Retrieves an array of all serializable properties of an object. +/// +/// @param {Asset.GMObject} _object The object to get serializable properties of. +/// @param {Array} _dest An array to store the properties +/// to. It is not automatically cleared before the properties are added! +/// +/// @return {Real} Number of serializable properties that the object has. +function bbmod_object_get_property_array(_object, _dest) +{ + static _map = ds_map_create(); + + ds_map_clear(_map); + bbmod_object_get_property_map(_object, _map); + + var _size = ds_map_size(_map); + + if (_size > 0) + { + var _propertyName = ds_map_find_first(_map); + + repeat (_size) + { + array_push(_dest, _map[? _propertyName]); + _propertyName = ds_map_find_next(_map, _propertyName); + } + } + + return _size; +} + +/// @func bbmod_instance_to_buffer(_instance, _buffer, _properties) +/// +/// @desc Serializes an instance to a buffer. +/// +/// @param {Id.Instance} _instance The instance to serialize. +/// @param {Id.Buffer} _buffer The buffer to serialize the instance to. +/// @param {Array} _properties Array of +/// properties to serialize. +/// +/// @see bbmod_object_get_property_array +function bbmod_instance_to_buffer(_instance, _buffer, _properties) +{ + with (_instance) + { + buffer_write(_buffer, buffer_string, object_get_name(object_index)); + buffer_write(_buffer, buffer_f32, x); + buffer_write(_buffer, buffer_f32, y); + buffer_write(_buffer, buffer_string, layer_get_name(layer)); + + var _propsCount = array_length(_properties); + var _propertyIndex = 0; + + repeat (_propsCount) + { + var _property = _properties[_propertyIndex++]; + var _propertyName = _property.Name; + var _propertyType = _property.Type; + + switch (_propertyType) + { + case BBMOD_EPropertyType.Bool: + buffer_write(_buffer, buffer_bool, variable_instance_get(id, _propertyName)); + break; + + case BBMOD_EPropertyType.Color: + var _color = variable_instance_get(id, _propertyName); + buffer_write(_buffer, buffer_f32, _color.Red); + buffer_write(_buffer, buffer_f32, _color.Green); + buffer_write(_buffer, buffer_f32, _color.Blue); + buffer_write(_buffer, buffer_f32, _color.Alpha); + break; + + case BBMOD_EPropertyType.GMFont: + buffer_write(_buffer, buffer_string, font_get_name(variable_instance_get(id, _propertyName))); + break; + + case BBMOD_EPropertyType.GMObject: + buffer_write(_buffer, buffer_string, object_get_name(variable_instance_get(id, _propertyName))); + break; + + case BBMOD_EPropertyType.GMPath: + buffer_write(_buffer, buffer_string, path_get_name(variable_instance_get(id, _propertyName))); + break; + + case BBMOD_EPropertyType.GMRoom: + buffer_write(_buffer, buffer_string, room_get_name(variable_instance_get(id, _propertyName))); + break; + + case BBMOD_EPropertyType.GMScript: + buffer_write(_buffer, buffer_string, script_get_name(variable_instance_get(id, _propertyName))); + break; + + case BBMOD_EPropertyType.GMShader: + buffer_write(_buffer, buffer_string, shader_get_name(variable_instance_get(id, _propertyName))); + break; + + case BBMOD_EPropertyType.GMSound: + buffer_write(_buffer, buffer_string, audio_get_name(variable_instance_get(id, _propertyName))); + break; + + case BBMOD_EPropertyType.GMSprite: + buffer_write(_buffer, buffer_string, sprite_get_name(variable_instance_get(id, _propertyName))); + break; + + case BBMOD_EPropertyType.GMTileSet: + buffer_write(_buffer, buffer_string, tileset_get_name(variable_instance_get(id, _propertyName))); + break; + + case BBMOD_EPropertyType.GMTimeline: + buffer_write(_buffer, buffer_string, timeline_get_name(variable_instance_get(id, _propertyName))); + break; + + case BBMOD_EPropertyType.Path: + buffer_write(_buffer, buffer_string, variable_instance_get(id, _propertyName)); + break; + + case BBMOD_EPropertyType.Quaternion: + var _quaternion = variable_instance_get(id, _propertyName); + buffer_write(_buffer, buffer_f32, _quaternion.X); + buffer_write(_buffer, buffer_f32, _quaternion.Y); + buffer_write(_buffer, buffer_f32, _quaternion.Z); + buffer_write(_buffer, buffer_f32, _quaternion.W); + break; + + case BBMOD_EPropertyType.Matrix: + var _matrix = variable_instance_get(id, _propertyName); + var i = 0; + repeat (16) + { + buffer_write(_buffer, buffer_f32, _matrix[i++]); + } + break; + + case BBMOD_EPropertyType.Real: + buffer_write(_buffer, buffer_f32, variable_instance_get(id, _propertyName)); + break; + + case BBMOD_EPropertyType.RealArray: + var _array = variable_instance_get(id, _propertyName); + var _size = array_length(_size); + buffer_write(_buffer, buffer_u32, _size); + var i = 0; + repeat (_size) + { + buffer_write(_buffer, buffer_f32, _array[i++]); + } + break; + + case BBMOD_EPropertyType.String: + buffer_write(_buffer, buffer_string, variable_instance_get(id, _propertyName)); + break; + + case BBMOD_EPropertyType.Vec2: + var _vec2 = variable_instance_get(id, _propertyName); + buffer_write(_buffer, buffer_f32, _vec2.X); + buffer_write(_buffer, buffer_f32, _vec2.Y); + break; + + case BBMOD_EPropertyType.Vec3: + var _vec3 = variable_instance_get(id, _propertyName); + buffer_write(_buffer, buffer_f32, _vec3.X); + buffer_write(_buffer, buffer_f32, _vec3.Y); + buffer_write(_buffer, buffer_f32, _vec3.Z); + break; + + case BBMOD_EPropertyType.Vec4: + var _vec4 = variable_instance_get(id, _propertyName); + buffer_write(_buffer, buffer_f32, _vec4.X); + buffer_write(_buffer, buffer_f32, _vec4.Y); + buffer_write(_buffer, buffer_f32, _vec4.Z); + buffer_write(_buffer, buffer_f32, _vec4.W); + break; + + default: + throw new BBMOD_Exception("Invalid property type " + string(_propertyType) + "!"); + } + } + } +} + +/// @func bbmod_instance_from_buffer(_buffer, _properties) +/// +/// @desc Deserializes an instance from a buffer. +/// +/// @param {Id.Buffer} _buffer The buffer to deserialize an instance from. +/// @param {Id.DsMap>} _properties A mapping +/// from object name to an array of properties of the object. +/// +/// @return {Id.Instance} The created instnace. +/// +/// @throws {BBMOD_Exception} If an error occurs. +function bbmod_instance_from_buffer(_buffer, _properties) +{ + var _objectName = buffer_read(_buffer, buffer_string); + var _objectIndex = asset_get_index(_objectName); + + if (_objectIndex == -1) + { + throw new BBMOD_Exception("Object \"" + _objectName + "\" not found!"); + } + + var _x = buffer_read(_buffer, buffer_f32); + var _y = buffer_read(_buffer, buffer_f32); + var _layerName = buffer_read(_buffer, buffer_string); + var _instance = instance_create_layer(_x, _y, _layerName, _objectIndex); + + if (!ds_map_exists(_properties, _objectName)) + { + return _instance; + } + + var _objectProperties = _properties[? _objectName]; + + with (_instance) + { + var _propertyIndex = 0; + repeat (array_length(_objectProperties)) + { + var _property = _objectProperties[_propertyIndex++]; + var _propertyName = _property.Name; + var _propertyType = _property.Type; + + switch (_propertyType) + { + case BBMOD_EPropertyType.Bool: + variable_instance_set(id, _propertyName, buffer_read(_buffer, buffer_bool)); + break; + + case BBMOD_EPropertyType.Color: + var _r = buffer_read(_buffer, buffer_f32); + var _g = buffer_read(_buffer, buffer_f32); + var _b = buffer_read(_buffer, buffer_f32); + var _a = buffer_read(_buffer, buffer_f32); + variable_instance_set(id, _propertyName, new BBMOD_Color(_r, _g, _b, _a)); + break; + + case BBMOD_EPropertyType.GMFont: + case BBMOD_EPropertyType.GMObject: + case BBMOD_EPropertyType.GMPath: + case BBMOD_EPropertyType.GMRoom: + case BBMOD_EPropertyType.GMScript: + case BBMOD_EPropertyType.GMShader: + case BBMOD_EPropertyType.GMSound: + case BBMOD_EPropertyType.GMSprite: + case BBMOD_EPropertyType.GMTileSet: + case BBMOD_EPropertyType.GMTimeline: + variable_instance_set(id, _propertyName, asset_get_index(buffer_read(_buffer, buffer_string))); + break; + + case BBMOD_EPropertyType.Path: + variable_instance_set(id, _propertyName, buffer_read(_buffer, buffer_string)); + break; + + case BBMOD_EPropertyType.Quaternion: + var _x = buffer_read(_buffer, buffer_f32); + var _y = buffer_read(_buffer, buffer_f32); + var _z = buffer_read(_buffer, buffer_f32); + var _w = buffer_read(_buffer, buffer_f32); + variable_instance_set(id, _propertyName, new BBMOD_Quaternion(_x, _y, _z, _w)); + break; + + case BBMOD_EPropertyType.Matrix: + var _matrix = array_create(16, 0); + var i = 0; + repeat (16) + { + _matrix[@ i++] = buffer_read(_buffer, buffer_f32); + } + variable_instance_set(id, _propertyName, _matrix); + break; + + case BBMOD_EPropertyType.Real: + variable_instance_set(id, _propertyName, buffer_read(_buffer, buffer_f32)); + break; + + case BBMOD_EPropertyType.RealArray: + var _size = buffer_read(_buffer, buffer_u32); + var _array = array_create(_size, 0); + var i = 0; + repeat (_size) + { + _array[@ i++] = buffer_read(_buffer, buffer_f32); + } + variable_instance_set(id, _propertyName, _array); + break; + + case BBMOD_EPropertyType.String: + variable_instance_set(id, _propertyName, buffer_read(_buffer, buffer_string)); + break; + + case BBMOD_EPropertyType.Vec2: + var _x = buffer_read(_buffer, buffer_f32); + var _y = buffer_read(_buffer, buffer_f32); + variable_instance_set(id, _propertyName, new BBMOD_Vec2(_x, _y)); + break; + + case BBMOD_EPropertyType.Vec3: + var _x = buffer_read(_buffer, buffer_f32); + var _y = buffer_read(_buffer, buffer_f32); + var _z = buffer_read(_buffer, buffer_f32); + variable_instance_set(id, _propertyName, new BBMOD_Vec3(_x, _y, _z)); + break; + + case BBMOD_EPropertyType.Vec4: + var _x = buffer_read(_buffer, buffer_f32); + var _y = buffer_read(_buffer, buffer_f32); + var _z = buffer_read(_buffer, buffer_f32); + var _w = buffer_read(_buffer, buffer_f32); + variable_instance_set(id, _propertyName, new BBMOD_Vec4(_x, _y, _z, _w)); + break; + + default: + throw new BBMOD_Exception("Invalid property type " + string(_propertyType) + "!"); + } + } + } + + return _instance; +} + +/// @func bbmod_save_instances_to_buffer(_object, _buffer) +/// +/// @desc Saves all instances of an object to a buffer. +/// +/// @param {Asset.GMObject} _object Use keyword `all` to save all existing +/// instances. +/// @param {Id.Buffer} _buffer The buffer to save the instances to. +/// +/// @return {Real} Number of saved instances. +function bbmod_save_instances_to_buffer(_object, _buffer) +{ + var _properties = ds_map_create(); + + // Write instances + var _bufferInstances = buffer_create(1, buffer_grow, 1); + var _instanceCount = instance_number(_object); + buffer_write(_bufferInstances, buffer_u64, _instanceCount); + + with (_object) + { + var _instanceProps; + if (!ds_map_exists(_properties, object_index)) + { + _instanceProps = []; + bbmod_object_get_property_array(object_index, _instanceProps); + _properties[? object_index] = _instanceProps; + } + else + { + _instanceProps = _properties[? object_index]; + } + bbmod_instance_to_buffer(id, _bufferInstances, _instanceProps); + } + + // Write header + var _objectCount = ds_map_size(_properties); + buffer_write(_buffer, buffer_u8, __BBMOD_SAVE_VERSION); + buffer_write(_buffer, buffer_u16, _objectCount); + if (_objectCount > 0) + { + var _objectIndex = ds_map_find_first(_properties); + repeat (_objectCount) + { + var _propertyArray = _properties[? _objectIndex]; + var _propertyArraySize = array_length(_propertyArray); + buffer_write(_buffer, buffer_string, object_get_name(_objectIndex)); + buffer_write(_buffer, buffer_u16, _propertyArraySize); + for (var i = 0; i < _propertyArraySize; ++i) + { + var _property = _propertyArray[i]; + buffer_write(_buffer, buffer_string, _property.Name); + buffer_write(_buffer, buffer_u8, _property.Type); + } + _objectIndex = ds_map_find_next(_properties, _objectIndex); + } + } + + // Copy instances into the destination buffer + buffer_copy(_bufferInstances, 0, buffer_get_size(_bufferInstances), _buffer, buffer_tell(_buffer)); + + // Free data + ds_map_destroy(_properties); + buffer_delete(_bufferInstances); + + return _instanceCount; +} + +/// @func bbmod_load_instances_from_buffer(_buffer[, _idsOut]) +/// +/// @desc Loads instances from a buffer. +/// +/// @param {Id.Buffer} _buffer A buffer to load instances from. +/// @param {Array} [_idsOut] An array to hold all loaded +/// instances. +/// +/// @return {Real} Returns number of loaded instances. +/// +/// @throws {BBMOD_Exception} If an error occurs. +function bbmod_load_instances_from_buffer(_buffer, _idsOut=undefined) +{ + var _saveVersion = buffer_read(_buffer, buffer_u8); + + if (_saveVersion != __BBMOD_SAVE_VERSION) + { + throw new BBMOD_Exception("Invalid save version " + string(_saveVersion) + "!"); + } + + var _objectCount = buffer_read(_buffer, buffer_u16); + var _propertyMap = ds_map_create(); + + repeat (_objectCount) + { + var _objectName = buffer_read(_buffer, buffer_string); + var _propertyCount = buffer_read(_buffer, buffer_u16); + var _propertyArray = []; + repeat (_propertyCount) + { + var _propertyName = buffer_read(_buffer, buffer_string); + var _propertyType = buffer_read(_buffer, buffer_u8); + array_push(_propertyArray, new BBMOD_Property(_propertyName, _propertyType)); + } + _propertyMap[? _objectName] = _propertyArray; + } + + var _instanceCount = buffer_read(_buffer, buffer_u64); + + if (_idsOut == undefined) + { + repeat (_instanceCount) + { + bbmod_instance_from_buffer(_buffer, _propertyMap); + } + } + else + { + repeat (_instanceCount) + { + array_push(_idsOut, bbmod_instance_from_buffer(_buffer, _propertyMap)); + } + } + + ds_map_destroy(_propertyMap); + + return _instanceCount; +} diff --git a/scripts/__bbmod_save/__bbmod_save.yy b/scripts/__bbmod_save/__bbmod_save.yy new file mode 100644 index 000000000..ac10b9a57 --- /dev/null +++ b/scripts/__bbmod_save/__bbmod_save.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_save", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Save", + "path": "folders/_Extensions/BBMOD/Save.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_sky/__bbmod_sky.gml b/scripts/__bbmod_sky/__bbmod_sky.gml new file mode 100644 index 000000000..0a21ea50a --- /dev/null +++ b/scripts/__bbmod_sky/__bbmod_sky.gml @@ -0,0 +1,25 @@ +/// @macro {Struct.BBMOD_BaseMaterial} A material for rendering RGBM encoded +/// skies. +/// @see BBMOD_BaseMaterial +#macro BBMOD_MATERIAL_SKY __bbmod_material_sky() + +function __bbmod_material_sky() +{ + static _skyRenderQueue = new BBMOD_RenderQueue("Sky", -$FFFFFFFF); + static _material = undefined; + if (_material == undefined) + { + var _skSky = new BBMOD_BaseShader( + BBMOD_ShSky, BBMOD_VFORMAT_DEFAULT); + _material = new BBMOD_BaseMaterial(_skSky); + _material.Culling = cull_noculling; + _material.Mipmapping = mip_off; + _material.ZWrite = false; + _material.ZTest = false; + _material.Filtering = true; + _material.RenderQueue = _skyRenderQueue; + } + return _material; +} + +bbmod_material_register("BBMOD_MATERIAL_SKY", BBMOD_MATERIAL_SKY); diff --git a/scripts/__bbmod_sky/__bbmod_sky.yy b/scripts/__bbmod_sky/__bbmod_sky.yy new file mode 100644 index 000000000..61a101fda --- /dev/null +++ b/scripts/__bbmod_sky/__bbmod_sky.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_sky", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Sky", + "path": "folders/_Extensions/BBMOD/Rendering/Sky.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_ssao/__bbmod_ssao.gml b/scripts/__bbmod_ssao/__bbmod_ssao.gml new file mode 100644 index 000000000..aa3e32c6a --- /dev/null +++ b/scripts/__bbmod_ssao/__bbmod_ssao.gml @@ -0,0 +1,199 @@ +/// @macro {Real} Size of the SSAO noise texture. Must be the same as in shaders! +/// @private +#macro __BBMOD_SSAO_NOISE_TEXTURE_SIZE 4 + +/// @macro {Real} The size of SSAO sampling kernel. The higher the better quality, +/// but lower performance. Must be the same as in shaders! +/// @private +#macro __BBMOD_SSAO_KERNEL_SIZE 8 + +/// @var {Id.Sprite} +/// @private +global.__bbmodSSAONoise = __bbmod_ssao_make_noise(__BBMOD_SSAO_NOISE_TEXTURE_SIZE); + +/// @var {Array} +/// @private +global.__bbmodSSAOKernel = __bbmod_ssao_create_kernel(__BBMOD_SSAO_KERNEL_SIZE); + +/// @func __bbmod_ssao_make_noise(_size) +/// +/// @desc Creates a sprite containing a random noise for the SSAO. +/// +/// @param {Real} _size The size of the sprite. +/// +/// @return {Id.Sprite} The created noise sprite. +/// +/// @private +function __bbmod_ssao_make_noise(_size) +{ + var _seed = random_get_seed(); + randomize(); + var _sur = surface_create(_size, _size); + surface_set_target(_sur); + draw_clear(0); + var _dir = 0; + var _dirStep = 180 / (_size * _size); + for (var i = 0; i < _size; ++i) + { + for (var j = 0; j < _size; ++j) + { + var _col = make_colour_rgb( + (dcos(_dir) * 0.5 + 0.5) * 255, + (dsin(_dir) * 0.5 + 0.5) * 255, + 0); + draw_point_colour(i, j, _col); + _dir += _dirStep; + } + } + surface_reset_target(); + random_set_seed(_seed); + var _sprite = sprite_create_from_surface( + _sur, 0, 0, _size, _size, false, false, 0, 0); + surface_free(_sur); + return _sprite; +} + +/// @func __bbmod_ssao_create_kernel(_size) +/// +/// @desc Generates a kernel of random vectors to be used for the SSAO. +/// +/// @param {Real} _size Number of vectors in the kernel. +/// +/// @return {Array} The created kernel as +/// `[v1X, v1Y, v1Z, v2X, v2Y, v2Z, ..., vnX, vnY, vnZ]`. +/// +/// @private +function __bbmod_ssao_create_kernel(_size) +{ + var _seed = random_get_seed(); + randomize(); + var _kernel = array_create(_size * 2, 0.0); + var _dir = 0; + var _dirStep = 360 / _size; + for (var i = _size - 1; i >= 0; --i) + { + var _len = (i + 1) / _size; + _kernel[i * 2 + 0] = lengthdir_x(_len, _dir); + _kernel[i * 2 + 1] = lengthdir_y(_len, _dir); + _dir += _dirStep; + } + random_set_seed(_seed); + return _kernel; +} + +/// @func bbmod_ssao_draw(_radius, _power, _angleBias, _depthRange, _surSsao, _surWork, _surDepth, _matProj, _clipFar[, _selfOcclusionBias[, _blurDepthRange]]) +/// +/// @desc Renders SSAO into the `_surSsao` surface. +/// +/// @param {Real} _radius Screen-space radius of the occlusion effect. +/// @param {Real} _power Strength of the occlusion effect. Should be greater +/// than 0. +/// @param {Real} _angleBias Angle bias in radians. +/// @param {Real} _depthRange Maximum depth difference of samples. +/// @param {Id.Surface} _surSsao The surface to draw the SSAO to. +/// @param {Id.Surface} _surWork A working surface used for blurring the SSAO. +/// Must have the same size as `_surSsao`! +/// @param {Id.Surface} _surDepth G-buffer surface. +/// @param {Array} _matProj The projection matrix used when rendering the +/// scene. +/// @param {Real} _clipFar Distance to the far clipping plane (same as in the +/// projection used when rendering the scene). +/// @param {Real} [_selfOcclusionBias] Defaults to 0.01. Increase to fix +/// self-occlusion. +/// @param {Real} [_blurDepthRange] Maximum depth difference over which can be SSAO samples +/// blurred. Defaults to 2. +function bbmod_ssao_draw( + _radius, + _power, + _angleBias, + _depthRange, + _surSsao, + _surWork, + _surDepth, + _matProj, + _clipFar, + _selfOcclusionBias=0.01, + _blurDepthRange=2.0) +{ + static _uTexNoise = shader_get_sampler_index(BBMOD_ShSSAO, "u_texNoise"); + static _uTexel = shader_get_uniform(BBMOD_ShSSAO, "u_vTexel"); + static _uClipFar = shader_get_uniform(BBMOD_ShSSAO, "u_fClipFar"); + static _uTanAspect = shader_get_uniform(BBMOD_ShSSAO, "u_vTanAspect"); + static _uSampleKernel = shader_get_uniform(BBMOD_ShSSAO, "u_vSampleKernel"); + static _uRadius = shader_get_uniform(BBMOD_ShSSAO, "u_fRadius"); + static _uPower = shader_get_uniform(BBMOD_ShSSAO, "u_fPower"); + static _uNoiseScale = shader_get_uniform(BBMOD_ShSSAO, "u_vNoiseScale"); + static _uAngleBias = shader_get_uniform(BBMOD_ShSSAO, "u_fAngleBias"); + static _uDepthRange = shader_get_uniform(BBMOD_ShSSAO, "u_fDepthRange"); + static _uSelfOcclusionBias = shader_get_uniform(BBMOD_ShSSAO, "u_fSelfOcclusionBias"); + static _uBlurTexel = shader_get_uniform(BBMOD_ShSSAOBlur, "u_vTexel"); + static _uBlurTexDepth = shader_get_sampler_index(BBMOD_ShSSAOBlur, "u_texDepth"); + static _uBlurClipFar = shader_get_uniform(BBMOD_ShSSAOBlur, "u_fClipFar"); + static _uBlurDepthRange = shader_get_uniform(BBMOD_ShSSAOBlur, "u_fDepthRange"); + + var _tanAspect = (_matProj[11] == 0.0) + ? [1.0, -1.0] // Ortho + : [1.0 / _matProj[0], -1.0 / _matProj[5]]; // Perspective + var _width = surface_get_width(_surSsao); + var _height = surface_get_height(_surSsao); + + gpu_push_state(); + gpu_set_tex_repeat(false); + + static _cam = camera_create(); + camera_set_view_size(_cam, _width, _height); + + gpu_set_tex_filter(false); + + surface_set_target(_surSsao); + camera_apply(_cam); + matrix_set(matrix_world, matrix_build_identity()); + draw_clear(c_white); + shader_set(BBMOD_ShSSAO); + texture_set_stage(_uTexNoise, sprite_get_texture(global.__bbmodSSAONoise, 0)); + gpu_set_texrepeat_ext(_uTexNoise, true); + shader_set_uniform_f(_uTexel, 1.0 / _width, 1.0 / _height); + shader_set_uniform_f(_uClipFar, _clipFar); + shader_set_uniform_f_array(_uTanAspect, _tanAspect); + shader_set_uniform_f_array(_uSampleKernel, global.__bbmodSSAOKernel); + shader_set_uniform_f(_uRadius, _radius); + shader_set_uniform_f(_uPower, _power); + shader_set_uniform_f(_uNoiseScale, + _width / __BBMOD_SSAO_NOISE_TEXTURE_SIZE, + _height / __BBMOD_SSAO_NOISE_TEXTURE_SIZE); + shader_set_uniform_f(_uAngleBias, _angleBias); + shader_set_uniform_f(_uDepthRange, _depthRange); + shader_set_uniform_f(_uSelfOcclusionBias, _selfOcclusionBias); + draw_surface_stretched(_surDepth, 0, 0, _width, _height); + shader_reset(); + surface_reset_target(); + + gpu_set_tex_filter(true); + + shader_set(BBMOD_ShSSAOBlur); + shader_set_uniform_f(_uBlurTexel, 1.0 / _width, 0.0); + shader_set_uniform_f(_uBlurClipFar, _clipFar); + texture_set_stage(_uBlurTexDepth, surface_get_texture(_surDepth)); + gpu_set_tex_filter_ext(_uBlurTexDepth, false); + shader_set_uniform_f(_uBlurDepthRange, _blurDepthRange); + + surface_set_target(_surWork); + camera_apply(_cam); + matrix_set(matrix_world, matrix_build_identity()); + draw_clear(0); + shader_set_uniform_f(_uBlurTexel, 1.0 / _width, 0.0); + draw_surface(_surSsao, 0, 0); + surface_reset_target(); + + surface_set_target(_surSsao); + camera_apply(_cam); + matrix_set(matrix_world, matrix_build_identity()); + draw_clear(0); + shader_set_uniform_f(_uBlurTexel, 0.0, 1.0 / _height); + draw_surface(_surWork, 0, 0); + surface_reset_target(); + + shader_reset(); + + gpu_pop_state(); +} diff --git a/scripts/__bbmod_ssao/__bbmod_ssao.yy b/scripts/__bbmod_ssao/__bbmod_ssao.yy new file mode 100644 index 000000000..68a90d074 --- /dev/null +++ b/scripts/__bbmod_ssao/__bbmod_ssao.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_ssao", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "SSAO", + "path": "folders/_Extensions/BBMOD/Rendering/SSAO.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_string/__bbmod_string.gml b/scripts/__bbmod_string/__bbmod_string.gml new file mode 100644 index 000000000..a85560a34 --- /dev/null +++ b/scripts/__bbmod_string/__bbmod_string.gml @@ -0,0 +1,86 @@ +/// @func bbmod_string_starts_with(_string, _substr) +/// +/// @desc Checks whether a string starts with a substring. +/// +/// @param {String} _string The string to check. +/// @param {String} _substr The substring. +/// +/// @return {Bool} Returns `true` if the string starts with the substring. +function bbmod_string_starts_with(_string, _substr) +{ + gml_pragma("forceinline"); + return (string_pos(_substr, _string) == 1); +} + +/// @func bbmod_string_split_on_first(_string, _delimiter[, _dest]) +/// +/// @desc Splits the string in two at the first occurrence of the delimiter. +/// +/// @param {String} _string The string to split. +/// @param {String} _delimiter The delimiter. +/// @param {Array} [_dest] The destination array. A new one is created +/// if not specified. +/// +/// @return {Array} An array containing `[firstHalf, secondHalf]`. If +/// the delimiter is not found in the string, then `secondHalf` equals an empty +/// string and `firstHalf` is the original string. +function bbmod_string_split_on_first(_string, _delimiter, _dest=[]) +{ + var i = string_pos(_delimiter, _string); + if (i == 0) + { + _dest[@ 0] = _string; + _dest[@ 1] = ""; + } + else + { + _dest[@ 0] = string_copy(_string, 1, i - 1); + _dest[@ 1] = string_delete(_string, 1, i); + } + return _dest; +} + +/// @func bbmod_string_explode(_string, _char, _dest) +/// +/// @desc Splits given string on every occurrence of given character and puts +/// created parts into an an array. +/// +/// @param {String} _string The string to explode. +/// @param {String} _char The character to split the string on. +/// @param {Array} _dest The destination array. +/// +/// @return {Real} Returns number of entries written into the destination array. +function bbmod_string_explode(_string, _char, _dest) +{ + static _temp = array_create(2); + var i = 0; + do + { + bbmod_string_split_on_first(_string, _char, _temp); + _dest[@ i++] = _temp[0]; + _string = _temp[1]; + } + until (_temp[1] == ""); + return i; +} + +/// @func bbmod_string_join_array(_separator, _array) +/// +/// @desc Joins an array into a string, putting separator in between each +/// entry. +/// +/// @param {String} _separator The string to put in between entries. +/// @param {Array} _array The array to join. +/// +/// @return {String} The resulting string. +function bbmod_string_join_array(_separator, _array) +{ + var _string = ""; + var i = 0; + repeat (array_length(_array) - 1) + { + _string += string(_array[i++]) + _separator; + } + _string += string(_array[i]); + return _string; +} diff --git a/scripts/__bbmod_string/__bbmod_string.yy b/scripts/__bbmod_string/__bbmod_string.yy new file mode 100644 index 000000000..360d3c65b --- /dev/null +++ b/scripts/__bbmod_string/__bbmod_string.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_string", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Utils", + "path": "folders/_Extensions/BBMOD/Core/Utils.yy", + }, +} \ No newline at end of file diff --git a/scripts/__bbmod_terrain/__bbmod_terrain.gml b/scripts/__bbmod_terrain/__bbmod_terrain.gml new file mode 100644 index 000000000..83cfbda3a --- /dev/null +++ b/scripts/__bbmod_terrain/__bbmod_terrain.gml @@ -0,0 +1,57 @@ +/// @macro {Struct.BBMOD_DefaultShader} Shader for terrain materials. +#macro BBMOD_SHADER_TERRAIN __bbmod_shader_terrain() + +/// @macro {Struct.BBMOD_DefaultMaterial} Base terrain material. +#macro BBMOD_MATERIAL_TERRAIN __bbmod_material_terrain() + +/// @macro {Struct.BBMOD_DefaultShader} Shader for unlit terrain materials. +#macro BBMOD_SHADER_TERRAIN_UNLIT __bbmod_shader_terrain_unlit() + +/// @macro {Struct.BBMOD_DefaultMaterial} Unlit terrain material. +#macro BBMOD_MATERIAL_TERRAIN_UNLIT __bbmod_material_terrain_unlit() + +function __bbmod_shader_terrain() +{ + static _shader = new BBMOD_DefaultShader( + BBMOD_ShTerrain, BBMOD_VFORMAT_DEFAULT); + return _shader; +} + +function __bbmod_shader_terrain_unlit() +{ + static _shader = new BBMOD_DefaultShader( + BBMOD_ShTerrainUnlit, BBMOD_VFORMAT_DEFAULT); + return _shader; +} + +function __bbmod_material_terrain() +{ + static _material = undefined; + if (_material == undefined) + { + _material = new BBMOD_DefaultMaterial(BBMOD_SHADER_TERRAIN); + _material.set_shader(BBMOD_ERenderPass.Shadows, BBMOD_SHADER_DEFAULT_DEPTH); + _material.Mipmapping = mip_on; + _material.Repeat = true; + _material.AlphaTest = 0.01; + _material.AlphaBlend = true; + } + return _material; +} + +function __bbmod_material_terrain_unlit() +{ + static _material = undefined; + if (_material == undefined) + { + _material = BBMOD_MATERIAL_TERRAIN.clone(); + _material.set_shader(BBMOD_ERenderPass.Forward, BBMOD_SHADER_TERRAIN_UNLIT); + } + return _material; +} + +bbmod_shader_register("BBMOD_SHADER_TERRAIN", BBMOD_SHADER_TERRAIN); +bbmod_shader_register("BBMOD_SHADER_TERRAIN_UNLIT", BBMOD_SHADER_TERRAIN); + +bbmod_material_register("BBMOD_MATERIAL_TERRAIN", BBMOD_MATERIAL_TERRAIN); +bbmod_material_register("BBMOD_MATERIAL_TERRAIN_UNLIT", BBMOD_MATERIAL_TERRAIN_UNLIT); diff --git a/scripts/__bbmod_terrain/__bbmod_terrain.yy b/scripts/__bbmod_terrain/__bbmod_terrain.yy new file mode 100644 index 000000000..48d5d5a44 --- /dev/null +++ b/scripts/__bbmod_terrain/__bbmod_terrain.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "__bbmod_terrain", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Terrain", + "path": "folders/_Extensions/BBMOD/Terrain.yy", + }, +} \ No newline at end of file diff --git a/scripts/addonPanel/addonPanel.gml b/scripts/addonPanel/addonPanel.gml index b96d98558..3fda6c1fb 100644 --- a/scripts/addonPanel/addonPanel.gml +++ b/scripts/addonPanel/addonPanel.gml @@ -28,16 +28,29 @@ function addonPanel(directory) : PanelContent() constructor { return self; } - static init = function() { + function init() { lua_add_file(thread, scriptPath); var runResult = lua_call(thread, "init"); + + array_push(ANIMATION_PRE, animationPreStep); + array_push(ANIMATION_POST, animationPostStep); } init(); function stepBegin() { + __addon_lua_panel_variable(thread, self); + var runResult = lua_call(thread, "step"); } + function animationPreStep() { + var runResult = lua_call(thread, "animationPreStep"); + } + + function animationPostStep() { + var runResult = lua_call(thread, "animationPostStep"); + } + function drawGUI() { var runResult = lua_call(thread, "drawUI"); } @@ -54,7 +67,8 @@ function addonPanel(directory) : PanelContent() constructor { // - static cleanUp = function() { - lua_state_destroy(thread); + function cleanUp() { + array_remove(ANIMATION_PRE, animationPreStep); + array_remove(ANIMATION_POST, animationPostStep); } } \ No newline at end of file diff --git a/scripts/addon_lua/addon_lua.gml b/scripts/addon_lua/addon_lua.gml index e5bfcea15..7b598b9d2 100644 --- a/scripts/addon_lua/addon_lua.gml +++ b/scripts/addon_lua/addon_lua.gml @@ -1,30 +1,847 @@ -#region - global.__lua_functions = [ - [ "print", print ], - - [ "draw_sprite", draw_sprite ], - [ "draw_sprite_ext", draw_sprite_ext ], - [ "draw_sprite_stretched", draw_sprite_stretched ], - [ "draw_sprite_stretched_ext", draw_sprite_stretched_ext ], - - [ "draw_surface", draw_surface ], - [ "draw_surface_ext", draw_surface_ext ], - [ "draw_surface_stretched", draw_surface_stretched ], - [ "draw_surface_stretched_ext", draw_surface_stretched_ext ], - - [ "draw_set_color", draw_set_color ], - [ "draw_set_alpha", draw_set_alpha ], - - [ "draw_circle", draw_circle ], - [ "draw_rectangle", draw_rectangle ], - [ "draw_line", draw_line ], - [ "draw_line_width", draw_line_width ], - ]; -#endregion - function __addon_lua_setup(lua) { for( var i = 0; i < array_length(global.__lua_functions); i++ ) { var _func = global.__lua_functions[i]; lua_add_function(lua, _func[0], _func[1]); } -} \ No newline at end of file + + lua_add_code(lua, "c_aqua = " + string(c_aqua)); + lua_add_code(lua, "c_black = " + string(c_black)); + lua_add_code(lua, "c_blue = " + string(c_blue)); + lua_add_code(lua, "c_dkgray = " + string(c_dkgray)); + lua_add_code(lua, "c_fuchsia = " + string(c_fuchsia)); + lua_add_code(lua, "c_gray = " + string(c_gray)); + lua_add_code(lua, "c_green = " + string(c_green)); + lua_add_code(lua, "c_lime = " + string(c_lime)); + lua_add_code(lua, "c_ltgray = " + string(c_ltgray)); + lua_add_code(lua, "c_maroon = " + string(c_maroon)); + lua_add_code(lua, "c_navy = " + string(c_navy)); + lua_add_code(lua, "c_olive = " + string(c_olive)); + lua_add_code(lua, "c_orange = " + string(c_orange)); + lua_add_code(lua, "c_purple = " + string(c_purple)); + lua_add_code(lua, "c_red = " + string(c_red)); + lua_add_code(lua, "c_silver = " + string(c_silver)); + lua_add_code(lua, "c_teal = " + string(c_teal)); + lua_add_code(lua, "c_white = " + string(c_white)); + lua_add_code(lua, "c_yellow = " + string(c_yellow)); + + lua_add_code(lua, "fa_left = " + string(fa_left)); + lua_add_code(lua, "fa_middle = " + string(fa_middle)); + lua_add_code(lua, "fa_right = " + string(fa_right)); + lua_add_code(lua, "fa_top = " + string(fa_top)); + lua_add_code(lua, "fa_bottom = " + string(fa_bottom)); + + lua_add_code(lua, "mb_left = " + string(mb_left)); + lua_add_code(lua, "mb_middle = " + string(mb_middle)); + lua_add_code(lua, "mb_right = " + string(mb_right)); + + lua_add_code(lua, "vk_nokey = " + string(vk_nokey)); + lua_add_code(lua, "vk_anykey = " + string(vk_anykey)); + lua_add_code(lua, "vk_left = " + string(vk_left)); + lua_add_code(lua, "vk_right = " + string(vk_right)); + lua_add_code(lua, "vk_up = " + string(vk_up)); + lua_add_code(lua, "vk_down = " + string(vk_down)); + lua_add_code(lua, "vk_enter = " + string(vk_enter)); + lua_add_code(lua, "vk_escape = " + string(vk_escape)); + lua_add_code(lua, "vk_space = " + string(vk_space)); + lua_add_code(lua, "vk_shift = " + string(vk_shift)); + lua_add_code(lua, "vk_control = " + string(vk_control)); + lua_add_code(lua, "vk_alt = " + string(vk_alt)); + lua_add_code(lua, "vk_backspace = " + string(vk_backspace)); + lua_add_code(lua, "vk_tab = " + string(vk_tab)); + lua_add_code(lua, "vk_home = " + string(vk_home)); + lua_add_code(lua, "vk_end = " + string(vk_end)); + lua_add_code(lua, "vk_delete = " + string(vk_delete)); + lua_add_code(lua, "vk_insert = " + string(vk_insert)); + lua_add_code(lua, "vk_pageup = " + string(vk_pageup)); + lua_add_code(lua, "vk_pagedown = " + string(vk_pagedown)); + lua_add_code(lua, "vk_pause = " + string(vk_pause)); + lua_add_code(lua, "vk_printscreen = " + string(vk_printscreen)); + lua_add_code(lua, "vk_f1 = " + string(vk_f1)); + lua_add_code(lua, "vk_f2 = " + string(vk_f2)); + lua_add_code(lua, "vk_f3 = " + string(vk_f3)); + lua_add_code(lua, "vk_f4 = " + string(vk_f4)); + lua_add_code(lua, "vk_f5 = " + string(vk_f5)); + lua_add_code(lua, "vk_f6 = " + string(vk_f6)); + lua_add_code(lua, "vk_f7 = " + string(vk_f7)); + lua_add_code(lua, "vk_f8 = " + string(vk_f8)); + lua_add_code(lua, "vk_f9 = " + string(vk_f9)); + lua_add_code(lua, "vk_f10 = " + string(vk_f10)); + lua_add_code(lua, "vk_f11 = " + string(vk_f11)); + lua_add_code(lua, "vk_f12 = " + string(vk_f12)); + lua_add_code(lua, "vk_numpad0 = " + string(vk_numpad0)); + lua_add_code(lua, "vk_numpad1 = " + string(vk_numpad1)); + lua_add_code(lua, "vk_numpad2 = " + string(vk_numpad2)); + lua_add_code(lua, "vk_numpad3 = " + string(vk_numpad3)); + lua_add_code(lua, "vk_numpad4 = " + string(vk_numpad4)); + lua_add_code(lua, "vk_numpad5 = " + string(vk_numpad5)); + lua_add_code(lua, "vk_numpad6 = " + string(vk_numpad6)); + lua_add_code(lua, "vk_numpad7 = " + string(vk_numpad7)); + lua_add_code(lua, "vk_numpad8 = " + string(vk_numpad8)); + lua_add_code(lua, "vk_numpad9 = " + string(vk_numpad9)); + lua_add_code(lua, "vk_multiply = " + string(vk_multiply)); + lua_add_code(lua, "vk_divide = " + string(vk_divide)); + lua_add_code(lua, "vk_add = " + string(vk_add)); + lua_add_code(lua, "vk_subtract = " + string(vk_subtract)); + lua_add_code(lua, "vk_decimal = " + string(vk_decimal)); + + lua_add_code(lua, "Panel = {};"); + lua_add_code(lua, "Animator = {};"); +} + +function __addon_lua_panel_variable(lua, panel) { + lua_add_code(lua, + "Panel.mx = " + string(panel.mx) + "\n" + + "Panel.my = " + string(panel.my) + "\n" + + "Panel.x = " + string(panel.x ) + "\n" + + "Panel.y = " + string(panel.y ) + "\n" + + "Panel.w = " + string(panel.w ) + "\n" + + "Panel.h = " + string(panel.h ) + "\n" + ); + + lua_add_code(lua, + "Animator.frame_current = " + string(ANIMATOR.current_frame) + "\n" + + "Animator.frame_total = " + string(ANIMATOR.frames_total) + "\n" + + "Animator.frame_rate = " + string(ANIMATOR.framerate) + "\n" + ); +} + +#region API + global.__lua_functions = [ + [ "print", print ], + + [ "colour_get_blue", colour_get_blue ], + [ "colour_get_green", colour_get_green ], + [ "colour_get_red", colour_get_red ], + [ "colour_get_hue", colour_get_hue ], + [ "colour_get_saturation", colour_get_saturation ], + [ "colour_get_value", colour_get_value ], + [ "draw_getpixel", draw_getpixel ], + [ "draw_getpixel_ext", draw_getpixel_ext ], + [ "draw_get_colour", draw_get_colour ], + [ "draw_get_alpha", draw_get_alpha ], + + [ "make_colour_hsv", make_colour_hsv ], + [ "make_colour_rgb", make_colour_rgb ], + [ "merge_colour", merge_colour ], + + [ "draw_clear", draw_clear ], + [ "draw_clear_alpha", draw_clear_alpha ], + [ "draw_set_alpha", draw_set_alpha ], + [ "draw_set_colour", draw_set_colour ], + + // gpu + [ "gpu_get_blendenable", gpu_get_blendenable], + [ "gpu_get_ztestenable", gpu_get_ztestenable], + [ "gpu_get_zfunc", gpu_get_zfunc], + [ "gpu_get_zwriteenable", gpu_get_zwriteenable], + [ "gpu_get_fog", gpu_get_fog], + [ "gpu_get_cullmode", gpu_get_cullmode], + [ "gpu_get_blendmode", gpu_get_blendmode], + [ "gpu_get_blendmode_ext", gpu_get_blendmode_ext], + [ "gpu_get_blendmode_ext_sepalpha", gpu_get_blendmode_ext_sepalpha], + [ "gpu_get_blendmode_src", gpu_get_blendmode_src], + [ "gpu_get_blendmode_dest", gpu_get_blendmode_dest], + [ "gpu_get_blendmode_srcalpha", gpu_get_blendmode_srcalpha], + [ "gpu_get_blendmode_destalpha", gpu_get_blendmode_destalpha], + [ "gpu_get_colourwriteenable", gpu_get_colourwriteenable], + [ "gpu_get_alphatestenable", gpu_get_alphatestenable], + [ "gpu_get_alphatestref", gpu_get_alphatestref], + [ "gpu_get_texfilter", gpu_get_texfilter], + [ "gpu_get_texfilter_ext", gpu_get_texfilter_ext], + [ "gpu_get_texrepeat", gpu_get_texrepeat], + [ "gpu_get_texrepeat_ext", gpu_get_texrepeat_ext], + + [ "gpu_set_blendenable", gpu_set_blendenable], + [ "gpu_set_ztestenable", gpu_set_ztestenable], + [ "gpu_set_zfunc", gpu_set_zfunc], + [ "gpu_set_zwriteenable", gpu_set_zwriteenable], + [ "gpu_set_fog", gpu_set_fog], + [ "gpu_set_cullmode", gpu_set_cullmode], + [ "gpu_set_blendmode", gpu_set_blendmode], + [ "gpu_set_blendmode_ext", gpu_set_blendmode_ext], + [ "gpu_set_blendmode_ext_sepalpha", gpu_set_blendmode_ext_sepalpha], + [ "gpu_set_colourwriteenable", gpu_set_colourwriteenable], + [ "gpu_set_alphatestenable", gpu_set_alphatestenable], + [ "gpu_set_alphatestref", gpu_set_alphatestref], + [ "gpu_set_texfilter", gpu_set_texfilter], + [ "gpu_set_texfilter_ext", gpu_set_texfilter_ext], + [ "gpu_set_texrepeat", gpu_set_texrepeat], + [ "gpu_set_texrepeat_ext", gpu_set_texrepeat_ext], + + [ "gpu_push_state", gpu_push_state], + [ "gpu_pop_state", gpu_pop_state], + [ "gpu_get_state", gpu_get_state], + [ "gpu_set_state", gpu_set_state], + + // basic form + [ "draw_arrow", draw_arrow ], + [ "draw_circle", draw_circle ], + [ "draw_circle_colour", draw_circle_colour ], + [ "draw_ellipse", draw_ellipse ], + [ "draw_ellipse_colour", draw_ellipse_colour ], + [ "draw_line", draw_line ], + [ "draw_line_colour", draw_line_colour ], + [ "draw_line_width", draw_line_width ], + [ "draw_line_width_colour", draw_line_width_colour ], + [ "draw_point", draw_point ], + [ "draw_point_colour", draw_point_colour ], + [ "draw_rectangle", draw_rectangle ], + [ "draw_rectangle_colour", draw_rectangle_colour ], + [ "draw_roundrect", draw_roundrect ], + [ "draw_roundrect_colour", draw_roundrect_colour ], + [ "draw_roundrect_ext", draw_roundrect_ext ], + [ "draw_roundrect_colour_ext", draw_roundrect_colour_ext ], + [ "draw_triangle", draw_triangle ], + [ "draw_triangle_colour", draw_triangle_colour ], + + [ "draw_set_circle_precision", draw_set_circle_precision ], + [ "draw_button", draw_button ], + [ "draw_healthbar", draw_healthbar ], + [ "draw_path", draw_path ], + + //sprite + [ "draw_self", draw_self ], + [ "draw_sprite", draw_sprite ], + [ "draw_sprite_ext", draw_sprite_ext ], + [ "draw_sprite_general", draw_sprite_general ], + [ "draw_sprite_part", draw_sprite_part ], + [ "draw_sprite_part_ext", draw_sprite_part_ext ], + [ "draw_sprite_stretched", draw_sprite_stretched ], + [ "draw_sprite_stretched_ext", draw_sprite_stretched_ext ], + [ "draw_sprite_pos", draw_sprite_pos ], + [ "draw_sprite_tiled", draw_sprite_tiled ], + [ "draw_sprite_tiled_ext", draw_sprite_tiled_ext ], + + // text + [ "draw_set_font", draw_set_font ], + [ "draw_set_halign", draw_set_halign ], + [ "draw_set_valign", draw_set_valign ], + [ "draw_get_font", draw_get_font ], + [ "draw_get_halign", draw_get_halign ], + [ "draw_get_valign", draw_get_valign ], + [ "draw_set_text", draw_set_text ], + + [ "draw_text", draw_text_add ], + [ "draw_text_ext", draw_text_ext_add ], + [ "draw_text_colour", draw_text_colour ], + [ "draw_text_transformed", draw_text_transformed ], + [ "draw_text_ext_colour", draw_text_ext_colour ], + [ "draw_text_ext_transformed", draw_text_ext_transformed ], + [ "draw_text_transformed_colour", draw_text_transformed_colour ], + [ "draw_text_ext_transformed_colour", draw_text_ext_transformed_colour ], + + //primitive + [ "draw_primitive_begin", draw_primitive_begin ], + [ "draw_primitive_begin_texture", draw_primitive_begin_texture ], + [ "draw_primitive_end", draw_primitive_end ], + [ "draw_vertex", draw_vertex ], + [ "draw_vertex_colour", draw_vertex_colour ], + [ "draw_vertex_texture", draw_vertex_texture ], + [ "draw_vertex_texture_colour", draw_vertex_texture_colour ], + + [ "vertex_format_begin", vertex_format_begin ], + [ "vertex_format_add_colour", vertex_format_add_colour ], + [ "vertex_format_add_position", vertex_format_add_position ], + [ "vertex_format_add_position_3d", vertex_format_add_position_3d ], + [ "vertex_format_add_texcoord", vertex_format_add_texcoord ], + [ "vertex_format_add_normal", vertex_format_add_normal ], + [ "vertex_format_add_custom", vertex_format_add_custom ], + [ "vertex_format_end", vertex_format_end ], + [ "vertex_format_delete", vertex_format_delete ], + + [ "vertex_create_buffer", vertex_create_buffer ], + [ "vertex_create_buffer_ext", vertex_create_buffer_ext ], + [ "vertex_create_buffer_from_buffer", vertex_create_buffer_from_buffer ], + [ "vertex_create_buffer_from_buffer_ext", vertex_create_buffer_from_buffer_ext ], + [ "vertex_get_buffer_size", vertex_get_buffer_size ], + [ "vertex_get_number", vertex_get_number ], + [ "vertex_delete_buffer", vertex_delete_buffer ], + [ "vertex_begin", vertex_begin ], + [ "vertex_colour", vertex_colour ], + [ "vertex_normal", vertex_normal ], + [ "vertex_position", vertex_position ], + [ "vertex_position_3d", vertex_position_3d ], + [ "vertex_argb", vertex_argb ], + [ "vertex_texcoord", vertex_texcoord ], + [ "vertex_float1", vertex_float1 ], + [ "vertex_float2", vertex_float2 ], + [ "vertex_float3", vertex_float3 ], + [ "vertex_float4", vertex_float4 ], + [ "vertex_ubyte4", vertex_ubyte4 ], + [ "vertex_end", vertex_end ], + [ "vertex_freeze", vertex_freeze ], + [ "vertex_submit", vertex_submit ], + + // surface + [ "surface_exists", surface_exists ], + [ "surface_create", surface_create ], + [ "surface_create_ext", surface_create_ext ], + [ "surface_resize", surface_resize ], + [ "surface_set_target", surface_set_target ], + [ "surface_set_target_ext", surface_set_target_ext ], + [ "surface_get_target", surface_get_target ], + [ "surface_get_target_ext", surface_get_target_ext ], + [ "surface_reset_target", surface_reset_target ], + [ "surface_copy", surface_copy ], + [ "surface_copy_part", surface_copy_part ], + [ "surface_depth_disable", surface_depth_disable ], + [ "surface_get_height", surface_get_height ], + [ "surface_get_width", surface_get_width ], + [ "surface_get_texture", surface_get_texture ], + [ "surface_get_depth_disable", surface_get_depth_disable ], + [ "surface_getpixel", surface_getpixel ], + [ "surface_getpixel_ext", surface_getpixel_ext ], + [ "surface_get_format", surface_get_format ], + [ "surface_format_is_supported", surface_format_is_supported ], + [ "surface_free", surface_free ], + [ "surface_save", surface_save ], + [ "surface_save_part", surface_save_part ], + [ "is_surface", is_surface ], + + [ "draw_surface", draw_surface ], + [ "draw_surface_ext", draw_surface_ext ], + [ "draw_surface_part", draw_surface_part ], + [ "draw_surface_part_ext", draw_surface_part_ext ], + [ "draw_surface_stretched", draw_surface_stretched ], + [ "draw_surface_stretched_ext", draw_surface_stretched_ext ], + [ "draw_surface_tiled", draw_surface_tiled ], + [ "draw_surface_tiled_ext", draw_surface_tiled_ext ], + [ "draw_surface_general", draw_surface_general ], + + [ "buffer_get_surface", buffer_get_surface ], + [ "buffer_set_surface", buffer_set_surface ], + + //variable + [ "variable_instance_exists", variable_instance_exists ], + [ "variable_instance_get_names", variable_instance_get_names ], + [ "variable_instance_names_count", variable_instance_names_count ], + [ "variable_instance_get", variable_instance_get ], + [ "variable_instance_set", variable_instance_set ], + [ "variable_global_exists", variable_global_exists ], + [ "variable_global_get", variable_global_get ], + [ "variable_global_set", variable_global_set ], + + [ "method", method ], + [ "method_get_self", method_get_self ], + [ "method_get_index", method_get_index ], + [ "method_call", method_call ], + + [ "variable_struct_exists", variable_struct_exists ], + [ "variable_struct_get", variable_struct_get ], + [ "variable_struct_set", variable_struct_set ], + [ "variable_struct_remove", variable_struct_remove ], + [ "variable_struct_get_names", variable_struct_get_names ], + [ "variable_struct_names_count", variable_struct_names_count ], + [ "is_instanceof", is_instanceof ], + [ "static_get", static_get ], + [ "static_set", static_set ], + [ "instanceof", instanceof ], + + [ "is_string", is_string ], + [ "is_real", is_real ], + [ "is_numeric", is_numeric ], + [ "is_bool", is_bool ], + [ "is_array", is_array ], + [ "is_struct", is_struct ], + [ "is_method", is_method ], + [ "is_callable", is_callable ], + [ "is_ptr", is_ptr ], + [ "is_int32", is_int32 ], + [ "is_int64", is_int64 ], + [ "is_undefined", is_undefined ], + [ "is_nan", is_nan ], + [ "is_infinity", is_infinity ], + [ "typeof", typeof ], + [ "bool", bool ], + [ "ptr", ptr ], + [ "int64", int64 ], + + //array + [ "array_create", array_create ], + [ "array_copy", array_copy ], + [ "array_equals", array_equals ], + [ "array_get", array_get ], + [ "array_set", array_set ], + [ "array_push", array_push ], + [ "array_pop", array_pop ], + [ "array_insert", array_insert ], + [ "array_delete", array_delete ], + [ "array_get_index", array_get_index ], + [ "array_contains", array_contains ], + [ "array_contains_ext", array_contains_ext ], + [ "array_sort", array_sort ], + [ "array_reverse", array_reverse ], + [ "array_shuffle", array_shuffle ], + [ "array_length", array_length ], + [ "array_resize", array_resize ], + [ "array_first", array_first ], + [ "array_last", array_last ], + + [ "array_find_index", array_find_index ], + [ "array_any", array_any ], + [ "array_all", array_all ], + [ "array_foreach", array_foreach ], + [ "array_reduce", array_reduce ], + [ "array_concat", array_concat ], + [ "array_union", array_union ], + [ "array_intersection", array_intersection ], + [ "array_filter", array_filter ], + [ "array_map", array_map ], + [ "array_unique", array_unique ], + [ "array_copy_while", array_copy_while ], + + [ "array_create_ext", array_create_ext ], + [ "array_filter_ext", array_filter_ext ], + [ "array_map_ext", array_map_ext ], + [ "array_unique_ext", array_unique_ext ], + [ "array_reverse_ext", array_reverse_ext ], + [ "array_shuffle_ext", array_shuffle_ext ], + + //window + [ "window_center", window_center ], + [ "window_handle", window_handle ], + [ "window_get_caption", window_get_caption ], + [ "window_get_colour", window_get_colour ], + [ "window_get_fullscreen", window_get_fullscreen ], + [ "window_get_width", window_get_width ], + [ "window_get_height", window_get_height ], + [ "window_get_x", window_get_x ], + [ "window_get_y", window_get_y ], + [ "window_get_cursor", window_get_cursor ], + [ "window_get_visible_rects", window_get_visible_rects ], + [ "window_mouse_get_x", window_mouse_get_x ], + [ "window_mouse_get_y", window_mouse_get_y ], + [ "window_mouse_set", window_mouse_set ], + [ "window_set_caption", window_set_caption ], + [ "window_set_colour", window_set_colour ], + [ "window_set_fullscreen", window_set_fullscreen ], + [ "window_set_position", window_set_position ], + [ "window_set_size", window_set_size ], + [ "window_set_rectangle", window_set_rectangle ], + [ "window_set_cursor", window_set_cursor ], + [ "window_set_min_width", window_set_min_width ], + [ "window_set_max_width", window_set_max_width ], + [ "window_set_min_height", window_set_min_height ], + [ "window_set_max_height", window_set_max_height ], + [ "window_has_focus", window_has_focus ], + [ "window_device", window_device ], + [ "window_view_mouse_get_x", window_view_mouse_get_x ], + [ "window_view_mouse_get_y", window_view_mouse_get_y ], + [ "window_views_mouse_get_x", window_views_mouse_get_x ], + [ "window_views_mouse_get_y", window_views_mouse_get_y ], + + //display + [ "display_reset", display_reset ], + [ "display_get_width", display_get_width ], + [ "display_get_height", display_get_height ], + [ "display_get_orientation", display_get_orientation ], + [ "display_get_dpi_x", display_get_dpi_x ], + [ "display_get_dpi_y", display_get_dpi_y ], + [ "display_get_gui_width", display_get_gui_width ], + [ "display_get_gui_height", display_get_gui_height ], + [ "display_get_timing_method", display_get_timing_method ], + [ "display_get_sleep_margin", display_get_sleep_margin ], + [ "display_get_frequency", display_get_frequency ], + [ "display_mouse_get_x", display_mouse_get_x ], + [ "display_mouse_get_y", display_mouse_get_y ], + [ "display_mouse_set", display_mouse_set ], + [ "display_set_gui_size", display_set_gui_size ], + [ "display_set_gui_maximise", display_set_gui_maximise ], + [ "display_set_ui_visibility", display_set_ui_visibility ], + [ "display_set_timing_method", display_set_timing_method ], + [ "display_set_sleep_margin", display_set_sleep_margin ], + + //keyboard + [ "io_clear", io_clear ], + [ "keyboard_check", keyboard_check ], + [ "keyboard_check_pressed", keyboard_check_pressed ], + [ "keyboard_check_released", keyboard_check_released ], + [ "keyboard_check_direct", keyboard_check_direct ], + [ "keyboard_clear", keyboard_clear ], + [ "keyboard_set_map", keyboard_set_map ], + [ "keyboard_get_map", keyboard_get_map ], + [ "keyboard_unset_map", keyboard_unset_map ], + [ "keyboard_set_numlock", keyboard_set_numlock ], + [ "keyboard_get_numlock", keyboard_get_numlock ], + + [ "keyboard_key", function() { return keyboard_key; } ], + [ "keyboard_lastkey", function() { return keyboard_lastkey; } ], + [ "keyboard_lastchar", function() { return keyboard_lastchar; } ], + [ "keyboard_string", function() { return keyboard_string; } ], + + [ "mouse_button", function() { return mouse_button; } ], + [ "mouse_x", function() { return mouse_x; } ], + [ "mouse_y", function() { return mouse_y; } ], + [ "mouse_check_button", mouse_check_button ], + [ "mouse_check_button_pressed", mouse_check_button_pressed ], + [ "mouse_check_button_released", mouse_check_button_released ], + [ "mouse_clear", mouse_clear ], + + [ "window_mouse_get_x", window_mouse_get_x ], + [ "window_mouse_get_y", window_mouse_get_y ], + [ "window_mouse_set", window_mouse_set ], + + [ "gamepad_is_supported", gamepad_is_supported ], + [ "gamepad_is_connected", gamepad_is_connected ], + [ "gamepad_get_guid", gamepad_get_guid ], + [ "gamepad_get_device_count", gamepad_get_device_count ], + [ "gamepad_get_description", gamepad_get_description ], + [ "gamepad_get_button_threshold", gamepad_get_button_threshold ], + [ "gamepad_get_axis_deadzone", gamepad_get_axis_deadzone ], + [ "gamepad_get_option", gamepad_get_option ], + [ "gamepad_set_button_threshold", gamepad_set_button_threshold ], + [ "gamepad_set_axis_deadzone", gamepad_set_axis_deadzone ], + [ "gamepad_set_vibration", gamepad_set_vibration ], + [ "gamepad_set_colour", gamepad_set_colour ], + [ "gamepad_set_option", gamepad_set_option ], + [ "gamepad_axis_count", gamepad_axis_count ], + [ "gamepad_axis_value", gamepad_axis_value ], + [ "gamepad_button_check", gamepad_button_check ], + [ "gamepad_button_check_pressed", gamepad_button_check_pressed ], + [ "gamepad_button_check_released", gamepad_button_check_released ], + [ "gamepad_button_count", gamepad_button_count ], + [ "gamepad_button_value", gamepad_button_value ], + [ "gamepad_hat_count", gamepad_hat_count ], + [ "gamepad_hat_value", gamepad_hat_value ], + + //string + [ "string", string ], + [ "string_ext", string_ext ], + [ "ansi_char", ansi_char ], + [ "chr", chr ], + [ "ord", ord ], + [ "real", real ], + [ "string_byte_at", string_byte_at ], + [ "string_byte_length", string_byte_length ], + [ "string_set_byte_at", string_set_byte_at ], + [ "string_char_at", string_char_at ], + [ "string_ord_at", string_ord_at ], + [ "string_length", string_length ], + [ "string_pos", string_pos ], + [ "string_pos_ext", string_pos_ext ], + [ "string_last_pos", string_last_pos ], + [ "string_last_pos_ext", string_last_pos_ext ], + [ "string_starts_with", string_starts_with ], + [ "string_ends_with", string_ends_with ], + [ "string_count", string_count ], + [ "string_copy", string_copy ], + [ "string_delete", string_delete ], + [ "string_digits", string_digits ], + [ "string_format", string_format ], + [ "string_insert", string_insert ], + [ "string_letters", string_letters ], + [ "string_lettersdigits", string_lettersdigits ], + [ "string_lower", string_lower ], + [ "string_repeat", string_repeat ], + [ "string_replace", string_replace ], + [ "string_replace_all", string_replace_all ], + [ "string_upper", string_upper ], + [ "string_hash_to_newline", string_hash_to_newline ], + [ "string_trim", string_trim ], + [ "string_trim_start", string_trim_start ], + [ "string_trim_end", string_trim_end ], + [ "string_split", string_split ], + [ "string_split_ext", string_split_ext ], + [ "string_join", string_join ], + [ "string_join_ext", string_join_ext ], + [ "string_concat", string_concat ], + [ "string_concat_ext", string_concat_ext ], + [ "string_width", string_width ], + [ "string_width_ext", string_width_ext ], + [ "string_height", string_height ], + [ "string_height_ext", string_height_ext ], + [ "string_foreach", string_foreach ], + + //date time + [ "date_set_timezone", date_set_timezone ], + [ "date_get_timezone", date_get_timezone ], + + [ "current_time", function() { return current_time } ], + [ "current_second", function() { return current_second } ], + [ "current_minute", function() { return current_minute } ], + [ "current_hour", function() { return current_hour } ], + [ "current_day", function() { return current_day } ], + [ "current_weekday", function() { return current_weekday } ], + [ "current_month", function() { return current_month } ], + [ "current_year", function() { return current_year } ], + + [ "date_create_datetime", date_create_datetime ], + [ "date_current_datetime", date_current_datetime ], + [ "date_compare_date", date_compare_date ], + [ "date_compare_datetime", date_compare_datetime ], + [ "date_compare_time", date_compare_time ], + [ "date_valid_datetime", date_valid_datetime ], + [ "date_date_of", date_date_of ], + [ "date_time_of", date_time_of ], + [ "date_is_today", date_is_today ], + [ "date_leap_year", date_leap_year ], + [ "date_date_string", date_date_string ], + [ "date_datetime_string", date_datetime_string ], + [ "date_time_string", date_time_string ], + [ "date_second_span", date_second_span ], + [ "date_minute_span", date_minute_span ], + [ "date_hour_span", date_hour_span ], + [ "date_day_span", date_day_span ], + [ "date_week_span", date_week_span ], + [ "date_month_span", date_month_span ], + [ "date_year_span", date_year_span ], + [ "date_days_in_month", date_days_in_month ], + [ "date_days_in_year", date_days_in_year ], + [ "date_get_second", date_get_second ], + [ "date_get_minute", date_get_minute ], + [ "date_get_hour", date_get_hour ], + [ "date_get_day", date_get_day ], + [ "date_get_weekday", date_get_weekday ], + [ "date_get_week", date_get_week ], + [ "date_get_month", date_get_month ], + [ "date_get_year", date_get_year ], + [ "date_get_second_of_year", date_get_second_of_year ], + [ "date_get_minute_of_year", date_get_minute_of_year ], + [ "date_get_hour_of_year", date_get_hour_of_year ], + [ "date_get_day_of_year", date_get_day_of_year ], + [ "date_inc_second", date_inc_second ], + [ "date_inc_minute", date_inc_minute ], + [ "date_inc_hour", date_inc_hour ], + [ "date_inc_day", date_inc_day ], + [ "date_inc_week", date_inc_week ], + [ "date_inc_month", date_inc_month ], + [ "date_inc_year", date_inc_year ], + + [ "get_timer", get_timer ], + [ "delta_time", function() { return delta_time } ], + + //number + [ "choose", choose ], + [ "random", random ], + [ "random_range", random_range ], + [ "irandom", irandom ], + [ "irandom_range", irandom_range ], + [ "random_set_seed", random_set_seed ], + [ "random_get_seed", random_get_seed ], + [ "randomise", randomise ], + + [ "round", round ], + [ "floor", floor ], + [ "frac", frac ], + [ "abs", abs ], + [ "sign", sign ], + [ "ceil", ceil ], + [ "max", max ], + [ "mean", mean ], + [ "median", median ], + [ "min", min ], + [ "lerp", lerp ], + [ "clamp", clamp ], + + [ "exp", exp ], + [ "ln", ln ], + [ "power", power ], + [ "sqr", sqr ], + [ "sqrt", sqrt ], + [ "log2", log2 ], + [ "log10", log10 ], + [ "logn", logn ], + + [ "arccos", arccos ], + [ "arcsin", arcsin ], + [ "arctan", arctan ], + [ "arctan2", arctan2 ], + [ "cos", cos ], + [ "sin", sin ], + [ "tan", tan ], + [ "dcos", dcos ], + [ "dsin", dsin ], + [ "dtan", dtan ], + [ "darccos", darccos ], + [ "darcsin", darcsin ], + [ "darctan", darctan ], + [ "darctan2", darctan2 ], + [ "degtorad", degtorad ], + [ "radtodeg", radtodeg ], + + [ "point_direction", point_direction ], + [ "point_distance", point_distance ], + [ "point_distance_3d", point_distance_3d ], + [ "distance_to_object", distance_to_object ], + [ "distance_to_point", distance_to_point ], + [ "dot_product", dot_product ], + [ "dot_product_3d", dot_product_3d ], + [ "dot_product_normalised", dot_product_normalised ], + [ "dot_product_3d_normalised", dot_product_3d_normalised ], + [ "angle_difference", angle_difference ], + [ "lengthdir_x", lengthdir_x ], + [ "lengthdir_y", lengthdir_y ], + + [ "matrix_get", matrix_get ], + [ "matrix_set", matrix_set ], + [ "matrix_build", matrix_build ], + [ "matrix_multiply", matrix_multiply ], + [ "matrix_build_identity", matrix_build_identity ], + [ "matrix_build_lookat", matrix_build_lookat ], + [ "matrix_build_projection_ortho", matrix_build_projection_ortho ], + [ "matrix_build_projection_perspective", matrix_build_projection_perspective ], + [ "matrix_build_projection_perspective_fov", matrix_build_projection_perspective_fov ], + [ "matrix_transform_vertex", matrix_transform_vertex ], + + [ "matrix_stack_is_empty", matrix_stack_is_empty ], + [ "matrix_stack_clear", matrix_stack_clear ], + [ "matrix_stack_set", matrix_stack_set ], + [ "matrix_stack_push", matrix_stack_push ], + [ "matrix_stack_pop", matrix_stack_pop ], + [ "matrix_stack_top", matrix_stack_top ], + + //file + [ "file_exists", file_exists ], + [ "file_delete", file_delete ], + [ "file_rename", file_rename ], + [ "file_copy", file_copy ], + [ "file_find_first", file_find_first ], + [ "file_find_next", file_find_next ], + [ "file_find_close", file_find_close ], + [ "file_attributes", file_attributes ], + + [ "filename_name", filename_name ], + [ "filename_path", filename_path ], + [ "filename_dir", filename_dir ], + [ "filename_drive", filename_drive ], + [ "filename_ext", filename_ext ], + [ "filename_change_ext", filename_change_ext ], + + [ "get_open_filename", get_open_filename ], + [ "get_open_filename_ext", get_open_filename_ext ], + [ "get_save_filename", get_save_filename ], + [ "get_save_filename_ext", get_save_filename_ext ], + + [ "ini_open", ini_open ], + [ "ini_close", ini_close ], + [ "ini_write_real", ini_write_real ], + [ "ini_write_string", ini_write_string ], + [ "ini_read_real", ini_read_real ], + [ "ini_read_string", ini_read_string ], + [ "ini_key_exists", ini_key_exists ], + [ "ini_section_exists", ini_section_exists ], + [ "ini_key_delete", ini_key_delete ], + [ "ini_section_delete", ini_section_delete ], + [ "ini_open_from_string", ini_open_from_string ], + + [ "file_text_open_read", file_text_open_read ], + [ "file_text_open_write", file_text_open_write ], + [ "file_text_open_append", file_text_open_append ], + [ "file_text_open_from_string", file_text_open_from_string ], + [ "file_text_read_real", file_text_read_real ], + [ "file_text_read_string", file_text_read_string ], + [ "file_text_readln", file_text_readln ], + [ "file_text_write_real", file_text_write_real ], + [ "file_text_write_string", file_text_write_string ], + [ "file_text_writeln", file_text_writeln ], + [ "file_text_eoln", file_text_eoln ], + [ "file_text_eof", file_text_eof ], + [ "file_text_close", file_text_close ], + + [ "file_bin_open", file_bin_open ], + [ "file_bin_rewrite", file_bin_rewrite ], + [ "file_bin_close", file_bin_close ], + [ "file_bin_size", file_bin_size ], + [ "file_bin_position", file_bin_position ], + [ "file_bin_seek", file_bin_seek ], + [ "file_bin_write_byte", file_bin_write_byte ], + [ "file_bin_read_byte", file_bin_read_byte ], + + [ "directory_exists", directory_exists ], + [ "directory_create", directory_create ], + [ "directory_destroy", directory_destroy ], + [ "temp_directory", function() { return temp_directory } ], + [ "working_directory", function() { return working_directory } ], + [ "program_directory", function() { return program_directory } ], + + [ "json_encode", json_encode ], + [ "json_decode", json_decode ], + [ "json_stringify", json_stringify ], + [ "json_parse", json_parse ], + + //buffer + [ "buffer_exists", buffer_exists ], + [ "buffer_create", buffer_create ], + [ "buffer_create_from_vertex_buffer", buffer_create_from_vertex_buffer ], + [ "buffer_create_from_vertex_buffer_ext", buffer_create_from_vertex_buffer_ext ], + [ "buffer_delete", buffer_delete ], + [ "buffer_read", buffer_read ], + [ "buffer_write", buffer_write ], + [ "buffer_fill", buffer_fill ], + [ "buffer_seek", buffer_seek ], + [ "buffer_tell", buffer_tell ], + [ "buffer_peek", buffer_peek ], + [ "buffer_poke", buffer_poke ], + [ "buffer_save", buffer_save ], + [ "buffer_save_ext", buffer_save_ext ], + [ "buffer_save_async", buffer_save_async ], + [ "buffer_load", buffer_load ], + [ "buffer_load_ext", buffer_load_ext ], + [ "buffer_load_async", buffer_load_async ], + [ "buffer_load_partial", buffer_load_partial ], + [ "buffer_compress", buffer_compress ], + [ "buffer_decompress", buffer_decompress ], + [ "buffer_async_group_begin", buffer_async_group_begin ], + [ "buffer_async_group_option", buffer_async_group_option ], + [ "buffer_async_group_end", buffer_async_group_end ], + [ "buffer_copy", buffer_copy ], + [ "buffer_copy_from_vertex_buffer", buffer_copy_from_vertex_buffer ], + [ "buffer_get_type", buffer_get_type ], + [ "buffer_get_alignment", buffer_get_alignment ], + [ "buffer_get_address", buffer_get_address ], + [ "buffer_get_size", buffer_get_size ], + [ "buffer_get_surface", buffer_get_surface ], + [ "buffer_set_surface", buffer_set_surface ], + [ "buffer_resize", buffer_resize ], + [ "buffer_sizeof", buffer_sizeof ], + [ "buffer_md5", buffer_md5 ], + [ "buffer_sha1", buffer_sha1 ], + [ "buffer_crc32", buffer_crc32 ], + [ "buffer_base64_encode", buffer_base64_encode ], + [ "buffer_base64_decode", buffer_base64_decode ], + [ "buffer_base64_decode_ext", buffer_base64_decode_ext ], + [ "buffer_set_used_size", buffer_set_used_size ], + + //os + [ "os_browser", function() { return os_browser } ], + [ "os_device", function() { return os_device } ], + [ "os_type", function() { return os_type } ], + [ "os_version", function() { return os_version } ], + [ "os_is_paused", os_is_paused ], + [ "os_is_network_connected", os_is_network_connected ], + [ "os_get_config", os_get_config ], + [ "os_get_language", os_get_language ], + [ "os_get_region", os_get_region ], + [ "os_get_info", os_get_info ], + [ "os_powersave_enable", os_powersave_enable ], + [ "os_lock_orientation", os_lock_orientation ], + [ "os_check_permission", os_check_permission ], + [ "os_request_permission", os_request_permission ], + + //debug + [ "print", print ], + [ "noti_error", noti_error ], + [ "noti_warning", noti_warning ], + + //panel + [ "panel_get", function(type) { + switch(type) { + case "animation" : return PANEL_ANIMATION; + case "collection" : return PANEL_COLLECTION; + case "graph" : return PANEL_GRAPH; + case "inspector" : return PANEL_INSPECTOR; + case "main" : return PANEL_MAIN; + } + } ], + + [ "draw_text_set_format", function(ind) { + switch(ind) { + case 0 : draw_set_font(f_h3); draw_set_color(COLORS._main_text); break; + case 1 : draw_set_font(f_p0); draw_set_color(COLORS._main_text); break; + case 2 : draw_set_font(f_p1); draw_set_color(COLORS._main_text_sub); break; + case 3 : draw_set_font(f_p2); draw_set_color(COLORS._main_text_sub); break; + case 4 : draw_set_font(f_p3); draw_set_color(COLORS._main_text_sub); break; + } + }], + ]; +#endregion \ No newline at end of file diff --git a/scripts/bbmod_cmp/bbmod_cmp.gml b/scripts/bbmod_cmp/bbmod_cmp.gml new file mode 100644 index 000000000..051713ebd --- /dev/null +++ b/scripts/bbmod_cmp/bbmod_cmp.gml @@ -0,0 +1,15 @@ +/// @func bbmod_cmp(_a, _b) +/// +/// @desc Checks whether two number are equal, taking into account +/// `math_get_epsilon`. +/// +/// @param {Real} _a The first number. +/// @param {Real} _b The second number. +/// +/// @return {Bool} Returns `true` if the two numbers are equal. +// Source: https://github.com/gszauer/GamePhysicsCookbook/blob/a0b8ee0c39fed6d4b90bb6d2195004dfcf5a1115/Code/Geometry3D.cpp#L6 +function bbmod_cmp(_a, _b) +{ + gml_pragma("forceinline"); + return (abs(_a - _b) <= math_get_epsilon() * max(1.0, abs(_a), abs(_b))); +} diff --git a/scripts/bbmod_cmp/bbmod_cmp.yy b/scripts/bbmod_cmp/bbmod_cmp.yy new file mode 100644 index 000000000..953a96ebd --- /dev/null +++ b/scripts/bbmod_cmp/bbmod_cmp.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "bbmod_cmp", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Raycasting", + "path": "folders/_Extensions/BBMOD/Raycasting.yy", + }, +} \ No newline at end of file diff --git a/scripts/bbmod_get_calling_function_name/bbmod_get_calling_function_name.gml b/scripts/bbmod_get_calling_function_name/bbmod_get_calling_function_name.gml new file mode 100644 index 000000000..8d894d363 --- /dev/null +++ b/scripts/bbmod_get_calling_function_name/bbmod_get_calling_function_name.gml @@ -0,0 +1,17 @@ +/// @func bbmod_get_calling_function_name() +/// +/// @desc Retrieves name of the calling function. +/// +/// @return {String} The name of the calling function. +function bbmod_get_calling_function_name() +{ + gml_pragma("forceinline"); + var _name = debug_get_callstack(/*2*/)[1]; // TODO: Check if this argument works in YYC already + _name = string_replace(_name, "gml_Script_", ""); + var _pos = string_pos(":", _name); + if (_pos > 0) + { + _name = string_copy(_name, 1, _pos - 1); + } + return _name; +} diff --git a/scripts/bbmod_get_calling_function_name/bbmod_get_calling_function_name.yy b/scripts/bbmod_get_calling_function_name/bbmod_get_calling_function_name.yy new file mode 100644 index 000000000..dc9691be4 --- /dev/null +++ b/scripts/bbmod_get_calling_function_name/bbmod_get_calling_function_name.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "bbmod_get_calling_function_name", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Utils", + "path": "folders/_Extensions/BBMOD/Core/Utils.yy", + }, +} \ No newline at end of file diff --git a/scripts/bbmod_gpu_get_default_state/bbmod_gpu_get_default_state.gml b/scripts/bbmod_gpu_get_default_state/bbmod_gpu_get_default_state.gml new file mode 100644 index 000000000..405bb2d33 --- /dev/null +++ b/scripts/bbmod_gpu_get_default_state/bbmod_gpu_get_default_state.gml @@ -0,0 +1,12 @@ +/// @func bbmod_gpu_get_default_state() +/// +/// @desc Retrieves the default GPU state. +/// +/// @return {Id.DsMap} The default GPU state. +function bbmod_gpu_get_default_state() +{ + static _state = gpu_get_state(); + return _state; +} + +bbmod_gpu_get_default_state(); diff --git a/scripts/bbmod_gpu_get_default_state/bbmod_gpu_get_default_state.yy b/scripts/bbmod_gpu_get_default_state/bbmod_gpu_get_default_state.yy new file mode 100644 index 000000000..f6d9adb27 --- /dev/null +++ b/scripts/bbmod_gpu_get_default_state/bbmod_gpu_get_default_state.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "bbmod_gpu_get_default_state", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/bbmod_json_load/bbmod_json_load.gml b/scripts/bbmod_json_load/bbmod_json_load.gml new file mode 100644 index 000000000..40206c1b7 --- /dev/null +++ b/scripts/bbmod_json_load/bbmod_json_load.gml @@ -0,0 +1,19 @@ +/// @func bbmod_json_load(_file) +/// +/// @desc Loads a JSON file. +/// +/// @param {String} _file The path to the file. +/// +/// @return {Struct, Array} The loaded JSON. +function bbmod_json_load(_file) +{ + var _jsonFile = file_text_open_read(_file); + var _jsonString = ""; + while (!file_text_eof(_jsonFile)) + { + _jsonString += file_text_read_string(_jsonFile) + "\n"; + file_text_readln(_jsonFile); + } + file_text_close(_jsonFile); + return json_parse(_jsonString); +} diff --git a/scripts/bbmod_json_load/bbmod_json_load.yy b/scripts/bbmod_json_load/bbmod_json_load.yy new file mode 100644 index 000000000..4fafb72d1 --- /dev/null +++ b/scripts/bbmod_json_load/bbmod_json_load.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "bbmod_json_load", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Utils", + "path": "folders/_Extensions/BBMOD/Core/Utils.yy", + }, +} \ No newline at end of file diff --git a/scripts/bbmod_lerp_delta_time/bbmod_lerp_delta_time.gml b/scripts/bbmod_lerp_delta_time/bbmod_lerp_delta_time.gml new file mode 100644 index 000000000..545d35d3f --- /dev/null +++ b/scripts/bbmod_lerp_delta_time/bbmod_lerp_delta_time.gml @@ -0,0 +1,18 @@ +/// @func bbmod_lerp_delta_time(_from, _to, _factor, _deltaTime) +/// +/// @desc Linearly interpolates two values, taking delta time into account. +/// +/// @param {Real} _from The value to interpolate from. +/// @param {Real} _to The value to interpolate to. +/// @param {Real} _factor The interpolation factor. +/// @param {Real} _deltaTime The `delta_time`. +/// +/// @return {Real} The resulting value. +function bbmod_lerp_delta_time(_from, _to, _factor, _deltaTime) +{ + gml_pragma("forceinline"); + return lerp( + _from, + _to, + _factor * (_deltaTime / game_get_speed(gamespeed_microseconds))); +} diff --git a/scripts/bbmod_lerp_delta_time/bbmod_lerp_delta_time.yy b/scripts/bbmod_lerp_delta_time/bbmod_lerp_delta_time.yy new file mode 100644 index 000000000..c720ea120 --- /dev/null +++ b/scripts/bbmod_lerp_delta_time/bbmod_lerp_delta_time.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "bbmod_lerp_delta_time", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Math", + "path": "folders/_Extensions/BBMOD/Core/Math.yy", + }, +} \ No newline at end of file diff --git a/scripts/bbmod_set_instance_id/bbmod_set_instance_id.gml b/scripts/bbmod_set_instance_id/bbmod_set_instance_id.gml new file mode 100644 index 000000000..308db9e0a --- /dev/null +++ b/scripts/bbmod_set_instance_id/bbmod_set_instance_id.gml @@ -0,0 +1,18 @@ +/// @var {Id.Instance} +/// @private +global.__bbmodInstanceID = 0; + +/// @var {Array,Undefined} +/// @private +global.__bbmodInstanceIDBatch = undefined; + +/// @func bbmod_set_instance_id(_id) +/// +/// @desc Sets an instance id for all subsequently rendered models. +/// +/// @param {Id.Instance} _id The id of the instance. +function bbmod_set_instance_id(_id) +{ + gml_pragma("forceinline"); + global.__bbmodInstanceID = _id; +} diff --git a/scripts/bbmod_set_instance_id/bbmod_set_instance_id.yy b/scripts/bbmod_set_instance_id/bbmod_set_instance_id.yy new file mode 100644 index 000000000..bb92880ee --- /dev/null +++ b/scripts/bbmod_set_instance_id/bbmod_set_instance_id.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "bbmod_set_instance_id", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Rendering", + "path": "folders/_Extensions/BBMOD/Core/Rendering.yy", + }, +} \ No newline at end of file diff --git a/scripts/bbmod_surface_check/bbmod_surface_check.gml b/scripts/bbmod_surface_check/bbmod_surface_check.gml new file mode 100644 index 000000000..801b5ec56 --- /dev/null +++ b/scripts/bbmod_surface_check/bbmod_surface_check.gml @@ -0,0 +1,28 @@ +/// @func bbmod_surface_check(_surface, _width, _height) +/// +/// @desc Checks whether the surface exists and if it has correct size. Broken +/// surfaces are recreated. Surfaces of wrong size are resized. +/// +/// @param {Id.Surface} _surface The surface to check. +/// @param {Real} _width The desired width of the surface. +/// @param {Real} _height The desired height of the surface. +/// +/// @return {Id.Surface} The surface. +function bbmod_surface_check(_surface, _width, _height) +{ + _width = max(round(_width), 1); + _height = max(round(_height), 1); + + if (!surface_exists(_surface)) + { + return surface_create(_width, _height); + } + + if (surface_get_width(_surface) != _width + || surface_get_height(_surface) != _height) + { + surface_resize(_surface, _width, _height); + } + + return _surface; +} diff --git a/scripts/bbmod_surface_check/bbmod_surface_check.yy b/scripts/bbmod_surface_check/bbmod_surface_check.yy new file mode 100644 index 000000000..88e5d9123 --- /dev/null +++ b/scripts/bbmod_surface_check/bbmod_surface_check.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "bbmod_surface_check", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Utils", + "path": "folders/_Extensions/BBMOD/Core/Utils.yy", + }, +} \ No newline at end of file diff --git a/scripts/bbmod_vtf_is_supported/bbmod_vtf_is_supported.gml b/scripts/bbmod_vtf_is_supported/bbmod_vtf_is_supported.gml new file mode 100644 index 000000000..bacb3f86a --- /dev/null +++ b/scripts/bbmod_vtf_is_supported/bbmod_vtf_is_supported.gml @@ -0,0 +1,43 @@ +/// @func bbmod_vtf_is_supported() +/// +/// @desc Checks whether vertex texture fetching is supported on the current +/// platform. +/// +/// @return {Bool} Returns `true` if vertex texture fetching is supported on +/// the current platform. +function bbmod_vtf_is_supported() +{ + var _isSupported = undefined; + + if (_isSupported == undefined) + { + var _shader = __BBMOD_ShCheckVTF; + + if (shader_is_compiled(_shader)) + { + var _surface = surface_create(1, 1); + surface_set_target(_surface); + draw_clear(c_black); + shader_set(_shader); + bbmod_texture_set_stage_vs( + shader_get_sampler_index(_shader, "u_texTest"), + sprite_get_texture(BBMOD_SprWhite, 0)); + draw_sprite(BBMOD_SprWhite, 0, 0, 0); + shader_reset(); + surface_reset_target(); + + var _pixel = surface_getpixel_ext(_surface, 0, 0); + _isSupported = (_pixel == $FFFFFFFF); + + surface_free(_surface); + } + else + { + _isSupported = false; + } + } + + return _isSupported; +} + +__bbmod_info("Vertex texture fetching " + (bbmod_vtf_is_supported() ? "IS" : "NOT") + " supported!"); diff --git a/scripts/bbmod_vtf_is_supported/bbmod_vtf_is_supported.yy b/scripts/bbmod_vtf_is_supported/bbmod_vtf_is_supported.yy new file mode 100644 index 000000000..fbe1bb834 --- /dev/null +++ b/scripts/bbmod_vtf_is_supported/bbmod_vtf_is_supported.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "bbmod_vtf_is_supported", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "Utils", + "path": "folders/_Extensions/BBMOD/Core/Utils.yy", + }, +} \ No newline at end of file diff --git a/scripts/globals/globals.gml b/scripts/globals/globals.gml index 98a410cbc..642c92424 100644 --- a/scripts/globals/globals.gml +++ b/scripts/globals/globals.gml @@ -33,11 +33,11 @@ globalvar VERSION, SAVEFILE_VERSION, VERSION_STRING; globalvar COLLECTION_VERSION, THEME_VERSION; - VERSION = 1142; + VERSION = 1143; SAVEFILE_VERSION = 1420; COLLECTION_VERSION = 1140.090; THEME_VERSION = 1140.090; - VERSION_STRING = "1.14.2"; + VERSION_STRING = "1.14.3"; globalvar NODES, NODE_MAP, APPEND_MAP, NODE_NAME_MAP; globalvar HOTKEYS, HOTKEY_CONTEXT, NODE_INSTANCES; diff --git a/scripts/node_array_sort/node_array_sort.gml b/scripts/node_array_sort/node_array_sort.gml index 814f135a0..ab841dbeb 100644 --- a/scripts/node_array_sort/node_array_sort.gml +++ b/scripts/node_array_sort/node_array_sort.gml @@ -13,6 +13,11 @@ function Node_Array_Sort(_x, _y, _group = noone) : Node(_x, _y, _group) construc outputs[| 0] = nodeValue("Sorted array", self, JUNCTION_CONNECT.output, VALUE_TYPE.any, []); + outputs[| 1] = nodeValue("Sorted index", self, JUNCTION_CONNECT.output, VALUE_TYPE.integer, []); + + static sortAcs = function(v1, v2) { return v1[1] < v2[1]; } + static sortDes = function(v1, v2) { return v1[1] > v2[1]; } + static update = function(frame = ANIMATOR.current_frame) { var arr = inputs[| 0].getValue(); var asc = inputs[| 1].getValue(); @@ -27,10 +32,20 @@ function Node_Array_Sort(_x, _y, _group = noone) : Node(_x, _y, _group) construc outputs[| 0].type = inputs[| 0].value_from.type; } - var _arr = array_clone(arr); - array_sort(_arr, bool(!asc)); + var _arr = []; + for( var i = 0; i < array_length(arr); i++ ) + _arr[i] = [ i, arr[i] ]; - outputs[| 0].setValue(_arr); + array_sort(_arr, asc? sortAcs : sortDes); + + var res = [ [], [] ]; + for( var i = 0; i < array_length(_arr); i++ ) { + res[0][i] = _arr[i][0]; + res[1][i] = _arr[i][1]; + } + + outputs[| 0].setValue(res[1]); + outputs[| 1].setValue(res[0]); } static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) { diff --git a/scripts/node_iterate_sort/node_iterate_sort.gml b/scripts/node_iterate_sort/node_iterate_sort.gml new file mode 100644 index 000000000..483b96f5f --- /dev/null +++ b/scripts/node_iterate_sort/node_iterate_sort.gml @@ -0,0 +1,132 @@ +function Node_Iterate_Sort(_x, _y, _group = noone) : Node_Collection(_x, _y, _group) constructor { + name = "Sort Array"; + color = COLORS.node_blend_loop; + icon = THEME.loop; + + combine_render_time = false; + + inputs[| 0] = nodeValue("Array", self, JUNCTION_CONNECT.input, VALUE_TYPE.any, [] ) + .setVisible(true, true); + + outputs[| 0] = nodeValue("Array", self, JUNCTION_CONNECT.output, VALUE_TYPE.any, noone ); + + custom_input_index = ds_list_size(inputs); + custom_output_index = ds_list_size(inputs); + loop_start_time = 0; + ALWAYS_FULL = true; + + inputNodes = []; + outputNode = noone; + nodeValid = false; + + if(!LOADING && !APPENDING && !CLONING) { + var input0 = nodeBuild("Node_Iterator_Sort_Input", -256, -64, self); + input0.display_name = "Value 1"; + + var input1 = nodeBuild("Node_Iterator_Sort_Input", -256, 64, self); + input1.display_name = "Value 2"; + + var output = nodeBuild("Node_Iterator_Sort_Output", 256, -32, self); + } + + static getNextNodes = function() { + initLoop(); + return __nodeLeafList(getNodeList()); + } + + static onStep = function() { + var type = inputs[| 0].value_from == noone? VALUE_TYPE.any : inputs[| 0].value_from.type; + inputs[| 0].type = type; + } + + static swap = function(arr, a, b) { + var temp = arr[a]; + arr[@ a] = arr[b]; + arr[@ b] = temp; + } + + static partition = function(arr, low, high) { + var pv = arr[high]; + var i = low - 1; + + for(var j = low; j < high; j++) { + if(compareValue(arr[j], pv)) continue; + + i++; + swap(arr, i, j); + } + + swap(arr, i + 1, high); + return i + 1; + } + + static quickSort = function(arr, low, high) { + if(!nodeValid) return; + if(low >= high) return; + + var p = partition(arr, low, high); + + quickSort(arr, low, p - 1); + quickSort(arr, p + 1, high); + } + + static compareValue = function(val1, val2) { + if(!nodeValid) return 0; + inputNodes[0].setValue(val1); + inputNodes[1].setValue(val2); + + RenderList(nodes); + var res = outputNode.getValue(); + //print("Comparing " + string(val1) + ", " + string(val2) + ": " + string(res)); + return res; + } + + static initLoop = function() { + if(inputs[| 0].value_from) { + inputs[| 0].type = inputs[| 0].value_from.type; + outputs[| 0].type = inputs[| 0].value_from.type; + } + + resetRender(); + iterated = 0; + loop_start_time = get_timer(); + + inputNodes = [ 0, 0 ]; + outputNode = noone; + var inputReady = 0; + for( var i = 0; i < ds_list_size(nodes); i++ ) { + if(nodes[| i].display_name == "Value 1") { + inputNodes[0] = nodes[| i].inputs[| 0]; + inputNodes[0].type = inputs[| 0].type; + inputReady++; + } else if(nodes[| i].display_name == "Value 2") { + inputNodes[1] = nodes[| i].inputs[| 0]; + inputNodes[1].type = inputs[| 0].type; + inputReady++; + } else if(nodes[| i].name == "Swap result") { + outputNode = nodes[| i].inputs[| 0]; + inputReady++; + } + } + + nodeValid = inputReady == 3; + if(!nodeValid) { + noti_warning("Sort: Missing inputs or output, need 2 inputs and 1 output for comparison."); + return; + } + + var arrIn = inputs[| 0].getValue(); + var arrOut = outputs[| 0].getValue(); + + if(inputs[| 0].type == VALUE_TYPE.surface) { + surface_array_free(arrOut); + arrOut = surface_array_clone(arrIn); + } else + arrOut = array_clone(arrIn); + + quickSort(arrOut, 0, array_length(arrOut) - 1); + outputs[| 0].setValue(arrOut); + } + + PATCH_STATIC +} \ No newline at end of file diff --git a/scripts/node_iterate_sort/node_iterate_sort.yy b/scripts/node_iterate_sort/node_iterate_sort.yy new file mode 100644 index 000000000..eae8d461a --- /dev/null +++ b/scripts/node_iterate_sort/node_iterate_sort.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "node_iterate_sort", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "for sort", + "path": "folders/nodes/data/iterate/for sort.yy", + }, +} \ No newline at end of file diff --git a/scripts/node_iterator_sort_input/node_iterator_sort_input.gml b/scripts/node_iterator_sort_input/node_iterator_sort_input.gml new file mode 100644 index 000000000..bb45a3a2f --- /dev/null +++ b/scripts/node_iterator_sort_input/node_iterator_sort_input.gml @@ -0,0 +1,19 @@ +function Node_Iterator_Sort_Input(_x, _y, _group = noone) : Node(_x, _y, _group) constructor { + name = "Sort Input"; + color = COLORS.node_blend_loop; + + manual_deletable = false; + + inputs[| 0] = nodeValue("Value in", self, JUNCTION_CONNECT.input, VALUE_TYPE.any, 0 ) + .setVisible(false, false); + + outputs[| 0] = nodeValue("Value in", self, JUNCTION_CONNECT.output, VALUE_TYPE.any, 0 ); + + static update = function() { + outputs[| 0].type = inputs[| 0].type; + + var val = inputs[| 0].getValue(); + outputs[| 0].setValue(val); + } + +} \ No newline at end of file diff --git a/scripts/node_iterator_sort_input/node_iterator_sort_input.yy b/scripts/node_iterator_sort_input/node_iterator_sort_input.yy new file mode 100644 index 000000000..4dc35c3ec --- /dev/null +++ b/scripts/node_iterator_sort_input/node_iterator_sort_input.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "node_iterator_sort_input", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "for sort", + "path": "folders/nodes/data/iterate/for sort.yy", + }, +} \ No newline at end of file diff --git a/scripts/node_iterator_sort_output/node_group_input.yy b/scripts/node_iterator_sort_output/node_group_input.yy new file mode 100644 index 000000000..b48fa62c4 --- /dev/null +++ b/scripts/node_iterator_sort_output/node_group_input.yy @@ -0,0 +1,12 @@ +{ + "isDnD": false, + "isCompatibility": false, + "parent": { + "name": "group", + "path": "folders/nodes/data/group.yy", + }, + "resourceVersion": "1.0", + "name": "node_group_input", + "tags": [], + "resourceType": "GMScript", +} \ No newline at end of file diff --git a/scripts/node_iterator_sort_output/node_iterator_sort_output.gml b/scripts/node_iterator_sort_output/node_iterator_sort_output.gml new file mode 100644 index 000000000..8df2350fd --- /dev/null +++ b/scripts/node_iterator_sort_output/node_iterator_sort_output.gml @@ -0,0 +1,19 @@ +function Node_Iterator_Sort_Output(_x, _y, _group = noone) : Node(_x, _y, _group) constructor { + name = "Swap result"; + color = COLORS.node_blend_loop; + + manual_deletable = false; + + inputs[| 0] = nodeValue("Result", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false ) + .setVisible(true, true); + + static getNextNodes = function() { + return []; + } + + static step = function() {} + + static update = function(frame = ANIMATOR.current_frame) { + //print(display_name + ": " + string(inputs[| 0].getValue())); + } +} \ No newline at end of file diff --git a/scripts/node_iterator_sort_output/node_iterator_sort_output.yy b/scripts/node_iterator_sort_output/node_iterator_sort_output.yy new file mode 100644 index 000000000..c7de91ddc --- /dev/null +++ b/scripts/node_iterator_sort_output/node_iterator_sort_output.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "node_iterator_sort_output", + "isCompatibility": false, + "isDnD": false, + "parent": { + "name": "for sort", + "path": "folders/nodes/data/iterate/for sort.yy", + }, +} \ No newline at end of file diff --git a/scripts/node_registry/node_registry.gml b/scripts/node_registry/node_registry.gml index f17ae0af1..6f4efe990 100644 --- a/scripts/node_registry/node_registry.gml +++ b/scripts/node_registry/node_registry.gml @@ -457,10 +457,11 @@ function NodeObject(_name, _spr, _node, _create, tags = []) constructor { addNodeObject(values, "Array Reverse", s_node_array_reverse, "Node_Array_Reverse", [1, Node_Array_Reverse], ["reverse array"]).setVersion(1120); addNodeObject(values, "Array Shift", s_node_array_shift, "Node_Array_Shift", [1, Node_Array_Shift]).setVersion(1137); addNodeObject(values, "Array Zip", s_node_array_zip, "Node_Array_Zip", [1, Node_Array_Zip]).setVersion(1138); - addNodeObject(values, "Sort Array", s_node_array_sort, "Node_Array_Sort", [1, Node_Array_Sort], ["array sort"]).setVersion(1120); + addNodeObject(values, "Sort Number", s_node_array_sort, "Node_Array_Sort", [1, Node_Array_Sort], ["array sort"]).setVersion(1120); addNodeObject(values, "Shuffle Array", s_node_array_shuffle, "Node_Array_Shuffle", [1, Node_Array_Shuffle], ["array shuffle"]).setVersion(1120); addNodeObject(values, "Loop Array", s_node_loop_array, "Node_Iterate_Each", [1, Node_Iterate_Each], ["iterate each", "for each", "array loop"], "Create group that iterate to each member in an array."); addNodeObject(values, "Filter Array", s_node_filter_array, "Node_Iterate_Filter", [1, Node_Iterate_Filter],, "Filter array using condition.").setVersion(1140); + addNodeObject(values, "Sort Array", s_node_sort_array, "Node_Iterate_Sort", [1, Node_Iterate_Sort],, "Sort array using node graph.").setVersion(1143); ds_list_add(values, "Paths"); addNodeObject(values, "Path", s_node_path, "Node_Path", [1, Node_Path]); @@ -570,6 +571,8 @@ function NodeObject(_name, _spr, _node, _create, tags = []) constructor { addNodeObject(hid, "Grid Noise", s_node_grid_noise, "Node_Grid_Noise", [1, Node_Grid_Noise]); addNodeObject(hid, "Triangular Noise", s_node_grid_tri_noise, "Node_Noise_Tri", [1, Node_Noise_Tri]).setVersion(1090); addNodeObject(hid, "Hexagonal Noise", s_node_grid_hex_noise, "Node_Noise_Hex", [1, Node_Noise_Hex]).setVersion(1090); + addNodeObject(hid, "Sort Input", s_node_grid_hex_noise, "Node_Iterator_Sort_Input", [1, Node_Iterator_Sort_Input]); + addNodeObject(hid, "Sort Output", s_node_grid_hex_noise, "Node_Iterator_Sort_Output", [1, Node_Iterator_Sort_Output]); } #endregion diff --git a/scripts/node_value/node_value.gml b/scripts/node_value/node_value.gml index 9a7759b24..224bff32f 100644 --- a/scripts/node_value/node_value.gml +++ b/scripts/node_value/node_value.gml @@ -343,7 +343,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru else if(_connect == JUNCTION_CONNECT.output) node.outputMap[? string_replace_all(name, " ", "_")] = self; } - tooltip = _tooltip; + tooltip = _tooltip; editWidget = noone; connect_type = _connect; @@ -638,6 +638,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru for( var i = 0; i < array_length(animators); i++ ) animators[i].suffix = " " + array_safe_get(global.displaySuffix_Area, i, ""); + extra_data[| 0] = AREA_MODE.area; extract_node = "Node_Area"; break; case VALUE_DISPLAY.padding : @@ -652,7 +653,6 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru for( var i = 0; i < array_length(animators); i++ ) animators[i].suffix = " " + array_safe_get(global.displaySuffix_Padding, i); - extra_data[| 0] = AREA_MODE.area; extract_node = "Node_Vector4"; break; case VALUE_DISPLAY.puppet_control : @@ -729,8 +729,6 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru return setValueDirect(gradient); } ); - extra_data[| 0] = GRADIENT_INTER.smooth; - extract_node = "Node_Gradient_Out"; break; case VALUE_DISPLAY.palette : diff --git a/scripts/panel_data/panel_data.gml b/scripts/panel_data/panel_data.gml index dc3fe3b54..2f865063e 100644 --- a/scripts/panel_data/panel_data.gml +++ b/scripts/panel_data/panel_data.gml @@ -776,6 +776,7 @@ function Panel(_parent, _x, _y, _w, _h) constructor { function remove() { var con = getContent(); array_remove(content, con); + if(con) con.onClose(); content_index = 0; refresh(); @@ -808,7 +809,7 @@ function PanelContent() constructor { w = 640; h = 480; padding = ui(16); - title_height = ui(24); + title_height = ui(28); tab_x = 0; @@ -876,6 +877,8 @@ function PanelContent() constructor { function drawContent(panel) {} function drawGUI() {} + + function onClose() {} } function setFocus(target, fstring = noone) { diff --git a/scripts/render_data/render_data.gml b/scripts/render_data/render_data.gml index 63c5bb4f2..95276da37 100644 --- a/scripts/render_data/render_data.gml +++ b/scripts/render_data/render_data.gml @@ -146,6 +146,60 @@ function __renderListReset(list) { } } +function RenderList(list) { + var log = false; + LOG_BLOCK_START(); + LOG_IF(log, "=== RENDER LIST START ==="); + var queue = ds_queue_create(); + + try { + var rendering = noone; + var error = 0; + var t = current_time; + + __renderListReset(list); + + // get leaf node + for( var i = 0; i < ds_list_size(list); i++ ) { + var _node = list[| i]; + + if(is_undefined(_node)) continue; + if(!is_struct(_node)) continue; + + if(!_node.active) continue; + if(!_node.renderActive) continue; + if(_node.rendered) continue; + + if(_node.isRenderable()) + ds_queue_enqueue(queue, _node); + } + + LOG_IF(log, "Get leaf complete: found " + string(ds_queue_size(queue)) + " leaves."); + LOG_IF(log, "Start rendering..."); + + // render forward + while(!ds_queue_empty(queue)) { + rendering = ds_queue_dequeue(queue); + if(!rendering.isRenderable()) continue; + + rendering.doUpdate(); + + LOG_LINE_IF(log, "Rendering " + rendering.name + " (" + rendering.display_name + ") "); + + var nextNodes = rendering.getNextNodes(); + for( var i = 0; i < array_length(nextNodes); i++ ) + ds_queue_enqueue(queue, nextNodes[i]); + } + + } catch(e) + noti_warning(exception_print(e)); + + LOG_IF(log, "=== RENDER COMPLETE ===\n"); + LOG_END(); + + ds_queue_destroy(queue); +} + function RenderListAction(list, context = PANEL_GRAPH.getCurrentContext()) { printIf(global.RENDER_LOG, "=== RENDER LIST ACTION START [frame " + string(ANIMATOR.current_frame) + "] ==="); diff --git a/shaders/BBMOD_ShDefault/BBMOD_ShDefault.fsh b/shaders/BBMOD_ShDefault/BBMOD_ShDefault.fsh new file mode 100644 index 000000000..b0086c766 --- /dev/null +++ b/shaders/BBMOD_ShDefault/BBMOD_ShDefault.fsh @@ -0,0 +1,806 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// SSAO + +// SSAO texture +uniform sampler2D bbmod_SSAO; + +//////////////////////////////////////////////////////////////////////////////// +// Image based lighting + +// Prefiltered octahedron env. map +uniform sampler2D bbmod_IBL; +// Texel size of one octahedron +uniform vec2 bbmod_IBLTexel; + +//////////////////////////////////////////////////////////////////////////////// +// Punctual lights + +// [(x, y, z, range), (r, g, b, m), ...] +uniform vec4 bbmod_LightPunctualDataA[2 * MAX_PUNCTUAL_LIGHTS]; +// [(isSpotLight, dcosInner, dcosOuter), (dX, dY, dZ), ...] +uniform vec3 bbmod_LightPunctualDataB[2 * MAX_PUNCTUAL_LIGHTS]; + +//////////////////////////////////////////////////////////////////////////////// +// Shadow mapping + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnablePS; +// Shadowmap texture +uniform sampler2D bbmod_Shadowmap; +// (1.0/shadowmapWidth, 1.0/shadowmapHeight) +uniform vec2 bbmod_ShadowmapTexel; +// The area that the shadowmap captures +uniform float bbmod_ShadowmapArea; +// The range over which meshes smoothly transition into shadow. +uniform float bbmod_ShadowmapBias; +// The index of the light that casts shadows. Use -1 for the directional light. +uniform float bbmod_ShadowCasterIndex; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +/// @param subsurface Color in RGB and thickness/intensity in A. +/// @source https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/ +vec3 xCheapSubsurface(vec4 subsurface, vec3 eye, vec3 normal, vec3 light, vec3 lightColor) +{ + const float fLTPower = 1.0; + const float fLTScale = 1.0; + vec3 vLTLight = light + normal; + float fLTDot = pow(clamp(dot(eye, -vLTLight), 0.0, 1.0), fLTPower) * fLTScale; + float fLT = fLTDot * subsurface.a; + return subsurface.rgb * lightColor * fLT; +} +#define X_PI 3.14159265359 +#define X_2_PI 6.28318530718 + +/// @return x^2 +#define xPow2(x) ((x) * (x)) + +/// @return x^3 +#define xPow3(x) ((x) * (x) * (x)) + +/// @return x^4 +#define xPow4(x) ((x) * (x) * (x) * (x)) + +/// @return x^5 +#define xPow5(x) ((x) * (x) * (x) * (x) * (x)) + +/// @return arctan2(x,y) +#define xAtan2(x, y) atan(y, x) + +/// @return Direction from point `from` to point `to` in degrees (0-360 range). +float xPointDirection(vec2 from, vec2 to) +{ + float x = xAtan2(from.x - to.x, from.y - to.y); + return ((x > 0.0) ? x : (2.0 * X_PI + x)) * 180.0 / X_PI; +} + +/// @desc Default specular color for dielectrics +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +#define X_F0_DEFAULT vec3(0.04, 0.04, 0.04) + +/// @desc Normal distribution function +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularD_GGX(float roughness, float NdotH) +{ + float r = xPow4(roughness); + float a = NdotH * NdotH * (r - 1.0) + 1.0; + return r / (X_PI * a * a); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xSpecularD_Approx(float roughness, float RdotL) +{ + float a = roughness * roughness; + float a2 = a * a; + float rcp_a2 = 1.0 / a2; + // 0.5 / ln(2), 0.275 / ln(2) + float c = (0.72134752 * rcp_a2) + 0.39674113; + return (rcp_a2 * exp2((c * RdotL) - c)); +} + +/// @desc Roughness remapping for analytic lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_Analytic(float roughness) +{ + return xPow2(roughness + 1.0) * 0.125; +} + +/// @desc Roughness remapping for IBL lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_IBL(float roughness) +{ + return xPow2(roughness) * 0.5; +} + +/// @desc Geometric attenuation +/// @param k Use either xK_Analytic for analytic lights or xK_IBL for image based lighting. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularG_Schlick(float k, float NdotL, float NdotV) +{ + return (NdotL / (NdotL * (1.0 - k) + k)) + * (NdotV / (NdotV * (1.0 - k) + k)); +} + +/// @desc Fresnel +/// @source https://en.wikipedia.org/wiki/Schlick%27s_approximation +vec3 xSpecularF_Schlick(vec3 f0, float VdotH) +{ + return f0 + (1.0 - f0) * xPow5(1.0 - VdotH); +} + +/// @desc Cook-Torrance microfacet specular shading +/// @note N = normalize(vertexNormal) +/// L = normalize(light - vertex) +/// V = normalize(camera - vertex) +/// H = normalize(L + V) +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xBRDF(vec3 f0, float roughness, float NdotL, float NdotV, float NdotH, float VdotH) +{ + vec3 specular = xSpecularD_GGX(roughness, NdotH) + * xSpecularF_Schlick(f0, VdotH) + * xSpecularG_Schlick(xK_Analytic(roughness), NdotL, NdotH); + return specular / ((4.0 * NdotL * NdotV) + 0.1); +} + +vec3 SpecularGGX(Material m, vec3 N, vec3 V, vec3 L) +{ + vec3 H = normalize(L + V); + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + float NdotH = max(dot(N, H), 0.0); + float VdotH = max(dot(V, H), 0.0); + return xBRDF(m.Specular, m.Roughness, NdotL, NdotV, NdotH, VdotH); +} + +void DoDirectionalLightPS( + vec3 direction, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = normalize(-direction); + float NdotL = max(dot(N, L), 0.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * NdotL; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoPointLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + att *= att; + float NdotL = max(dot(N, L), 0.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * NdotL * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoSpotLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 direction, + float dcosInner, + float dcosOuter, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + float theta = dot(L, normalize(-direction)); + float epsilon = dcosInner - dcosOuter; + float intensity = clamp((theta - dcosOuter) / epsilon, 0.0, 1.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * intensity * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} +// Source: https://gamedev.stackexchange.com/questions/169508/octahedral-impostors-octahedral-mapping + +/// @param dir Sampling dir vector in world-space. +/// @return UV coordinates on an octahedron map. +vec2 xVec3ToOctahedronUv(vec3 dir) +{ + vec3 octant = sign(dir); + float sum = dot(dir, octant); + vec3 octahedron = dir / sum; + if (octahedron.z < 0.0) + { + vec3 absolute = abs(octahedron); + octahedron.xy = octant.xy * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return octahedron.xy * 0.5 + 0.5; +} + +/// @desc Converts octahedron UV into a world-space vector. +vec3 xOctahedronUvToVec3Normalized(vec2 uv) +{ + vec3 position = vec3(2.0 * (uv - 0.5), 0); + vec2 absolute = abs(position.xy); + position.z = 1.0 - absolute.x - absolute.y; + if (position.z < 0.0) + { + position.xy = sign(position.xy) * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return position; +} + +vec3 xDiffuseIBL(sampler2D ibl, vec2 texel, vec3 N) +{ + const float s = 1.0 / 8.0; + const float r2 = 7.0; + + vec2 uv0 = xVec3ToOctahedronUv(N); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + return xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +vec2 xEnvBRDFApprox(float roughness, float NdotV) +{ + const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); + vec4 r = (roughness * c0) + c1; + float a004 = (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; + return ((vec2(-1.04, 1.04) * a004) + r.zw); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xEnvBRDFApproxNonmetal(float roughness, float NdotV) +{ + // Same as EnvBRDFApprox(0.04, Roughness, NdotV) + const vec2 c0 = vec2(-1.0, -0.0275); + const vec2 c1 = vec2(1.0, 0.0425); + vec2 r = (roughness * c0) + c1; + return (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; +} + +// Fully rough optimization: +// xEnvBRDFApprox(SpecularColor, 1, 1) == SpecularColor * 0.4524 - 0.0024 +// DiffuseColor += SpecularColor * 0.45; +// SpecularColor = 0.0; + +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xSpecularIBL(sampler2D ibl, vec2 texel/*, sampler2D brdf*/, vec3 f0, float roughness, vec3 N, vec3 V) +{ + float NdotV = clamp(dot(N, V), 0.0, 1.0); + vec3 R = 2.0 * dot(V, N) * N - V; + // vec2 envBRDF = texture2D(brdf, vec2(roughness, NdotV)).xy; + vec2 envBRDF = xEnvBRDFApprox(roughness, NdotV); + + const float s = 1.0 / 8.0; + float r = roughness * 7.0; + float r2 = floor(r); + float rDiff = r - r2; + + vec2 uv0 = xVec3ToOctahedronUv(R); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + vec2 uv1 = uv0; + uv1.x = uv1.x + s; + + vec3 specular = f0 * envBRDF.x + envBRDF.y; + + vec3 col0 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))) * specular; + vec3 col1 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv1))) * specular; + + return mix(col0, col1, rDiff); +} +/// @param tanAspect (tanFovY*(screenWidth/screenHeight),-tanFovY), where +/// tanFovY = dtan(fov*0.5) +/// @param texCoord Sceen-space UV. +/// @param depth Scene depth at texCoord. +/// @return Point projected to view-space. +vec3 xProject(vec2 tanAspect, vec2 texCoord, float depth) +{ + return vec3(tanAspect * (texCoord * 2.0 - 1.0) * depth, depth); +} + +/// @param p A point in clip space (transformed by projection matrix, but not +/// normalized). +/// @return P's UV coordinates on the screen. +vec2 xUnproject(vec4 p) +{ + vec2 uv = p.xy / p.w; + uv = uv * 0.5 + 0.5; +#if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + uv.y = 1.0 - uv.y; +#endif + return uv; +} +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} +// Shadowmap filtering source: https://www.gamedev.net/tutorials/programming/graphics/contact-hardening-soft-shadows-made-fast-r4906/ +float InterleavedGradientNoise(vec2 positionScreen) +{ + vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); + return fract(magic.z * fract(dot(positionScreen, magic.xy))); +} +vec2 VogelDiskSample(int sampleIndex, int samplesCount, float phi) +{ + float GoldenAngle = 2.4; + float r = sqrt(float(sampleIndex) + 0.5) / sqrt(float(samplesCount)); + float theta = float(sampleIndex) * GoldenAngle + phi; + float sine = sin(theta); + float cosine = cos(theta); + return vec2(r * cosine, r * sine); +} + +float ShadowMap(sampler2D shadowMap, vec2 texel, vec2 uv, float compareZ) +{ + if (clamp(uv.xy, vec2(0.0), vec2(1.0)) != uv.xy) + { + return 0.0; + } + float shadow = 0.0; + float noise = 6.28 * InterleavedGradientNoise(gl_FragCoord.xy); + float bias = bbmod_ShadowmapBias / bbmod_ShadowmapArea; + for (int i = 0; i < SHADOWMAP_SAMPLE_COUNT; ++i) + { + vec2 uv2 = uv + VogelDiskSample(i, SHADOWMAP_SAMPLE_COUNT, noise) * texel * 4.0; + float depth = xDecodeDepth(texture2D(shadowMap, uv2).rgb); + if (bias != 0.0) + { + shadow += clamp((compareZ - depth) / bias, 0.0, 1.0); + } + else + { + shadow += step(depth, compareZ); + } + } + return (shadow / float(SHADOWMAP_SAMPLE_COUNT)); +} + +void PBRShader(Material material, float depth) +{ + vec3 N = material.Normal; + vec3 V = (v_vEye.w == 1.0) ? v_vEye.xyz : normalize(bbmod_CamPos - v_vVertex); + vec3 lightDiffuse = vec3(0.0); + vec3 lightSpecular = vec3(0.0); + vec3 lightSubsurface = vec3(0.0); + + // Ambient light + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + lightDiffuse += mix(ambientDown, ambientUp, N.z * 0.5 + 0.5); + + // Shadow mapping + float shadow = 0.0; + if (bbmod_ShadowmapEnablePS == 1.0) + { + vec4 shadowmapPos = v_vPosShadowmap; + shadowmapPos.xy /= shadowmapPos.w; + float shadowmapAtt = (bbmod_ShadowCasterIndex == -1.0) + ? clamp((1.0 - length(shadowmapPos.xy)) / 0.1, 0.0, 1.0) + : 1.0; + shadowmapPos.xy = shadowmapPos.xy * 0.5 + 0.5; + #if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + shadowmapPos.y = 1.0 - shadowmapPos.y; + #endif + shadowmapPos.z /= bbmod_ShadowmapArea; + + shadow = ShadowMap(bbmod_Shadowmap, bbmod_ShadowmapTexel, shadowmapPos.xy, shadowmapPos.z) + * shadowmapAtt; + } + + // IBL + lightDiffuse += xDiffuseIBL(bbmod_IBL, bbmod_IBLTexel, N); + lightSpecular += xSpecularIBL(bbmod_IBL, bbmod_IBLTexel, material.Specular, material.Roughness, N, V); + // TODO: Subsurface scattering for IBL + + // Directional light + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + DoDirectionalLightPS( + bbmod_LightDirectionalDir, + directionalLightColor, + (bbmod_ShadowCasterIndex == -1.0) ? shadow : 0.0, + v_vVertex, N, V, material, lightDiffuse, lightSpecular, lightSubsurface); + + // SSAO + float ssao = texture2D(bbmod_SSAO, xUnproject(v_vPosition)).r; + lightDiffuse *= ssao; + lightSpecular *= ssao; + + // Punctual lights + for (int i = 0; i < MAX_PUNCTUAL_LIGHTS; ++i) + { + vec4 positionRange = bbmod_LightPunctualDataA[i * 2]; + vec4 colorAlpha = bbmod_LightPunctualDataA[(i * 2) + 1]; + vec3 isSpotInnerOuter = bbmod_LightPunctualDataB[i * 2]; + vec3 direction = bbmod_LightPunctualDataB[(i * 2) + 1]; + vec3 color = xGammaToLinear(colorAlpha.rgb) * colorAlpha.a; + + if (isSpotInnerOuter.x == 1.0) + { + DoSpotLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + direction, isSpotInnerOuter.y, isSpotInnerOuter.z, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + else + { + DoPointLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + } + + // Lightmap + + // Diffuse + gl_FragColor.rgb = material.Base * lightDiffuse; + // Specular + gl_FragColor.rgb += lightSpecular; + // Ambient occlusion + gl_FragColor.rgb *= material.AO; + // Emissive + gl_FragColor.rgb += material.Emissive; + // Subsurface scattering + gl_FragColor.rgb += lightSubsurface; + // Opacity + gl_FragColor.a = material.Opacity; + // Soft particles + // Fog + Fog(depth); + + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + PBRShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefault/BBMOD_ShDefault.vsh b/shaders/BBMOD_ShDefault/BBMOD_ShDefault.vsh new file mode 100644 index 000000000..3d6c77617 --- /dev/null +++ b/shaders/BBMOD_ShDefault/BBMOD_ShDefault.vsh @@ -0,0 +1,115 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnableVS; +// WORLD_VIEW_PROJECTION matrix used when rendering shadowmap +uniform mat4 bbmod_ShadowmapMatrix; +// Offsets vertex position by its normal scaled by this value +uniform float bbmod_ShadowmapNormalOffset; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_vEye.xyz = normalize(-vec3( + gm_Matrices[MATRIX_VIEW][0][2], + gm_Matrices[MATRIX_VIEW][1][2], + gm_Matrices[MATRIX_VIEW][2][2] + )); + v_vEye.w = (gm_Matrices[MATRIX_PROJECTION][2][3] == 0.0) ? 1.0 : 0.0; + + v_mTBN = mat3(tangent, bitangent, normal); + + //////////////////////////////////////////////////////////////////////////// + // Vertex position in shadowmap + if (bbmod_ShadowmapEnableVS == 1.0) + { + v_vPosShadowmap = bbmod_ShadowmapMatrix + * vec4(v_vVertex + normal * bbmod_ShadowmapNormalOffset, 1.0); + } +} diff --git a/shaders/BBMOD_ShDefault/BBMOD_ShDefault.yy b/shaders/BBMOD_ShDefault/BBMOD_ShDefault.yy new file mode 100644 index 000000000..1e06cac74 --- /dev/null +++ b/shaders/BBMOD_ShDefault/BBMOD_ShDefault.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefault", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShDefaultAnimated/BBMOD_ShDefaultAnimated.fsh b/shaders/BBMOD_ShDefaultAnimated/BBMOD_ShDefaultAnimated.fsh new file mode 100644 index 000000000..b0086c766 --- /dev/null +++ b/shaders/BBMOD_ShDefaultAnimated/BBMOD_ShDefaultAnimated.fsh @@ -0,0 +1,806 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// SSAO + +// SSAO texture +uniform sampler2D bbmod_SSAO; + +//////////////////////////////////////////////////////////////////////////////// +// Image based lighting + +// Prefiltered octahedron env. map +uniform sampler2D bbmod_IBL; +// Texel size of one octahedron +uniform vec2 bbmod_IBLTexel; + +//////////////////////////////////////////////////////////////////////////////// +// Punctual lights + +// [(x, y, z, range), (r, g, b, m), ...] +uniform vec4 bbmod_LightPunctualDataA[2 * MAX_PUNCTUAL_LIGHTS]; +// [(isSpotLight, dcosInner, dcosOuter), (dX, dY, dZ), ...] +uniform vec3 bbmod_LightPunctualDataB[2 * MAX_PUNCTUAL_LIGHTS]; + +//////////////////////////////////////////////////////////////////////////////// +// Shadow mapping + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnablePS; +// Shadowmap texture +uniform sampler2D bbmod_Shadowmap; +// (1.0/shadowmapWidth, 1.0/shadowmapHeight) +uniform vec2 bbmod_ShadowmapTexel; +// The area that the shadowmap captures +uniform float bbmod_ShadowmapArea; +// The range over which meshes smoothly transition into shadow. +uniform float bbmod_ShadowmapBias; +// The index of the light that casts shadows. Use -1 for the directional light. +uniform float bbmod_ShadowCasterIndex; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +/// @param subsurface Color in RGB and thickness/intensity in A. +/// @source https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/ +vec3 xCheapSubsurface(vec4 subsurface, vec3 eye, vec3 normal, vec3 light, vec3 lightColor) +{ + const float fLTPower = 1.0; + const float fLTScale = 1.0; + vec3 vLTLight = light + normal; + float fLTDot = pow(clamp(dot(eye, -vLTLight), 0.0, 1.0), fLTPower) * fLTScale; + float fLT = fLTDot * subsurface.a; + return subsurface.rgb * lightColor * fLT; +} +#define X_PI 3.14159265359 +#define X_2_PI 6.28318530718 + +/// @return x^2 +#define xPow2(x) ((x) * (x)) + +/// @return x^3 +#define xPow3(x) ((x) * (x) * (x)) + +/// @return x^4 +#define xPow4(x) ((x) * (x) * (x) * (x)) + +/// @return x^5 +#define xPow5(x) ((x) * (x) * (x) * (x) * (x)) + +/// @return arctan2(x,y) +#define xAtan2(x, y) atan(y, x) + +/// @return Direction from point `from` to point `to` in degrees (0-360 range). +float xPointDirection(vec2 from, vec2 to) +{ + float x = xAtan2(from.x - to.x, from.y - to.y); + return ((x > 0.0) ? x : (2.0 * X_PI + x)) * 180.0 / X_PI; +} + +/// @desc Default specular color for dielectrics +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +#define X_F0_DEFAULT vec3(0.04, 0.04, 0.04) + +/// @desc Normal distribution function +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularD_GGX(float roughness, float NdotH) +{ + float r = xPow4(roughness); + float a = NdotH * NdotH * (r - 1.0) + 1.0; + return r / (X_PI * a * a); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xSpecularD_Approx(float roughness, float RdotL) +{ + float a = roughness * roughness; + float a2 = a * a; + float rcp_a2 = 1.0 / a2; + // 0.5 / ln(2), 0.275 / ln(2) + float c = (0.72134752 * rcp_a2) + 0.39674113; + return (rcp_a2 * exp2((c * RdotL) - c)); +} + +/// @desc Roughness remapping for analytic lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_Analytic(float roughness) +{ + return xPow2(roughness + 1.0) * 0.125; +} + +/// @desc Roughness remapping for IBL lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_IBL(float roughness) +{ + return xPow2(roughness) * 0.5; +} + +/// @desc Geometric attenuation +/// @param k Use either xK_Analytic for analytic lights or xK_IBL for image based lighting. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularG_Schlick(float k, float NdotL, float NdotV) +{ + return (NdotL / (NdotL * (1.0 - k) + k)) + * (NdotV / (NdotV * (1.0 - k) + k)); +} + +/// @desc Fresnel +/// @source https://en.wikipedia.org/wiki/Schlick%27s_approximation +vec3 xSpecularF_Schlick(vec3 f0, float VdotH) +{ + return f0 + (1.0 - f0) * xPow5(1.0 - VdotH); +} + +/// @desc Cook-Torrance microfacet specular shading +/// @note N = normalize(vertexNormal) +/// L = normalize(light - vertex) +/// V = normalize(camera - vertex) +/// H = normalize(L + V) +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xBRDF(vec3 f0, float roughness, float NdotL, float NdotV, float NdotH, float VdotH) +{ + vec3 specular = xSpecularD_GGX(roughness, NdotH) + * xSpecularF_Schlick(f0, VdotH) + * xSpecularG_Schlick(xK_Analytic(roughness), NdotL, NdotH); + return specular / ((4.0 * NdotL * NdotV) + 0.1); +} + +vec3 SpecularGGX(Material m, vec3 N, vec3 V, vec3 L) +{ + vec3 H = normalize(L + V); + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + float NdotH = max(dot(N, H), 0.0); + float VdotH = max(dot(V, H), 0.0); + return xBRDF(m.Specular, m.Roughness, NdotL, NdotV, NdotH, VdotH); +} + +void DoDirectionalLightPS( + vec3 direction, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = normalize(-direction); + float NdotL = max(dot(N, L), 0.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * NdotL; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoPointLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + att *= att; + float NdotL = max(dot(N, L), 0.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * NdotL * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoSpotLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 direction, + float dcosInner, + float dcosOuter, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + float theta = dot(L, normalize(-direction)); + float epsilon = dcosInner - dcosOuter; + float intensity = clamp((theta - dcosOuter) / epsilon, 0.0, 1.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * intensity * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} +// Source: https://gamedev.stackexchange.com/questions/169508/octahedral-impostors-octahedral-mapping + +/// @param dir Sampling dir vector in world-space. +/// @return UV coordinates on an octahedron map. +vec2 xVec3ToOctahedronUv(vec3 dir) +{ + vec3 octant = sign(dir); + float sum = dot(dir, octant); + vec3 octahedron = dir / sum; + if (octahedron.z < 0.0) + { + vec3 absolute = abs(octahedron); + octahedron.xy = octant.xy * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return octahedron.xy * 0.5 + 0.5; +} + +/// @desc Converts octahedron UV into a world-space vector. +vec3 xOctahedronUvToVec3Normalized(vec2 uv) +{ + vec3 position = vec3(2.0 * (uv - 0.5), 0); + vec2 absolute = abs(position.xy); + position.z = 1.0 - absolute.x - absolute.y; + if (position.z < 0.0) + { + position.xy = sign(position.xy) * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return position; +} + +vec3 xDiffuseIBL(sampler2D ibl, vec2 texel, vec3 N) +{ + const float s = 1.0 / 8.0; + const float r2 = 7.0; + + vec2 uv0 = xVec3ToOctahedronUv(N); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + return xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +vec2 xEnvBRDFApprox(float roughness, float NdotV) +{ + const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); + vec4 r = (roughness * c0) + c1; + float a004 = (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; + return ((vec2(-1.04, 1.04) * a004) + r.zw); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xEnvBRDFApproxNonmetal(float roughness, float NdotV) +{ + // Same as EnvBRDFApprox(0.04, Roughness, NdotV) + const vec2 c0 = vec2(-1.0, -0.0275); + const vec2 c1 = vec2(1.0, 0.0425); + vec2 r = (roughness * c0) + c1; + return (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; +} + +// Fully rough optimization: +// xEnvBRDFApprox(SpecularColor, 1, 1) == SpecularColor * 0.4524 - 0.0024 +// DiffuseColor += SpecularColor * 0.45; +// SpecularColor = 0.0; + +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xSpecularIBL(sampler2D ibl, vec2 texel/*, sampler2D brdf*/, vec3 f0, float roughness, vec3 N, vec3 V) +{ + float NdotV = clamp(dot(N, V), 0.0, 1.0); + vec3 R = 2.0 * dot(V, N) * N - V; + // vec2 envBRDF = texture2D(brdf, vec2(roughness, NdotV)).xy; + vec2 envBRDF = xEnvBRDFApprox(roughness, NdotV); + + const float s = 1.0 / 8.0; + float r = roughness * 7.0; + float r2 = floor(r); + float rDiff = r - r2; + + vec2 uv0 = xVec3ToOctahedronUv(R); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + vec2 uv1 = uv0; + uv1.x = uv1.x + s; + + vec3 specular = f0 * envBRDF.x + envBRDF.y; + + vec3 col0 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))) * specular; + vec3 col1 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv1))) * specular; + + return mix(col0, col1, rDiff); +} +/// @param tanAspect (tanFovY*(screenWidth/screenHeight),-tanFovY), where +/// tanFovY = dtan(fov*0.5) +/// @param texCoord Sceen-space UV. +/// @param depth Scene depth at texCoord. +/// @return Point projected to view-space. +vec3 xProject(vec2 tanAspect, vec2 texCoord, float depth) +{ + return vec3(tanAspect * (texCoord * 2.0 - 1.0) * depth, depth); +} + +/// @param p A point in clip space (transformed by projection matrix, but not +/// normalized). +/// @return P's UV coordinates on the screen. +vec2 xUnproject(vec4 p) +{ + vec2 uv = p.xy / p.w; + uv = uv * 0.5 + 0.5; +#if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + uv.y = 1.0 - uv.y; +#endif + return uv; +} +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} +// Shadowmap filtering source: https://www.gamedev.net/tutorials/programming/graphics/contact-hardening-soft-shadows-made-fast-r4906/ +float InterleavedGradientNoise(vec2 positionScreen) +{ + vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); + return fract(magic.z * fract(dot(positionScreen, magic.xy))); +} +vec2 VogelDiskSample(int sampleIndex, int samplesCount, float phi) +{ + float GoldenAngle = 2.4; + float r = sqrt(float(sampleIndex) + 0.5) / sqrt(float(samplesCount)); + float theta = float(sampleIndex) * GoldenAngle + phi; + float sine = sin(theta); + float cosine = cos(theta); + return vec2(r * cosine, r * sine); +} + +float ShadowMap(sampler2D shadowMap, vec2 texel, vec2 uv, float compareZ) +{ + if (clamp(uv.xy, vec2(0.0), vec2(1.0)) != uv.xy) + { + return 0.0; + } + float shadow = 0.0; + float noise = 6.28 * InterleavedGradientNoise(gl_FragCoord.xy); + float bias = bbmod_ShadowmapBias / bbmod_ShadowmapArea; + for (int i = 0; i < SHADOWMAP_SAMPLE_COUNT; ++i) + { + vec2 uv2 = uv + VogelDiskSample(i, SHADOWMAP_SAMPLE_COUNT, noise) * texel * 4.0; + float depth = xDecodeDepth(texture2D(shadowMap, uv2).rgb); + if (bias != 0.0) + { + shadow += clamp((compareZ - depth) / bias, 0.0, 1.0); + } + else + { + shadow += step(depth, compareZ); + } + } + return (shadow / float(SHADOWMAP_SAMPLE_COUNT)); +} + +void PBRShader(Material material, float depth) +{ + vec3 N = material.Normal; + vec3 V = (v_vEye.w == 1.0) ? v_vEye.xyz : normalize(bbmod_CamPos - v_vVertex); + vec3 lightDiffuse = vec3(0.0); + vec3 lightSpecular = vec3(0.0); + vec3 lightSubsurface = vec3(0.0); + + // Ambient light + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + lightDiffuse += mix(ambientDown, ambientUp, N.z * 0.5 + 0.5); + + // Shadow mapping + float shadow = 0.0; + if (bbmod_ShadowmapEnablePS == 1.0) + { + vec4 shadowmapPos = v_vPosShadowmap; + shadowmapPos.xy /= shadowmapPos.w; + float shadowmapAtt = (bbmod_ShadowCasterIndex == -1.0) + ? clamp((1.0 - length(shadowmapPos.xy)) / 0.1, 0.0, 1.0) + : 1.0; + shadowmapPos.xy = shadowmapPos.xy * 0.5 + 0.5; + #if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + shadowmapPos.y = 1.0 - shadowmapPos.y; + #endif + shadowmapPos.z /= bbmod_ShadowmapArea; + + shadow = ShadowMap(bbmod_Shadowmap, bbmod_ShadowmapTexel, shadowmapPos.xy, shadowmapPos.z) + * shadowmapAtt; + } + + // IBL + lightDiffuse += xDiffuseIBL(bbmod_IBL, bbmod_IBLTexel, N); + lightSpecular += xSpecularIBL(bbmod_IBL, bbmod_IBLTexel, material.Specular, material.Roughness, N, V); + // TODO: Subsurface scattering for IBL + + // Directional light + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + DoDirectionalLightPS( + bbmod_LightDirectionalDir, + directionalLightColor, + (bbmod_ShadowCasterIndex == -1.0) ? shadow : 0.0, + v_vVertex, N, V, material, lightDiffuse, lightSpecular, lightSubsurface); + + // SSAO + float ssao = texture2D(bbmod_SSAO, xUnproject(v_vPosition)).r; + lightDiffuse *= ssao; + lightSpecular *= ssao; + + // Punctual lights + for (int i = 0; i < MAX_PUNCTUAL_LIGHTS; ++i) + { + vec4 positionRange = bbmod_LightPunctualDataA[i * 2]; + vec4 colorAlpha = bbmod_LightPunctualDataA[(i * 2) + 1]; + vec3 isSpotInnerOuter = bbmod_LightPunctualDataB[i * 2]; + vec3 direction = bbmod_LightPunctualDataB[(i * 2) + 1]; + vec3 color = xGammaToLinear(colorAlpha.rgb) * colorAlpha.a; + + if (isSpotInnerOuter.x == 1.0) + { + DoSpotLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + direction, isSpotInnerOuter.y, isSpotInnerOuter.z, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + else + { + DoPointLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + } + + // Lightmap + + // Diffuse + gl_FragColor.rgb = material.Base * lightDiffuse; + // Specular + gl_FragColor.rgb += lightSpecular; + // Ambient occlusion + gl_FragColor.rgb *= material.AO; + // Emissive + gl_FragColor.rgb += material.Emissive; + // Subsurface scattering + gl_FragColor.rgb += lightSubsurface; + // Opacity + gl_FragColor.a = material.Opacity; + // Soft particles + // Fog + Fog(depth); + + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + PBRShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefaultAnimated/BBMOD_ShDefaultAnimated.vsh b/shaders/BBMOD_ShDefaultAnimated/BBMOD_ShDefaultAnimated.vsh new file mode 100644 index 000000000..defb33d01 --- /dev/null +++ b/shaders/BBMOD_ShDefaultAnimated/BBMOD_ShDefaultAnimated.vsh @@ -0,0 +1,171 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +attribute vec4 in_BoneIndex; +attribute vec4 in_BoneWeight; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +uniform vec4 bbmod_Bones[2 * BBMOD_MAX_BONES]; + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnableVS; +// WORLD_VIEW_PROJECTION matrix used when rendering shadowmap +uniform mat4 bbmod_ShadowmapMatrix; +// Offsets vertex position by its normal scaled by this value +uniform float bbmod_ShadowmapNormalOffset; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +vec3 QuaternionRotate(vec4 q, vec3 v) +{ + return (v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v)); +} + +vec3 DualQuaternionTransform(vec4 real, vec4 dual, vec3 v) +{ + return (QuaternionRotate(real, v) + + 2.0 * (real.w * dual.xyz - dual.w * real.xyz + cross(real.xyz, dual.xyz))); +} + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + // Source: + // https://www.cs.utah.edu/~ladislav/kavan07skinning/kavan07skinning.pdf + // https://www.cs.utah.edu/~ladislav/dq/dqs.cg + ivec4 i = ivec4(in_BoneIndex) * 2; + ivec4 j = i + 1; + + vec4 real0 = bbmod_Bones[i.x]; + vec4 real1 = bbmod_Bones[i.y]; + vec4 real2 = bbmod_Bones[i.z]; + vec4 real3 = bbmod_Bones[i.w]; + + vec4 dual0 = bbmod_Bones[j.x]; + vec4 dual1 = bbmod_Bones[j.y]; + vec4 dual2 = bbmod_Bones[j.z]; + vec4 dual3 = bbmod_Bones[j.w]; + + if (dot(real0, real1) < 0.0) { real1 *= -1.0; dual1 *= -1.0; } + if (dot(real0, real2) < 0.0) { real2 *= -1.0; dual2 *= -1.0; } + if (dot(real0, real3) < 0.0) { real3 *= -1.0; dual3 *= -1.0; } + + vec4 blendReal = + real0 * in_BoneWeight.x + + real1 * in_BoneWeight.y + + real2 * in_BoneWeight.z + + real3 * in_BoneWeight.w; + + vec4 blendDual = + dual0 * in_BoneWeight.x + + dual1 * in_BoneWeight.y + + dual2 * in_BoneWeight.z + + dual3 * in_BoneWeight.w; + + float len = length(blendReal); + blendReal /= len; + blendDual /= len; + + vertex = vec4(DualQuaternionTransform(blendReal, blendDual, vertex.xyz), 1.0); + normal = QuaternionRotate(blendReal, normal); + tangent = QuaternionRotate(blendReal, tangent); + bitangent = QuaternionRotate(blendReal, bitangent); + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_vEye.xyz = normalize(-vec3( + gm_Matrices[MATRIX_VIEW][0][2], + gm_Matrices[MATRIX_VIEW][1][2], + gm_Matrices[MATRIX_VIEW][2][2] + )); + v_vEye.w = (gm_Matrices[MATRIX_PROJECTION][2][3] == 0.0) ? 1.0 : 0.0; + + v_mTBN = mat3(tangent, bitangent, normal); + + //////////////////////////////////////////////////////////////////////////// + // Vertex position in shadowmap + if (bbmod_ShadowmapEnableVS == 1.0) + { + v_vPosShadowmap = bbmod_ShadowmapMatrix + * vec4(v_vVertex + normal * bbmod_ShadowmapNormalOffset, 1.0); + } +} diff --git a/shaders/BBMOD_ShDefaultAnimated/BBMOD_ShDefaultAnimated.yy b/shaders/BBMOD_ShDefaultAnimated/BBMOD_ShDefaultAnimated.yy new file mode 100644 index 000000000..d2056085d --- /dev/null +++ b/shaders/BBMOD_ShDefaultAnimated/BBMOD_ShDefaultAnimated.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefaultAnimated", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShDefaultBatched/BBMOD_ShDefaultBatched.fsh b/shaders/BBMOD_ShDefaultBatched/BBMOD_ShDefaultBatched.fsh new file mode 100644 index 000000000..b0086c766 --- /dev/null +++ b/shaders/BBMOD_ShDefaultBatched/BBMOD_ShDefaultBatched.fsh @@ -0,0 +1,806 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// SSAO + +// SSAO texture +uniform sampler2D bbmod_SSAO; + +//////////////////////////////////////////////////////////////////////////////// +// Image based lighting + +// Prefiltered octahedron env. map +uniform sampler2D bbmod_IBL; +// Texel size of one octahedron +uniform vec2 bbmod_IBLTexel; + +//////////////////////////////////////////////////////////////////////////////// +// Punctual lights + +// [(x, y, z, range), (r, g, b, m), ...] +uniform vec4 bbmod_LightPunctualDataA[2 * MAX_PUNCTUAL_LIGHTS]; +// [(isSpotLight, dcosInner, dcosOuter), (dX, dY, dZ), ...] +uniform vec3 bbmod_LightPunctualDataB[2 * MAX_PUNCTUAL_LIGHTS]; + +//////////////////////////////////////////////////////////////////////////////// +// Shadow mapping + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnablePS; +// Shadowmap texture +uniform sampler2D bbmod_Shadowmap; +// (1.0/shadowmapWidth, 1.0/shadowmapHeight) +uniform vec2 bbmod_ShadowmapTexel; +// The area that the shadowmap captures +uniform float bbmod_ShadowmapArea; +// The range over which meshes smoothly transition into shadow. +uniform float bbmod_ShadowmapBias; +// The index of the light that casts shadows. Use -1 for the directional light. +uniform float bbmod_ShadowCasterIndex; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +/// @param subsurface Color in RGB and thickness/intensity in A. +/// @source https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/ +vec3 xCheapSubsurface(vec4 subsurface, vec3 eye, vec3 normal, vec3 light, vec3 lightColor) +{ + const float fLTPower = 1.0; + const float fLTScale = 1.0; + vec3 vLTLight = light + normal; + float fLTDot = pow(clamp(dot(eye, -vLTLight), 0.0, 1.0), fLTPower) * fLTScale; + float fLT = fLTDot * subsurface.a; + return subsurface.rgb * lightColor * fLT; +} +#define X_PI 3.14159265359 +#define X_2_PI 6.28318530718 + +/// @return x^2 +#define xPow2(x) ((x) * (x)) + +/// @return x^3 +#define xPow3(x) ((x) * (x) * (x)) + +/// @return x^4 +#define xPow4(x) ((x) * (x) * (x) * (x)) + +/// @return x^5 +#define xPow5(x) ((x) * (x) * (x) * (x) * (x)) + +/// @return arctan2(x,y) +#define xAtan2(x, y) atan(y, x) + +/// @return Direction from point `from` to point `to` in degrees (0-360 range). +float xPointDirection(vec2 from, vec2 to) +{ + float x = xAtan2(from.x - to.x, from.y - to.y); + return ((x > 0.0) ? x : (2.0 * X_PI + x)) * 180.0 / X_PI; +} + +/// @desc Default specular color for dielectrics +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +#define X_F0_DEFAULT vec3(0.04, 0.04, 0.04) + +/// @desc Normal distribution function +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularD_GGX(float roughness, float NdotH) +{ + float r = xPow4(roughness); + float a = NdotH * NdotH * (r - 1.0) + 1.0; + return r / (X_PI * a * a); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xSpecularD_Approx(float roughness, float RdotL) +{ + float a = roughness * roughness; + float a2 = a * a; + float rcp_a2 = 1.0 / a2; + // 0.5 / ln(2), 0.275 / ln(2) + float c = (0.72134752 * rcp_a2) + 0.39674113; + return (rcp_a2 * exp2((c * RdotL) - c)); +} + +/// @desc Roughness remapping for analytic lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_Analytic(float roughness) +{ + return xPow2(roughness + 1.0) * 0.125; +} + +/// @desc Roughness remapping for IBL lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_IBL(float roughness) +{ + return xPow2(roughness) * 0.5; +} + +/// @desc Geometric attenuation +/// @param k Use either xK_Analytic for analytic lights or xK_IBL for image based lighting. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularG_Schlick(float k, float NdotL, float NdotV) +{ + return (NdotL / (NdotL * (1.0 - k) + k)) + * (NdotV / (NdotV * (1.0 - k) + k)); +} + +/// @desc Fresnel +/// @source https://en.wikipedia.org/wiki/Schlick%27s_approximation +vec3 xSpecularF_Schlick(vec3 f0, float VdotH) +{ + return f0 + (1.0 - f0) * xPow5(1.0 - VdotH); +} + +/// @desc Cook-Torrance microfacet specular shading +/// @note N = normalize(vertexNormal) +/// L = normalize(light - vertex) +/// V = normalize(camera - vertex) +/// H = normalize(L + V) +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xBRDF(vec3 f0, float roughness, float NdotL, float NdotV, float NdotH, float VdotH) +{ + vec3 specular = xSpecularD_GGX(roughness, NdotH) + * xSpecularF_Schlick(f0, VdotH) + * xSpecularG_Schlick(xK_Analytic(roughness), NdotL, NdotH); + return specular / ((4.0 * NdotL * NdotV) + 0.1); +} + +vec3 SpecularGGX(Material m, vec3 N, vec3 V, vec3 L) +{ + vec3 H = normalize(L + V); + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + float NdotH = max(dot(N, H), 0.0); + float VdotH = max(dot(V, H), 0.0); + return xBRDF(m.Specular, m.Roughness, NdotL, NdotV, NdotH, VdotH); +} + +void DoDirectionalLightPS( + vec3 direction, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = normalize(-direction); + float NdotL = max(dot(N, L), 0.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * NdotL; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoPointLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + att *= att; + float NdotL = max(dot(N, L), 0.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * NdotL * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoSpotLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 direction, + float dcosInner, + float dcosOuter, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + float theta = dot(L, normalize(-direction)); + float epsilon = dcosInner - dcosOuter; + float intensity = clamp((theta - dcosOuter) / epsilon, 0.0, 1.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * intensity * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} +// Source: https://gamedev.stackexchange.com/questions/169508/octahedral-impostors-octahedral-mapping + +/// @param dir Sampling dir vector in world-space. +/// @return UV coordinates on an octahedron map. +vec2 xVec3ToOctahedronUv(vec3 dir) +{ + vec3 octant = sign(dir); + float sum = dot(dir, octant); + vec3 octahedron = dir / sum; + if (octahedron.z < 0.0) + { + vec3 absolute = abs(octahedron); + octahedron.xy = octant.xy * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return octahedron.xy * 0.5 + 0.5; +} + +/// @desc Converts octahedron UV into a world-space vector. +vec3 xOctahedronUvToVec3Normalized(vec2 uv) +{ + vec3 position = vec3(2.0 * (uv - 0.5), 0); + vec2 absolute = abs(position.xy); + position.z = 1.0 - absolute.x - absolute.y; + if (position.z < 0.0) + { + position.xy = sign(position.xy) * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return position; +} + +vec3 xDiffuseIBL(sampler2D ibl, vec2 texel, vec3 N) +{ + const float s = 1.0 / 8.0; + const float r2 = 7.0; + + vec2 uv0 = xVec3ToOctahedronUv(N); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + return xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +vec2 xEnvBRDFApprox(float roughness, float NdotV) +{ + const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); + vec4 r = (roughness * c0) + c1; + float a004 = (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; + return ((vec2(-1.04, 1.04) * a004) + r.zw); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xEnvBRDFApproxNonmetal(float roughness, float NdotV) +{ + // Same as EnvBRDFApprox(0.04, Roughness, NdotV) + const vec2 c0 = vec2(-1.0, -0.0275); + const vec2 c1 = vec2(1.0, 0.0425); + vec2 r = (roughness * c0) + c1; + return (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; +} + +// Fully rough optimization: +// xEnvBRDFApprox(SpecularColor, 1, 1) == SpecularColor * 0.4524 - 0.0024 +// DiffuseColor += SpecularColor * 0.45; +// SpecularColor = 0.0; + +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xSpecularIBL(sampler2D ibl, vec2 texel/*, sampler2D brdf*/, vec3 f0, float roughness, vec3 N, vec3 V) +{ + float NdotV = clamp(dot(N, V), 0.0, 1.0); + vec3 R = 2.0 * dot(V, N) * N - V; + // vec2 envBRDF = texture2D(brdf, vec2(roughness, NdotV)).xy; + vec2 envBRDF = xEnvBRDFApprox(roughness, NdotV); + + const float s = 1.0 / 8.0; + float r = roughness * 7.0; + float r2 = floor(r); + float rDiff = r - r2; + + vec2 uv0 = xVec3ToOctahedronUv(R); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + vec2 uv1 = uv0; + uv1.x = uv1.x + s; + + vec3 specular = f0 * envBRDF.x + envBRDF.y; + + vec3 col0 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))) * specular; + vec3 col1 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv1))) * specular; + + return mix(col0, col1, rDiff); +} +/// @param tanAspect (tanFovY*(screenWidth/screenHeight),-tanFovY), where +/// tanFovY = dtan(fov*0.5) +/// @param texCoord Sceen-space UV. +/// @param depth Scene depth at texCoord. +/// @return Point projected to view-space. +vec3 xProject(vec2 tanAspect, vec2 texCoord, float depth) +{ + return vec3(tanAspect * (texCoord * 2.0 - 1.0) * depth, depth); +} + +/// @param p A point in clip space (transformed by projection matrix, but not +/// normalized). +/// @return P's UV coordinates on the screen. +vec2 xUnproject(vec4 p) +{ + vec2 uv = p.xy / p.w; + uv = uv * 0.5 + 0.5; +#if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + uv.y = 1.0 - uv.y; +#endif + return uv; +} +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} +// Shadowmap filtering source: https://www.gamedev.net/tutorials/programming/graphics/contact-hardening-soft-shadows-made-fast-r4906/ +float InterleavedGradientNoise(vec2 positionScreen) +{ + vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); + return fract(magic.z * fract(dot(positionScreen, magic.xy))); +} +vec2 VogelDiskSample(int sampleIndex, int samplesCount, float phi) +{ + float GoldenAngle = 2.4; + float r = sqrt(float(sampleIndex) + 0.5) / sqrt(float(samplesCount)); + float theta = float(sampleIndex) * GoldenAngle + phi; + float sine = sin(theta); + float cosine = cos(theta); + return vec2(r * cosine, r * sine); +} + +float ShadowMap(sampler2D shadowMap, vec2 texel, vec2 uv, float compareZ) +{ + if (clamp(uv.xy, vec2(0.0), vec2(1.0)) != uv.xy) + { + return 0.0; + } + float shadow = 0.0; + float noise = 6.28 * InterleavedGradientNoise(gl_FragCoord.xy); + float bias = bbmod_ShadowmapBias / bbmod_ShadowmapArea; + for (int i = 0; i < SHADOWMAP_SAMPLE_COUNT; ++i) + { + vec2 uv2 = uv + VogelDiskSample(i, SHADOWMAP_SAMPLE_COUNT, noise) * texel * 4.0; + float depth = xDecodeDepth(texture2D(shadowMap, uv2).rgb); + if (bias != 0.0) + { + shadow += clamp((compareZ - depth) / bias, 0.0, 1.0); + } + else + { + shadow += step(depth, compareZ); + } + } + return (shadow / float(SHADOWMAP_SAMPLE_COUNT)); +} + +void PBRShader(Material material, float depth) +{ + vec3 N = material.Normal; + vec3 V = (v_vEye.w == 1.0) ? v_vEye.xyz : normalize(bbmod_CamPos - v_vVertex); + vec3 lightDiffuse = vec3(0.0); + vec3 lightSpecular = vec3(0.0); + vec3 lightSubsurface = vec3(0.0); + + // Ambient light + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + lightDiffuse += mix(ambientDown, ambientUp, N.z * 0.5 + 0.5); + + // Shadow mapping + float shadow = 0.0; + if (bbmod_ShadowmapEnablePS == 1.0) + { + vec4 shadowmapPos = v_vPosShadowmap; + shadowmapPos.xy /= shadowmapPos.w; + float shadowmapAtt = (bbmod_ShadowCasterIndex == -1.0) + ? clamp((1.0 - length(shadowmapPos.xy)) / 0.1, 0.0, 1.0) + : 1.0; + shadowmapPos.xy = shadowmapPos.xy * 0.5 + 0.5; + #if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + shadowmapPos.y = 1.0 - shadowmapPos.y; + #endif + shadowmapPos.z /= bbmod_ShadowmapArea; + + shadow = ShadowMap(bbmod_Shadowmap, bbmod_ShadowmapTexel, shadowmapPos.xy, shadowmapPos.z) + * shadowmapAtt; + } + + // IBL + lightDiffuse += xDiffuseIBL(bbmod_IBL, bbmod_IBLTexel, N); + lightSpecular += xSpecularIBL(bbmod_IBL, bbmod_IBLTexel, material.Specular, material.Roughness, N, V); + // TODO: Subsurface scattering for IBL + + // Directional light + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + DoDirectionalLightPS( + bbmod_LightDirectionalDir, + directionalLightColor, + (bbmod_ShadowCasterIndex == -1.0) ? shadow : 0.0, + v_vVertex, N, V, material, lightDiffuse, lightSpecular, lightSubsurface); + + // SSAO + float ssao = texture2D(bbmod_SSAO, xUnproject(v_vPosition)).r; + lightDiffuse *= ssao; + lightSpecular *= ssao; + + // Punctual lights + for (int i = 0; i < MAX_PUNCTUAL_LIGHTS; ++i) + { + vec4 positionRange = bbmod_LightPunctualDataA[i * 2]; + vec4 colorAlpha = bbmod_LightPunctualDataA[(i * 2) + 1]; + vec3 isSpotInnerOuter = bbmod_LightPunctualDataB[i * 2]; + vec3 direction = bbmod_LightPunctualDataB[(i * 2) + 1]; + vec3 color = xGammaToLinear(colorAlpha.rgb) * colorAlpha.a; + + if (isSpotInnerOuter.x == 1.0) + { + DoSpotLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + direction, isSpotInnerOuter.y, isSpotInnerOuter.z, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + else + { + DoPointLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + } + + // Lightmap + + // Diffuse + gl_FragColor.rgb = material.Base * lightDiffuse; + // Specular + gl_FragColor.rgb += lightSpecular; + // Ambient occlusion + gl_FragColor.rgb *= material.AO; + // Emissive + gl_FragColor.rgb += material.Emissive; + // Subsurface scattering + gl_FragColor.rgb += lightSubsurface; + // Opacity + gl_FragColor.a = material.Opacity; + // Soft particles + // Fog + Fog(depth); + + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + PBRShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefaultBatched/BBMOD_ShDefaultBatched.vsh b/shaders/BBMOD_ShDefaultBatched/BBMOD_ShDefaultBatched.vsh new file mode 100644 index 000000000..02e53a08f --- /dev/null +++ b/shaders/BBMOD_ShDefaultBatched/BBMOD_ShDefaultBatched.vsh @@ -0,0 +1,132 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +attribute float in_Id; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +uniform vec4 bbmod_BatchData[BBMOD_MAX_BATCH_VEC4S]; + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnableVS; +// WORLD_VIEW_PROJECTION matrix used when rendering shadowmap +uniform mat4 bbmod_ShadowmapMatrix; +// Offsets vertex position by its normal scaled by this value +uniform float bbmod_ShadowmapNormalOffset; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +vec3 QuaternionRotate(vec4 q, vec3 v) +{ + return (v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v)); +} + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); + + int idx = int(in_Id) * 3; + vec4 posScale = bbmod_BatchData[idx]; + vec4 rot = bbmod_BatchData[idx + 1]; + + vertex = vec4(posScale.xyz + (QuaternionRotate(rot, vertex.xyz) * posScale.w), 1.0); + normal = QuaternionRotate(rot, normal); + tangent = QuaternionRotate(rot, tangent); + bitangent = QuaternionRotate(rot, bitangent); + +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_vEye.xyz = normalize(-vec3( + gm_Matrices[MATRIX_VIEW][0][2], + gm_Matrices[MATRIX_VIEW][1][2], + gm_Matrices[MATRIX_VIEW][2][2] + )); + v_vEye.w = (gm_Matrices[MATRIX_PROJECTION][2][3] == 0.0) ? 1.0 : 0.0; + + v_mTBN = mat3(tangent, bitangent, normal); + + //////////////////////////////////////////////////////////////////////////// + // Vertex position in shadowmap + if (bbmod_ShadowmapEnableVS == 1.0) + { + v_vPosShadowmap = bbmod_ShadowmapMatrix + * vec4(v_vVertex + normal * bbmod_ShadowmapNormalOffset, 1.0); + } +} diff --git a/shaders/BBMOD_ShDefaultBatched/BBMOD_ShDefaultBatched.yy b/shaders/BBMOD_ShDefaultBatched/BBMOD_ShDefaultBatched.yy new file mode 100644 index 000000000..ea4476efc --- /dev/null +++ b/shaders/BBMOD_ShDefaultBatched/BBMOD_ShDefaultBatched.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefaultBatched", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShDefaultDepth/BBMOD_ShDefaultDepth.fsh b/shaders/BBMOD_ShDefaultDepth/BBMOD_ShDefaultDepth.fsh new file mode 100644 index 000000000..aae83911a --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepth/BBMOD_ShDefaultDepth.fsh @@ -0,0 +1,275 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} + +void DepthShader(float depth) +{ + gl_FragColor.rgb = xEncodeDepth(depth / bbmod_ZFar); + gl_FragColor.a = 1.0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + DepthShader(v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefaultDepth/BBMOD_ShDefaultDepth.vsh b/shaders/BBMOD_ShDefaultDepth/BBMOD_ShDefaultDepth.vsh new file mode 100644 index 000000000..2da02540c --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepth/BBMOD_ShDefaultDepth.vsh @@ -0,0 +1,90 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_mTBN = mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShDefaultDepth/BBMOD_ShDefaultDepth.yy b/shaders/BBMOD_ShDefaultDepth/BBMOD_ShDefaultDepth.yy new file mode 100644 index 000000000..f673bf674 --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepth/BBMOD_ShDefaultDepth.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefaultDepth", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShDefaultDepthAnimated/BBMOD_ShDefaultDepthAnimated.fsh b/shaders/BBMOD_ShDefaultDepthAnimated/BBMOD_ShDefaultDepthAnimated.fsh new file mode 100644 index 000000000..aae83911a --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepthAnimated/BBMOD_ShDefaultDepthAnimated.fsh @@ -0,0 +1,275 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} + +void DepthShader(float depth) +{ + gl_FragColor.rgb = xEncodeDepth(depth / bbmod_ZFar); + gl_FragColor.a = 1.0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + DepthShader(v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefaultDepthAnimated/BBMOD_ShDefaultDepthAnimated.vsh b/shaders/BBMOD_ShDefaultDepthAnimated/BBMOD_ShDefaultDepthAnimated.vsh new file mode 100644 index 000000000..238b0254b --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepthAnimated/BBMOD_ShDefaultDepthAnimated.vsh @@ -0,0 +1,146 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +attribute vec4 in_BoneIndex; +attribute vec4 in_BoneWeight; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +uniform vec4 bbmod_Bones[2 * BBMOD_MAX_BONES]; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +vec3 QuaternionRotate(vec4 q, vec3 v) +{ + return (v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v)); +} + +vec3 DualQuaternionTransform(vec4 real, vec4 dual, vec3 v) +{ + return (QuaternionRotate(real, v) + + 2.0 * (real.w * dual.xyz - dual.w * real.xyz + cross(real.xyz, dual.xyz))); +} + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + // Source: + // https://www.cs.utah.edu/~ladislav/kavan07skinning/kavan07skinning.pdf + // https://www.cs.utah.edu/~ladislav/dq/dqs.cg + ivec4 i = ivec4(in_BoneIndex) * 2; + ivec4 j = i + 1; + + vec4 real0 = bbmod_Bones[i.x]; + vec4 real1 = bbmod_Bones[i.y]; + vec4 real2 = bbmod_Bones[i.z]; + vec4 real3 = bbmod_Bones[i.w]; + + vec4 dual0 = bbmod_Bones[j.x]; + vec4 dual1 = bbmod_Bones[j.y]; + vec4 dual2 = bbmod_Bones[j.z]; + vec4 dual3 = bbmod_Bones[j.w]; + + if (dot(real0, real1) < 0.0) { real1 *= -1.0; dual1 *= -1.0; } + if (dot(real0, real2) < 0.0) { real2 *= -1.0; dual2 *= -1.0; } + if (dot(real0, real3) < 0.0) { real3 *= -1.0; dual3 *= -1.0; } + + vec4 blendReal = + real0 * in_BoneWeight.x + + real1 * in_BoneWeight.y + + real2 * in_BoneWeight.z + + real3 * in_BoneWeight.w; + + vec4 blendDual = + dual0 * in_BoneWeight.x + + dual1 * in_BoneWeight.y + + dual2 * in_BoneWeight.z + + dual3 * in_BoneWeight.w; + + float len = length(blendReal); + blendReal /= len; + blendDual /= len; + + vertex = vec4(DualQuaternionTransform(blendReal, blendDual, vertex.xyz), 1.0); + normal = QuaternionRotate(blendReal, normal); + tangent = QuaternionRotate(blendReal, tangent); + bitangent = QuaternionRotate(blendReal, bitangent); + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_mTBN = mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShDefaultDepthAnimated/BBMOD_ShDefaultDepthAnimated.yy b/shaders/BBMOD_ShDefaultDepthAnimated/BBMOD_ShDefaultDepthAnimated.yy new file mode 100644 index 000000000..6d51f3908 --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepthAnimated/BBMOD_ShDefaultDepthAnimated.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefaultDepthAnimated", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShDefaultDepthBatched/BBMOD_ShDefaultDepthBatched.fsh b/shaders/BBMOD_ShDefaultDepthBatched/BBMOD_ShDefaultDepthBatched.fsh new file mode 100644 index 000000000..aae83911a --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepthBatched/BBMOD_ShDefaultDepthBatched.fsh @@ -0,0 +1,275 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} + +void DepthShader(float depth) +{ + gl_FragColor.rgb = xEncodeDepth(depth / bbmod_ZFar); + gl_FragColor.a = 1.0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + DepthShader(v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefaultDepthBatched/BBMOD_ShDefaultDepthBatched.vsh b/shaders/BBMOD_ShDefaultDepthBatched/BBMOD_ShDefaultDepthBatched.vsh new file mode 100644 index 000000000..0ee29413a --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepthBatched/BBMOD_ShDefaultDepthBatched.vsh @@ -0,0 +1,107 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +attribute float in_Id; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +uniform vec4 bbmod_BatchData[BBMOD_MAX_BATCH_VEC4S]; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +vec3 QuaternionRotate(vec4 q, vec3 v) +{ + return (v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v)); +} + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); + + int idx = int(in_Id) * 3; + vec4 posScale = bbmod_BatchData[idx]; + vec4 rot = bbmod_BatchData[idx + 1]; + + vertex = vec4(posScale.xyz + (QuaternionRotate(rot, vertex.xyz) * posScale.w), 1.0); + normal = QuaternionRotate(rot, normal); + tangent = QuaternionRotate(rot, tangent); + bitangent = QuaternionRotate(rot, bitangent); + +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_mTBN = mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShDefaultDepthBatched/BBMOD_ShDefaultDepthBatched.yy b/shaders/BBMOD_ShDefaultDepthBatched/BBMOD_ShDefaultDepthBatched.yy new file mode 100644 index 000000000..56da1486b --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepthBatched/BBMOD_ShDefaultDepthBatched.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefaultDepthBatched", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShDefaultDepthLightmap/BBMOD_ShDefaultDepthLightmap.fsh b/shaders/BBMOD_ShDefaultDepthLightmap/BBMOD_ShDefaultDepthLightmap.fsh new file mode 100644 index 000000000..2d35eb798 --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepthLightmap/BBMOD_ShDefaultDepthLightmap.fsh @@ -0,0 +1,278 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying vec2 v_vTexCoord2; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// RGBA: RGBM encoded lightmap +uniform sampler2D bbmod_Lightmap; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texEmissive, + sampler2D texLightmap, + vec2 uvLightmap, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + // Lightmap + m.Lightmap = xGammaToLinear(xDecodeRGBM(texture2D(texLightmap, uvLightmap))); + + return m; +} + +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} + +void DepthShader(float depth) +{ + gl_FragColor.rgb = xEncodeDepth(depth / bbmod_ZFar); + gl_FragColor.a = 1.0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Emissive, + bbmod_Lightmap, + v_vTexCoord2, + v_mTBN, + v_vTexCoord); + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + DepthShader(v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefaultDepthLightmap/BBMOD_ShDefaultDepthLightmap.vsh b/shaders/BBMOD_ShDefaultDepthLightmap/BBMOD_ShDefaultDepthLightmap.vsh new file mode 100644 index 000000000..50aa10f82 --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepthLightmap/BBMOD_ShDefaultDepthLightmap.vsh @@ -0,0 +1,93 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; +attribute vec2 in_TextureCoord1; + +attribute vec4 in_TangentW; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying vec2 v_vTexCoord2; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + v_vTexCoord2 = in_TextureCoord1; + + v_mTBN = mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShDefaultDepthLightmap/BBMOD_ShDefaultDepthLightmap.yy b/shaders/BBMOD_ShDefaultDepthLightmap/BBMOD_ShDefaultDepthLightmap.yy new file mode 100644 index 000000000..8232bd92b --- /dev/null +++ b/shaders/BBMOD_ShDefaultDepthLightmap/BBMOD_ShDefaultDepthLightmap.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefaultDepthLightmap", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShDefaultLightmap/BBMOD_ShDefaultLightmap.fsh b/shaders/BBMOD_ShDefaultLightmap/BBMOD_ShDefaultLightmap.fsh new file mode 100644 index 000000000..729097b0e --- /dev/null +++ b/shaders/BBMOD_ShDefaultLightmap/BBMOD_ShDefaultLightmap.fsh @@ -0,0 +1,807 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying vec2 v_vTexCoord2; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// RGBA: RGBM encoded lightmap +uniform sampler2D bbmod_Lightmap; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// SSAO + +// SSAO texture +uniform sampler2D bbmod_SSAO; + +//////////////////////////////////////////////////////////////////////////////// +// Image based lighting + +// Prefiltered octahedron env. map +uniform sampler2D bbmod_IBL; +// Texel size of one octahedron +uniform vec2 bbmod_IBLTexel; + +//////////////////////////////////////////////////////////////////////////////// +// Punctual lights + +// [(x, y, z, range), (r, g, b, m), ...] +uniform vec4 bbmod_LightPunctualDataA[2 * MAX_PUNCTUAL_LIGHTS]; +// [(isSpotLight, dcosInner, dcosOuter), (dX, dY, dZ), ...] +uniform vec3 bbmod_LightPunctualDataB[2 * MAX_PUNCTUAL_LIGHTS]; + +//////////////////////////////////////////////////////////////////////////////// +// Shadow mapping + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnablePS; +// Shadowmap texture +uniform sampler2D bbmod_Shadowmap; +// (1.0/shadowmapWidth, 1.0/shadowmapHeight) +uniform vec2 bbmod_ShadowmapTexel; +// The area that the shadowmap captures +uniform float bbmod_ShadowmapArea; +// The range over which meshes smoothly transition into shadow. +uniform float bbmod_ShadowmapBias; +// The index of the light that casts shadows. Use -1 for the directional light. +uniform float bbmod_ShadowCasterIndex; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texEmissive, + sampler2D texLightmap, + vec2 uvLightmap, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + // Lightmap + m.Lightmap = xGammaToLinear(xDecodeRGBM(texture2D(texLightmap, uvLightmap))); + + return m; +} + +/// @param subsurface Color in RGB and thickness/intensity in A. +/// @source https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/ +vec3 xCheapSubsurface(vec4 subsurface, vec3 eye, vec3 normal, vec3 light, vec3 lightColor) +{ + const float fLTPower = 1.0; + const float fLTScale = 1.0; + vec3 vLTLight = light + normal; + float fLTDot = pow(clamp(dot(eye, -vLTLight), 0.0, 1.0), fLTPower) * fLTScale; + float fLT = fLTDot * subsurface.a; + return subsurface.rgb * lightColor * fLT; +} +#define X_PI 3.14159265359 +#define X_2_PI 6.28318530718 + +/// @return x^2 +#define xPow2(x) ((x) * (x)) + +/// @return x^3 +#define xPow3(x) ((x) * (x) * (x)) + +/// @return x^4 +#define xPow4(x) ((x) * (x) * (x) * (x)) + +/// @return x^5 +#define xPow5(x) ((x) * (x) * (x) * (x) * (x)) + +/// @return arctan2(x,y) +#define xAtan2(x, y) atan(y, x) + +/// @return Direction from point `from` to point `to` in degrees (0-360 range). +float xPointDirection(vec2 from, vec2 to) +{ + float x = xAtan2(from.x - to.x, from.y - to.y); + return ((x > 0.0) ? x : (2.0 * X_PI + x)) * 180.0 / X_PI; +} + +/// @desc Default specular color for dielectrics +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +#define X_F0_DEFAULT vec3(0.04, 0.04, 0.04) + +/// @desc Normal distribution function +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularD_GGX(float roughness, float NdotH) +{ + float r = xPow4(roughness); + float a = NdotH * NdotH * (r - 1.0) + 1.0; + return r / (X_PI * a * a); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xSpecularD_Approx(float roughness, float RdotL) +{ + float a = roughness * roughness; + float a2 = a * a; + float rcp_a2 = 1.0 / a2; + // 0.5 / ln(2), 0.275 / ln(2) + float c = (0.72134752 * rcp_a2) + 0.39674113; + return (rcp_a2 * exp2((c * RdotL) - c)); +} + +/// @desc Roughness remapping for analytic lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_Analytic(float roughness) +{ + return xPow2(roughness + 1.0) * 0.125; +} + +/// @desc Roughness remapping for IBL lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_IBL(float roughness) +{ + return xPow2(roughness) * 0.5; +} + +/// @desc Geometric attenuation +/// @param k Use either xK_Analytic for analytic lights or xK_IBL for image based lighting. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularG_Schlick(float k, float NdotL, float NdotV) +{ + return (NdotL / (NdotL * (1.0 - k) + k)) + * (NdotV / (NdotV * (1.0 - k) + k)); +} + +/// @desc Fresnel +/// @source https://en.wikipedia.org/wiki/Schlick%27s_approximation +vec3 xSpecularF_Schlick(vec3 f0, float VdotH) +{ + return f0 + (1.0 - f0) * xPow5(1.0 - VdotH); +} + +/// @desc Cook-Torrance microfacet specular shading +/// @note N = normalize(vertexNormal) +/// L = normalize(light - vertex) +/// V = normalize(camera - vertex) +/// H = normalize(L + V) +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xBRDF(vec3 f0, float roughness, float NdotL, float NdotV, float NdotH, float VdotH) +{ + vec3 specular = xSpecularD_GGX(roughness, NdotH) + * xSpecularF_Schlick(f0, VdotH) + * xSpecularG_Schlick(xK_Analytic(roughness), NdotL, NdotH); + return specular / ((4.0 * NdotL * NdotV) + 0.1); +} + +vec3 SpecularGGX(Material m, vec3 N, vec3 V, vec3 L) +{ + vec3 H = normalize(L + V); + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + float NdotH = max(dot(N, H), 0.0); + float VdotH = max(dot(V, H), 0.0); + return xBRDF(m.Specular, m.Roughness, NdotL, NdotV, NdotH, VdotH); +} + +void DoDirectionalLightPS( + vec3 direction, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = normalize(-direction); + float NdotL = max(dot(N, L), 0.0); + color *= (1.0 - shadow) * NdotL; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoPointLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + att *= att; + float NdotL = max(dot(N, L), 0.0); + color *= (1.0 - shadow) * NdotL * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoSpotLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 direction, + float dcosInner, + float dcosOuter, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + float theta = dot(L, normalize(-direction)); + float epsilon = dcosInner - dcosOuter; + float intensity = clamp((theta - dcosOuter) / epsilon, 0.0, 1.0); + color *= (1.0 - shadow) * intensity * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} +// Source: https://gamedev.stackexchange.com/questions/169508/octahedral-impostors-octahedral-mapping + +/// @param dir Sampling dir vector in world-space. +/// @return UV coordinates on an octahedron map. +vec2 xVec3ToOctahedronUv(vec3 dir) +{ + vec3 octant = sign(dir); + float sum = dot(dir, octant); + vec3 octahedron = dir / sum; + if (octahedron.z < 0.0) + { + vec3 absolute = abs(octahedron); + octahedron.xy = octant.xy * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return octahedron.xy * 0.5 + 0.5; +} + +/// @desc Converts octahedron UV into a world-space vector. +vec3 xOctahedronUvToVec3Normalized(vec2 uv) +{ + vec3 position = vec3(2.0 * (uv - 0.5), 0); + vec2 absolute = abs(position.xy); + position.z = 1.0 - absolute.x - absolute.y; + if (position.z < 0.0) + { + position.xy = sign(position.xy) * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return position; +} + +vec3 xDiffuseIBL(sampler2D ibl, vec2 texel, vec3 N) +{ + const float s = 1.0 / 8.0; + const float r2 = 7.0; + + vec2 uv0 = xVec3ToOctahedronUv(N); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + return xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +vec2 xEnvBRDFApprox(float roughness, float NdotV) +{ + const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); + vec4 r = (roughness * c0) + c1; + float a004 = (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; + return ((vec2(-1.04, 1.04) * a004) + r.zw); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xEnvBRDFApproxNonmetal(float roughness, float NdotV) +{ + // Same as EnvBRDFApprox(0.04, Roughness, NdotV) + const vec2 c0 = vec2(-1.0, -0.0275); + const vec2 c1 = vec2(1.0, 0.0425); + vec2 r = (roughness * c0) + c1; + return (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; +} + +// Fully rough optimization: +// xEnvBRDFApprox(SpecularColor, 1, 1) == SpecularColor * 0.4524 - 0.0024 +// DiffuseColor += SpecularColor * 0.45; +// SpecularColor = 0.0; + +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xSpecularIBL(sampler2D ibl, vec2 texel/*, sampler2D brdf*/, vec3 f0, float roughness, vec3 N, vec3 V) +{ + float NdotV = clamp(dot(N, V), 0.0, 1.0); + vec3 R = 2.0 * dot(V, N) * N - V; + // vec2 envBRDF = texture2D(brdf, vec2(roughness, NdotV)).xy; + vec2 envBRDF = xEnvBRDFApprox(roughness, NdotV); + + const float s = 1.0 / 8.0; + float r = roughness * 7.0; + float r2 = floor(r); + float rDiff = r - r2; + + vec2 uv0 = xVec3ToOctahedronUv(R); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + vec2 uv1 = uv0; + uv1.x = uv1.x + s; + + vec3 specular = f0 * envBRDF.x + envBRDF.y; + + vec3 col0 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))) * specular; + vec3 col1 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv1))) * specular; + + return mix(col0, col1, rDiff); +} +/// @param tanAspect (tanFovY*(screenWidth/screenHeight),-tanFovY), where +/// tanFovY = dtan(fov*0.5) +/// @param texCoord Sceen-space UV. +/// @param depth Scene depth at texCoord. +/// @return Point projected to view-space. +vec3 xProject(vec2 tanAspect, vec2 texCoord, float depth) +{ + return vec3(tanAspect * (texCoord * 2.0 - 1.0) * depth, depth); +} + +/// @param p A point in clip space (transformed by projection matrix, but not +/// normalized). +/// @return P's UV coordinates on the screen. +vec2 xUnproject(vec4 p) +{ + vec2 uv = p.xy / p.w; + uv = uv * 0.5 + 0.5; +#if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + uv.y = 1.0 - uv.y; +#endif + return uv; +} +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} +// Shadowmap filtering source: https://www.gamedev.net/tutorials/programming/graphics/contact-hardening-soft-shadows-made-fast-r4906/ +float InterleavedGradientNoise(vec2 positionScreen) +{ + vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); + return fract(magic.z * fract(dot(positionScreen, magic.xy))); +} +vec2 VogelDiskSample(int sampleIndex, int samplesCount, float phi) +{ + float GoldenAngle = 2.4; + float r = sqrt(float(sampleIndex) + 0.5) / sqrt(float(samplesCount)); + float theta = float(sampleIndex) * GoldenAngle + phi; + float sine = sin(theta); + float cosine = cos(theta); + return vec2(r * cosine, r * sine); +} + +float ShadowMap(sampler2D shadowMap, vec2 texel, vec2 uv, float compareZ) +{ + if (clamp(uv.xy, vec2(0.0), vec2(1.0)) != uv.xy) + { + return 0.0; + } + float shadow = 0.0; + float noise = 6.28 * InterleavedGradientNoise(gl_FragCoord.xy); + float bias = bbmod_ShadowmapBias / bbmod_ShadowmapArea; + for (int i = 0; i < SHADOWMAP_SAMPLE_COUNT; ++i) + { + vec2 uv2 = uv + VogelDiskSample(i, SHADOWMAP_SAMPLE_COUNT, noise) * texel * 4.0; + float depth = xDecodeDepth(texture2D(shadowMap, uv2).rgb); + if (bias != 0.0) + { + shadow += clamp((compareZ - depth) / bias, 0.0, 1.0); + } + else + { + shadow += step(depth, compareZ); + } + } + return (shadow / float(SHADOWMAP_SAMPLE_COUNT)); +} + +void PBRShader(Material material, float depth) +{ + vec3 N = material.Normal; + vec3 V = (v_vEye.w == 1.0) ? v_vEye.xyz : normalize(bbmod_CamPos - v_vVertex); + vec3 lightDiffuse = vec3(0.0); + vec3 lightSpecular = vec3(0.0); + vec3 lightSubsurface = vec3(0.0); + + // Ambient light + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + lightDiffuse += mix(ambientDown, ambientUp, N.z * 0.5 + 0.5); + + // Shadow mapping + float shadow = 0.0; + if (bbmod_ShadowmapEnablePS == 1.0) + { + vec4 shadowmapPos = v_vPosShadowmap; + shadowmapPos.xy /= shadowmapPos.w; + float shadowmapAtt = (bbmod_ShadowCasterIndex == -1.0) + ? clamp((1.0 - length(shadowmapPos.xy)) / 0.1, 0.0, 1.0) + : 1.0; + shadowmapPos.xy = shadowmapPos.xy * 0.5 + 0.5; + #if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + shadowmapPos.y = 1.0 - shadowmapPos.y; + #endif + shadowmapPos.z /= bbmod_ShadowmapArea; + + shadow = ShadowMap(bbmod_Shadowmap, bbmod_ShadowmapTexel, shadowmapPos.xy, shadowmapPos.z) + * shadowmapAtt; + } + + // IBL + lightDiffuse += xDiffuseIBL(bbmod_IBL, bbmod_IBLTexel, N); + lightSpecular += xSpecularIBL(bbmod_IBL, bbmod_IBLTexel, material.Specular, material.Roughness, N, V); + // TODO: Subsurface scattering for IBL + + // Directional light + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + DoDirectionalLightPS( + bbmod_LightDirectionalDir, + directionalLightColor, + (bbmod_ShadowCasterIndex == -1.0) ? shadow : 0.0, + v_vVertex, N, V, material, lightDiffuse, lightSpecular, lightSubsurface); + + // SSAO + float ssao = texture2D(bbmod_SSAO, xUnproject(v_vPosition)).r; + lightDiffuse *= ssao; + lightSpecular *= ssao; + + // Punctual lights + for (int i = 0; i < MAX_PUNCTUAL_LIGHTS; ++i) + { + vec4 positionRange = bbmod_LightPunctualDataA[i * 2]; + vec4 colorAlpha = bbmod_LightPunctualDataA[(i * 2) + 1]; + vec3 isSpotInnerOuter = bbmod_LightPunctualDataB[i * 2]; + vec3 direction = bbmod_LightPunctualDataB[(i * 2) + 1]; + vec3 color = xGammaToLinear(colorAlpha.rgb) * colorAlpha.a; + + if (isSpotInnerOuter.x == 1.0) + { + DoSpotLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + direction, isSpotInnerOuter.y, isSpotInnerOuter.z, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + else + { + DoPointLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + } + + // Lightmap + lightDiffuse += material.Lightmap; + + // Diffuse + gl_FragColor.rgb = material.Base * lightDiffuse; + // Specular + gl_FragColor.rgb += lightSpecular; + // Ambient occlusion + gl_FragColor.rgb *= material.AO; + // Emissive + gl_FragColor.rgb += material.Emissive; + // Subsurface scattering + gl_FragColor.rgb += lightSubsurface; + // Opacity + gl_FragColor.a = material.Opacity; + // Soft particles + // Fog + Fog(depth); + + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Emissive, + bbmod_Lightmap, + v_vTexCoord2, + v_mTBN, + v_vTexCoord); + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + PBRShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefaultLightmap/BBMOD_ShDefaultLightmap.vsh b/shaders/BBMOD_ShDefaultLightmap/BBMOD_ShDefaultLightmap.vsh new file mode 100644 index 000000000..14e1f4e78 --- /dev/null +++ b/shaders/BBMOD_ShDefaultLightmap/BBMOD_ShDefaultLightmap.vsh @@ -0,0 +1,118 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; +attribute vec2 in_TextureCoord1; + +attribute vec4 in_TangentW; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnableVS; +// WORLD_VIEW_PROJECTION matrix used when rendering shadowmap +uniform mat4 bbmod_ShadowmapMatrix; +// Offsets vertex position by its normal scaled by this value +uniform float bbmod_ShadowmapNormalOffset; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying vec2 v_vTexCoord2; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + v_vTexCoord2 = in_TextureCoord1; + + v_vEye.xyz = normalize(-vec3( + gm_Matrices[MATRIX_VIEW][0][2], + gm_Matrices[MATRIX_VIEW][1][2], + gm_Matrices[MATRIX_VIEW][2][2] + )); + v_vEye.w = (gm_Matrices[MATRIX_PROJECTION][2][3] == 0.0) ? 1.0 : 0.0; + + v_mTBN = mat3(tangent, bitangent, normal); + + //////////////////////////////////////////////////////////////////////////// + // Vertex position in shadowmap + if (bbmod_ShadowmapEnableVS == 1.0) + { + v_vPosShadowmap = bbmod_ShadowmapMatrix + * vec4(v_vVertex + normal * bbmod_ShadowmapNormalOffset, 1.0); + } +} diff --git a/shaders/BBMOD_ShDefaultLightmap/BBMOD_ShDefaultLightmap.yy b/shaders/BBMOD_ShDefaultLightmap/BBMOD_ShDefaultLightmap.yy new file mode 100644 index 000000000..588cfca39 --- /dev/null +++ b/shaders/BBMOD_ShDefaultLightmap/BBMOD_ShDefaultLightmap.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefaultLightmap", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShDefaultSprite/BBMOD_ShDefaultSprite.fsh b/shaders/BBMOD_ShDefaultSprite/BBMOD_ShDefaultSprite.fsh new file mode 100644 index 000000000..77d315a7b --- /dev/null +++ b/shaders/BBMOD_ShDefaultSprite/BBMOD_ShDefaultSprite.fsh @@ -0,0 +1,798 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec4 v_vColor; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// UVs of the BaseOpacity texture +uniform vec4 bbmod_BaseOpacityUV; +// UVs of the NormalW texture +uniform vec4 bbmod_NormalWUV; +// UVs of the Material texture +uniform vec4 bbmod_MaterialUV; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// SSAO + +// SSAO texture +uniform sampler2D bbmod_SSAO; + +//////////////////////////////////////////////////////////////////////////////// +// Image based lighting + +// Prefiltered octahedron env. map +uniform sampler2D bbmod_IBL; +// Texel size of one octahedron +uniform vec2 bbmod_IBLTexel; + +//////////////////////////////////////////////////////////////////////////////// +// Punctual lights + +// [(x, y, z, range), (r, g, b, m), ...] +uniform vec4 bbmod_LightPunctualDataA[2 * MAX_PUNCTUAL_LIGHTS]; +// [(isSpotLight, dcosInner, dcosOuter), (dX, dY, dZ), ...] +uniform vec3 bbmod_LightPunctualDataB[2 * MAX_PUNCTUAL_LIGHTS]; + +//////////////////////////////////////////////////////////////////////////////// +// Shadow mapping + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnablePS; +// Shadowmap texture +uniform sampler2D bbmod_Shadowmap; +// (1.0/shadowmapWidth, 1.0/shadowmapHeight) +uniform vec2 bbmod_ShadowmapTexel; +// The area that the shadowmap captures +uniform float bbmod_ShadowmapArea; +// The range over which meshes smoothly transition into shadow. +uniform float bbmod_ShadowmapBias; +// The index of the light that casts shadows. Use -1 for the directional light. +uniform float bbmod_ShadowCasterIndex; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + mix(bbmod_BaseOpacityUV.xy, bbmod_BaseOpacityUV.zw, uv) + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + mix(bbmod_NormalWUV.xy, bbmod_NormalWUV.zw, uv) + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + mix(bbmod_MaterialUV.xy, bbmod_MaterialUV.zw, uv) + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +/// @param subsurface Color in RGB and thickness/intensity in A. +/// @source https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/ +vec3 xCheapSubsurface(vec4 subsurface, vec3 eye, vec3 normal, vec3 light, vec3 lightColor) +{ + const float fLTPower = 1.0; + const float fLTScale = 1.0; + vec3 vLTLight = light + normal; + float fLTDot = pow(clamp(dot(eye, -vLTLight), 0.0, 1.0), fLTPower) * fLTScale; + float fLT = fLTDot * subsurface.a; + return subsurface.rgb * lightColor * fLT; +} +#define X_PI 3.14159265359 +#define X_2_PI 6.28318530718 + +/// @return x^2 +#define xPow2(x) ((x) * (x)) + +/// @return x^3 +#define xPow3(x) ((x) * (x) * (x)) + +/// @return x^4 +#define xPow4(x) ((x) * (x) * (x) * (x)) + +/// @return x^5 +#define xPow5(x) ((x) * (x) * (x) * (x) * (x)) + +/// @return arctan2(x,y) +#define xAtan2(x, y) atan(y, x) + +/// @return Direction from point `from` to point `to` in degrees (0-360 range). +float xPointDirection(vec2 from, vec2 to) +{ + float x = xAtan2(from.x - to.x, from.y - to.y); + return ((x > 0.0) ? x : (2.0 * X_PI + x)) * 180.0 / X_PI; +} + +/// @desc Default specular color for dielectrics +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +#define X_F0_DEFAULT vec3(0.04, 0.04, 0.04) + +/// @desc Normal distribution function +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularD_GGX(float roughness, float NdotH) +{ + float r = xPow4(roughness); + float a = NdotH * NdotH * (r - 1.0) + 1.0; + return r / (X_PI * a * a); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xSpecularD_Approx(float roughness, float RdotL) +{ + float a = roughness * roughness; + float a2 = a * a; + float rcp_a2 = 1.0 / a2; + // 0.5 / ln(2), 0.275 / ln(2) + float c = (0.72134752 * rcp_a2) + 0.39674113; + return (rcp_a2 * exp2((c * RdotL) - c)); +} + +/// @desc Roughness remapping for analytic lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_Analytic(float roughness) +{ + return xPow2(roughness + 1.0) * 0.125; +} + +/// @desc Roughness remapping for IBL lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_IBL(float roughness) +{ + return xPow2(roughness) * 0.5; +} + +/// @desc Geometric attenuation +/// @param k Use either xK_Analytic for analytic lights or xK_IBL for image based lighting. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularG_Schlick(float k, float NdotL, float NdotV) +{ + return (NdotL / (NdotL * (1.0 - k) + k)) + * (NdotV / (NdotV * (1.0 - k) + k)); +} + +/// @desc Fresnel +/// @source https://en.wikipedia.org/wiki/Schlick%27s_approximation +vec3 xSpecularF_Schlick(vec3 f0, float VdotH) +{ + return f0 + (1.0 - f0) * xPow5(1.0 - VdotH); +} + +/// @desc Cook-Torrance microfacet specular shading +/// @note N = normalize(vertexNormal) +/// L = normalize(light - vertex) +/// V = normalize(camera - vertex) +/// H = normalize(L + V) +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xBRDF(vec3 f0, float roughness, float NdotL, float NdotV, float NdotH, float VdotH) +{ + vec3 specular = xSpecularD_GGX(roughness, NdotH) + * xSpecularF_Schlick(f0, VdotH) + * xSpecularG_Schlick(xK_Analytic(roughness), NdotL, NdotH); + return specular / ((4.0 * NdotL * NdotV) + 0.1); +} + +vec3 SpecularGGX(Material m, vec3 N, vec3 V, vec3 L) +{ + vec3 H = normalize(L + V); + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + float NdotH = max(dot(N, H), 0.0); + float VdotH = max(dot(V, H), 0.0); + return xBRDF(m.Specular, m.Roughness, NdotL, NdotV, NdotH, VdotH); +} + +void DoDirectionalLightPS( + vec3 direction, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = normalize(-direction); + float NdotL = max(dot(N, L), 0.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * NdotL; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoPointLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + att *= att; + float NdotL = max(dot(N, L), 0.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * NdotL * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoSpotLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 direction, + float dcosInner, + float dcosOuter, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + float theta = dot(L, normalize(-direction)); + float epsilon = dcosInner - dcosOuter; + float intensity = clamp((theta - dcosOuter) / epsilon, 0.0, 1.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * intensity * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} +// Source: https://gamedev.stackexchange.com/questions/169508/octahedral-impostors-octahedral-mapping + +/// @param dir Sampling dir vector in world-space. +/// @return UV coordinates on an octahedron map. +vec2 xVec3ToOctahedronUv(vec3 dir) +{ + vec3 octant = sign(dir); + float sum = dot(dir, octant); + vec3 octahedron = dir / sum; + if (octahedron.z < 0.0) + { + vec3 absolute = abs(octahedron); + octahedron.xy = octant.xy * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return octahedron.xy * 0.5 + 0.5; +} + +/// @desc Converts octahedron UV into a world-space vector. +vec3 xOctahedronUvToVec3Normalized(vec2 uv) +{ + vec3 position = vec3(2.0 * (uv - 0.5), 0); + vec2 absolute = abs(position.xy); + position.z = 1.0 - absolute.x - absolute.y; + if (position.z < 0.0) + { + position.xy = sign(position.xy) * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return position; +} + +vec3 xDiffuseIBL(sampler2D ibl, vec2 texel, vec3 N) +{ + const float s = 1.0 / 8.0; + const float r2 = 7.0; + + vec2 uv0 = xVec3ToOctahedronUv(N); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + return xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +vec2 xEnvBRDFApprox(float roughness, float NdotV) +{ + const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); + vec4 r = (roughness * c0) + c1; + float a004 = (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; + return ((vec2(-1.04, 1.04) * a004) + r.zw); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xEnvBRDFApproxNonmetal(float roughness, float NdotV) +{ + // Same as EnvBRDFApprox(0.04, Roughness, NdotV) + const vec2 c0 = vec2(-1.0, -0.0275); + const vec2 c1 = vec2(1.0, 0.0425); + vec2 r = (roughness * c0) + c1; + return (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; +} + +// Fully rough optimization: +// xEnvBRDFApprox(SpecularColor, 1, 1) == SpecularColor * 0.4524 - 0.0024 +// DiffuseColor += SpecularColor * 0.45; +// SpecularColor = 0.0; + +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xSpecularIBL(sampler2D ibl, vec2 texel/*, sampler2D brdf*/, vec3 f0, float roughness, vec3 N, vec3 V) +{ + float NdotV = clamp(dot(N, V), 0.0, 1.0); + vec3 R = 2.0 * dot(V, N) * N - V; + // vec2 envBRDF = texture2D(brdf, vec2(roughness, NdotV)).xy; + vec2 envBRDF = xEnvBRDFApprox(roughness, NdotV); + + const float s = 1.0 / 8.0; + float r = roughness * 7.0; + float r2 = floor(r); + float rDiff = r - r2; + + vec2 uv0 = xVec3ToOctahedronUv(R); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + vec2 uv1 = uv0; + uv1.x = uv1.x + s; + + vec3 specular = f0 * envBRDF.x + envBRDF.y; + + vec3 col0 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))) * specular; + vec3 col1 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv1))) * specular; + + return mix(col0, col1, rDiff); +} +/// @param tanAspect (tanFovY*(screenWidth/screenHeight),-tanFovY), where +/// tanFovY = dtan(fov*0.5) +/// @param texCoord Sceen-space UV. +/// @param depth Scene depth at texCoord. +/// @return Point projected to view-space. +vec3 xProject(vec2 tanAspect, vec2 texCoord, float depth) +{ + return vec3(tanAspect * (texCoord * 2.0 - 1.0) * depth, depth); +} + +/// @param p A point in clip space (transformed by projection matrix, but not +/// normalized). +/// @return P's UV coordinates on the screen. +vec2 xUnproject(vec4 p) +{ + vec2 uv = p.xy / p.w; + uv = uv * 0.5 + 0.5; +#if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + uv.y = 1.0 - uv.y; +#endif + return uv; +} +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} +// Shadowmap filtering source: https://www.gamedev.net/tutorials/programming/graphics/contact-hardening-soft-shadows-made-fast-r4906/ +float InterleavedGradientNoise(vec2 positionScreen) +{ + vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); + return fract(magic.z * fract(dot(positionScreen, magic.xy))); +} +vec2 VogelDiskSample(int sampleIndex, int samplesCount, float phi) +{ + float GoldenAngle = 2.4; + float r = sqrt(float(sampleIndex) + 0.5) / sqrt(float(samplesCount)); + float theta = float(sampleIndex) * GoldenAngle + phi; + float sine = sin(theta); + float cosine = cos(theta); + return vec2(r * cosine, r * sine); +} + +float ShadowMap(sampler2D shadowMap, vec2 texel, vec2 uv, float compareZ) +{ + if (clamp(uv.xy, vec2(0.0), vec2(1.0)) != uv.xy) + { + return 0.0; + } + float shadow = 0.0; + float noise = 6.28 * InterleavedGradientNoise(gl_FragCoord.xy); + float bias = bbmod_ShadowmapBias / bbmod_ShadowmapArea; + for (int i = 0; i < SHADOWMAP_SAMPLE_COUNT; ++i) + { + vec2 uv2 = uv + VogelDiskSample(i, SHADOWMAP_SAMPLE_COUNT, noise) * texel * 4.0; + float depth = xDecodeDepth(texture2D(shadowMap, uv2).rgb); + if (bias != 0.0) + { + shadow += clamp((compareZ - depth) / bias, 0.0, 1.0); + } + else + { + shadow += step(depth, compareZ); + } + } + return (shadow / float(SHADOWMAP_SAMPLE_COUNT)); +} + +void PBRShader(Material material, float depth) +{ + vec3 N = material.Normal; + vec3 V = v_vEye.xyz; + vec3 lightDiffuse = vec3(0.0); + vec3 lightSpecular = vec3(0.0); + vec3 lightSubsurface = vec3(0.0); + + // Ambient light + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + lightDiffuse += mix(ambientDown, ambientUp, N.z * 0.5 + 0.5); + + // Shadow mapping + float shadow = 0.0; + + // IBL + lightDiffuse += xDiffuseIBL(bbmod_IBL, bbmod_IBLTexel, N); + lightSpecular += xSpecularIBL(bbmod_IBL, bbmod_IBLTexel, material.Specular, material.Roughness, N, V); + // TODO: Subsurface scattering for IBL + + // Directional light + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + DoDirectionalLightPS( + bbmod_LightDirectionalDir, + directionalLightColor, + (bbmod_ShadowCasterIndex == -1.0) ? shadow : 0.0, + v_vVertex, N, V, material, lightDiffuse, lightSpecular, lightSubsurface); + + // SSAO + float ssao = texture2D(bbmod_SSAO, xUnproject(v_vPosition)).r; + lightDiffuse *= ssao; + lightSpecular *= ssao; + + // Punctual lights + for (int i = 0; i < MAX_PUNCTUAL_LIGHTS; ++i) + { + vec4 positionRange = bbmod_LightPunctualDataA[i * 2]; + vec4 colorAlpha = bbmod_LightPunctualDataA[(i * 2) + 1]; + vec3 isSpotInnerOuter = bbmod_LightPunctualDataB[i * 2]; + vec3 direction = bbmod_LightPunctualDataB[(i * 2) + 1]; + vec3 color = xGammaToLinear(colorAlpha.rgb) * colorAlpha.a; + + if (isSpotInnerOuter.x == 1.0) + { + DoSpotLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + direction, isSpotInnerOuter.y, isSpotInnerOuter.z, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + else + { + DoPointLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + } + + // Lightmap + + // Diffuse + gl_FragColor.rgb = material.Base * lightDiffuse; + // Specular + gl_FragColor.rgb += lightSpecular; + // Ambient occlusion + gl_FragColor.rgb *= material.AO; + // Emissive + gl_FragColor.rgb += material.Emissive; + // Subsurface scattering + gl_FragColor.rgb += lightSubsurface; + // Opacity + gl_FragColor.a = material.Opacity; + // Soft particles + // Fog + Fog(depth); + + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + material.Base *= v_vColor.rgb; + material.Opacity *= v_vColor.a; + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + PBRShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefaultSprite/BBMOD_ShDefaultSprite.vsh b/shaders/BBMOD_ShDefaultSprite/BBMOD_ShDefaultSprite.vsh new file mode 100644 index 000000000..95288047a --- /dev/null +++ b/shaders/BBMOD_ShDefaultSprite/BBMOD_ShDefaultSprite.vsh @@ -0,0 +1,120 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_Color; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec4 v_vColor; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = vec3(0.0, 0.0, 1.0); + vec3 tangent = vec3(1.0, 0.0, 0.0); + vec3 bitangent = vec3(0.0, 1.0, 0.0); + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vColor = in_Color; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_vEye.xyz = normalize(-vec3( + gm_Matrices[MATRIX_VIEW][0][2], + gm_Matrices[MATRIX_VIEW][1][2], + gm_Matrices[MATRIX_VIEW][2][2] + )); + v_vEye.w = (gm_Matrices[MATRIX_PROJECTION][2][3] == 0.0) ? 1.0 : 0.0; + + v_mTBN = mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShDefaultSprite/BBMOD_ShDefaultSprite.yy b/shaders/BBMOD_ShDefaultSprite/BBMOD_ShDefaultSprite.yy new file mode 100644 index 000000000..b4dec8241 --- /dev/null +++ b/shaders/BBMOD_ShDefaultSprite/BBMOD_ShDefaultSprite.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefaultSprite", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShDefaultUnlit/BBMOD_ShDefaultUnlit.fsh b/shaders/BBMOD_ShDefaultUnlit/BBMOD_ShDefaultUnlit.fsh new file mode 100644 index 000000000..2fd59ff79 --- /dev/null +++ b/shaders/BBMOD_ShDefaultUnlit/BBMOD_ShDefaultUnlit.fsh @@ -0,0 +1,308 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} + +void UnlitShader(Material material, float depth) +{ + gl_FragColor.rgb = material.Base; + gl_FragColor.rgb += material.Emissive; + gl_FragColor.a = material.Opacity; + // Soft particles + Fog(depth); + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + UnlitShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefaultUnlit/BBMOD_ShDefaultUnlit.vsh b/shaders/BBMOD_ShDefaultUnlit/BBMOD_ShDefaultUnlit.vsh new file mode 100644 index 000000000..07baa60f8 --- /dev/null +++ b/shaders/BBMOD_ShDefaultUnlit/BBMOD_ShDefaultUnlit.vsh @@ -0,0 +1,92 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_mTBN = mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShDefaultUnlit/BBMOD_ShDefaultUnlit.yy b/shaders/BBMOD_ShDefaultUnlit/BBMOD_ShDefaultUnlit.yy new file mode 100644 index 000000000..9bfe43dfc --- /dev/null +++ b/shaders/BBMOD_ShDefaultUnlit/BBMOD_ShDefaultUnlit.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefaultUnlit", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShDefaultUnlitAnimated/BBMOD_ShDefaultUnlitAnimated.fsh b/shaders/BBMOD_ShDefaultUnlitAnimated/BBMOD_ShDefaultUnlitAnimated.fsh new file mode 100644 index 000000000..2fd59ff79 --- /dev/null +++ b/shaders/BBMOD_ShDefaultUnlitAnimated/BBMOD_ShDefaultUnlitAnimated.fsh @@ -0,0 +1,308 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} + +void UnlitShader(Material material, float depth) +{ + gl_FragColor.rgb = material.Base; + gl_FragColor.rgb += material.Emissive; + gl_FragColor.a = material.Opacity; + // Soft particles + Fog(depth); + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + UnlitShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefaultUnlitAnimated/BBMOD_ShDefaultUnlitAnimated.vsh b/shaders/BBMOD_ShDefaultUnlitAnimated/BBMOD_ShDefaultUnlitAnimated.vsh new file mode 100644 index 000000000..af46994ff --- /dev/null +++ b/shaders/BBMOD_ShDefaultUnlitAnimated/BBMOD_ShDefaultUnlitAnimated.vsh @@ -0,0 +1,148 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +attribute vec4 in_BoneIndex; +attribute vec4 in_BoneWeight; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +uniform vec4 bbmod_Bones[2 * BBMOD_MAX_BONES]; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +vec3 QuaternionRotate(vec4 q, vec3 v) +{ + return (v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v)); +} + +vec3 DualQuaternionTransform(vec4 real, vec4 dual, vec3 v) +{ + return (QuaternionRotate(real, v) + + 2.0 * (real.w * dual.xyz - dual.w * real.xyz + cross(real.xyz, dual.xyz))); +} + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + // Source: + // https://www.cs.utah.edu/~ladislav/kavan07skinning/kavan07skinning.pdf + // https://www.cs.utah.edu/~ladislav/dq/dqs.cg + ivec4 i = ivec4(in_BoneIndex) * 2; + ivec4 j = i + 1; + + vec4 real0 = bbmod_Bones[i.x]; + vec4 real1 = bbmod_Bones[i.y]; + vec4 real2 = bbmod_Bones[i.z]; + vec4 real3 = bbmod_Bones[i.w]; + + vec4 dual0 = bbmod_Bones[j.x]; + vec4 dual1 = bbmod_Bones[j.y]; + vec4 dual2 = bbmod_Bones[j.z]; + vec4 dual3 = bbmod_Bones[j.w]; + + if (dot(real0, real1) < 0.0) { real1 *= -1.0; dual1 *= -1.0; } + if (dot(real0, real2) < 0.0) { real2 *= -1.0; dual2 *= -1.0; } + if (dot(real0, real3) < 0.0) { real3 *= -1.0; dual3 *= -1.0; } + + vec4 blendReal = + real0 * in_BoneWeight.x + + real1 * in_BoneWeight.y + + real2 * in_BoneWeight.z + + real3 * in_BoneWeight.w; + + vec4 blendDual = + dual0 * in_BoneWeight.x + + dual1 * in_BoneWeight.y + + dual2 * in_BoneWeight.z + + dual3 * in_BoneWeight.w; + + float len = length(blendReal); + blendReal /= len; + blendDual /= len; + + vertex = vec4(DualQuaternionTransform(blendReal, blendDual, vertex.xyz), 1.0); + normal = QuaternionRotate(blendReal, normal); + tangent = QuaternionRotate(blendReal, tangent); + bitangent = QuaternionRotate(blendReal, bitangent); + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_mTBN = mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShDefaultUnlitAnimated/BBMOD_ShDefaultUnlitAnimated.yy b/shaders/BBMOD_ShDefaultUnlitAnimated/BBMOD_ShDefaultUnlitAnimated.yy new file mode 100644 index 000000000..9939daa86 --- /dev/null +++ b/shaders/BBMOD_ShDefaultUnlitAnimated/BBMOD_ShDefaultUnlitAnimated.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefaultUnlitAnimated", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShDefaultUnlitBatched/BBMOD_ShDefaultUnlitBatched.fsh b/shaders/BBMOD_ShDefaultUnlitBatched/BBMOD_ShDefaultUnlitBatched.fsh new file mode 100644 index 000000000..2fd59ff79 --- /dev/null +++ b/shaders/BBMOD_ShDefaultUnlitBatched/BBMOD_ShDefaultUnlitBatched.fsh @@ -0,0 +1,308 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} + +void UnlitShader(Material material, float depth) +{ + gl_FragColor.rgb = material.Base; + gl_FragColor.rgb += material.Emissive; + gl_FragColor.a = material.Opacity; + // Soft particles + Fog(depth); + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + UnlitShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShDefaultUnlitBatched/BBMOD_ShDefaultUnlitBatched.vsh b/shaders/BBMOD_ShDefaultUnlitBatched/BBMOD_ShDefaultUnlitBatched.vsh new file mode 100644 index 000000000..c692853de --- /dev/null +++ b/shaders/BBMOD_ShDefaultUnlitBatched/BBMOD_ShDefaultUnlitBatched.vsh @@ -0,0 +1,109 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +attribute float in_Id; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +uniform vec4 bbmod_BatchData[BBMOD_MAX_BATCH_VEC4S]; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +vec3 QuaternionRotate(vec4 q, vec3 v) +{ + return (v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v)); +} + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); + + int idx = int(in_Id) * 3; + vec4 posScale = bbmod_BatchData[idx]; + vec4 rot = bbmod_BatchData[idx + 1]; + + vertex = vec4(posScale.xyz + (QuaternionRotate(rot, vertex.xyz) * posScale.w), 1.0); + normal = QuaternionRotate(rot, normal); + tangent = QuaternionRotate(rot, tangent); + bitangent = QuaternionRotate(rot, bitangent); + +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_mTBN = mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShDefaultUnlitBatched/BBMOD_ShDefaultUnlitBatched.yy b/shaders/BBMOD_ShDefaultUnlitBatched/BBMOD_ShDefaultUnlitBatched.yy new file mode 100644 index 000000000..b9d3f9a04 --- /dev/null +++ b/shaders/BBMOD_ShDefaultUnlitBatched/BBMOD_ShDefaultUnlitBatched.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShDefaultUnlitBatched", + "parent": { + "name": "Shaders", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Shaders.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShExtractSplatmapLayer/BBMOD_ShExtractSplatmapLayer.fsh b/shaders/BBMOD_ShExtractSplatmapLayer/BBMOD_ShExtractSplatmapLayer.fsh new file mode 100644 index 000000000..6641357d6 --- /dev/null +++ b/shaders/BBMOD_ShExtractSplatmapLayer/BBMOD_ShExtractSplatmapLayer.fsh @@ -0,0 +1,15 @@ +varying vec2 v_vTexCoord; + +uniform sampler2D bbmod_Splatmap; +uniform int bbmod_SplatmapIndex; + +void main() +{ + vec4 splatmap = texture2D(bbmod_Splatmap, v_vTexCoord); + // splatmap[bbmod_SplatmapIndex] does not work in HTML5 + gl_FragColor.rgb = vec3((bbmod_SplatmapIndex == 0) ? splatmap.r + : ((bbmod_SplatmapIndex == 1) ? splatmap.g + : ((bbmod_SplatmapIndex == 2) ? splatmap.b + : splatmap.a))); + gl_FragColor.a = 1.0; +} diff --git a/shaders/BBMOD_ShExtractSplatmapLayer/BBMOD_ShExtractSplatmapLayer.vsh b/shaders/BBMOD_ShExtractSplatmapLayer/BBMOD_ShExtractSplatmapLayer.vsh new file mode 100644 index 000000000..6a6ea8cad --- /dev/null +++ b/shaders/BBMOD_ShExtractSplatmapLayer/BBMOD_ShExtractSplatmapLayer.vsh @@ -0,0 +1,10 @@ +attribute vec4 in_Position; +attribute vec2 in_TextureCoord; + +varying vec2 v_vTexCoord; + +void main() +{ + gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position; + v_vTexCoord = in_TextureCoord; +} diff --git a/shaders/BBMOD_ShExtractSplatmapLayer/BBMOD_ShExtractSplatmapLayer.yy b/shaders/BBMOD_ShExtractSplatmapLayer/BBMOD_ShExtractSplatmapLayer.yy new file mode 100644 index 000000000..ab7b23c48 --- /dev/null +++ b/shaders/BBMOD_ShExtractSplatmapLayer/BBMOD_ShExtractSplatmapLayer.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShExtractSplatmapLayer", + "parent": { + "name": "Terrain", + "path": "folders/_Extensions/BBMOD/Terrain.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShFXAA/BBMOD_ShFXAA.fsh b/shaders/BBMOD_ShFXAA/BBMOD_ShFXAA.fsh new file mode 100644 index 000000000..49c836523 --- /dev/null +++ b/shaders/BBMOD_ShFXAA/BBMOD_ShFXAA.fsh @@ -0,0 +1,60 @@ +// Source: https://www.geeks3d.com/20110405/fxaa-fast-approximate-anti-aliasing-demo-glsl-opengl-test-radeon-geforce/3/ +#define FXAA_REDUCE_MIN (1.0 / 128.0) +#define FXAA_REDUCE_MUL (1.0 / 8.0) +#define FXAA_SPAN_MAX 8.0 + +varying vec4 v_vFragPos; + +uniform vec2 u_vTexelPS; + +/// @param tex Input texture. +/// @param fragPos Output of FXAAFragPos. +/// @param texel vec2(1.0 / textureWidth, 1.0 / textureHeight) +vec4 FXAA(sampler2D tex, vec4 fragPos, vec2 texel) +{ +/*---------------------------------------------------------*/ + vec3 rgbNW = texture2D(tex, fragPos.zw).xyz; + vec3 rgbNE = texture2D(tex, fragPos.zw + vec2(1.0, 0.0) * texel).xyz; + vec3 rgbSW = texture2D(tex, fragPos.zw + vec2(0.0, 1.0) * texel).xyz; + vec3 rgbSE = texture2D(tex, fragPos.zw + vec2(1.0, 1.0) * texel).xyz; + vec3 rgbM = texture2D(tex, fragPos.xy).xyz; +/*---------------------------------------------------------*/ + vec3 luma = vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma); + float lumaNE = dot(rgbNE, luma); + float lumaSW = dot(rgbSW, luma); + float lumaSE = dot(rgbSE, luma); + float lumaM = dot(rgbM, luma); +/*---------------------------------------------------------*/ + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); +/*---------------------------------------------------------*/ + vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); +/*---------------------------------------------------------*/ + float dirReduce = max( + (lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), + FXAA_REDUCE_MIN); + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), + max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), + dir * rcpDirMin)) * texel; +/*--------------------------------------------------------*/ + vec3 rgbA = (1.0 / 2.0) * ( + texture2D(tex, fragPos.xy + dir * (1.0 / 3.0 - 0.5)).xyz + + texture2D(tex, fragPos.xy + dir * (2.0 / 3.0 - 0.5)).xyz); + vec3 rgbB = rgbA * (1.0 / 2.0) + (1.0 / 4.0) * ( + texture2D(tex, fragPos.xy + dir * (0.0 / 3.0 - 0.5)).xyz + + texture2D(tex, fragPos.xy + dir * (3.0 / 3.0 - 0.5)).xyz); + float lumaB = dot(rgbB, luma); + vec4 ret; + ret.xyz = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? rgbA : rgbB; + ret.w = 1.0; + return ret; +} + +void main() +{ + gl_FragColor = FXAA(gm_BaseTexture, v_vFragPos, u_vTexelPS); +} \ No newline at end of file diff --git a/shaders/BBMOD_ShFXAA/BBMOD_ShFXAA.vsh b/shaders/BBMOD_ShFXAA/BBMOD_ShFXAA.vsh new file mode 100644 index 000000000..6d468cf78 --- /dev/null +++ b/shaders/BBMOD_ShFXAA/BBMOD_ShFXAA.vsh @@ -0,0 +1,23 @@ +// Source: https://www.geeks3d.com/20110405/fxaa-fast-approximate-anti-aliasing-demo-glsl-opengl-test-radeon-geforce/3/ +attribute vec4 in_Position; +attribute vec2 in_TextureCoord; + +varying vec4 v_vFragPos; + +uniform vec2 u_vTexelVS; + +/// @param texCoord Texture coordinates. +/// @param texel vec2(1.0 / textureWidth, 1.0 / textureHeight) +vec4 FXAAFragPos(vec2 texCoord, vec2 texel) +{ + vec4 pos; + pos.xy = texCoord; + pos.zw = texCoord - (texel * 0.75); + return pos; +} + +void main() +{ + gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position; + v_vFragPos = FXAAFragPos(in_TextureCoord, u_vTexelVS); +} \ No newline at end of file diff --git a/shaders/BBMOD_ShFXAA/BBMOD_ShFXAA.yy b/shaders/BBMOD_ShFXAA/BBMOD_ShFXAA.yy new file mode 100644 index 000000000..64cc99259 --- /dev/null +++ b/shaders/BBMOD_ShFXAA/BBMOD_ShFXAA.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShFXAA", + "parent": { + "name": "FXAA", + "path": "folders/_Extensions/BBMOD/Rendering/FXAA.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShGizmo/BBMOD_ShGizmo.fsh b/shaders/BBMOD_ShGizmo/BBMOD_ShGizmo.fsh new file mode 100644 index 000000000..90fd88fe2 --- /dev/null +++ b/shaders/BBMOD_ShGizmo/BBMOD_ShGizmo.fsh @@ -0,0 +1,41 @@ +// FIXME: Temporary fix! +precision highp float; + +varying vec3 v_vNormal; +varying vec2 v_vTexCoord; + +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} + +void main() +{ + vec3 base = xGammaToLinear(texture2D(gm_BaseTexture, v_vTexCoord).rgb); + vec3 N = normalize(v_vNormal); + vec3 L = vec3(0.0, 0.0, -1.0); + float light = mix(0.25, 1.0, max(dot(N, L), 0.0)); + gl_FragColor.rgb = base * light; + gl_FragColor.a = 1.0; + GammaCorrect(); +} diff --git a/shaders/BBMOD_ShGizmo/BBMOD_ShGizmo.vsh b/shaders/BBMOD_ShGizmo/BBMOD_ShGizmo.vsh new file mode 100644 index 000000000..2c92b52ef --- /dev/null +++ b/shaders/BBMOD_ShGizmo/BBMOD_ShGizmo.vsh @@ -0,0 +1,17 @@ +// FIXME: Temporary fix! +precision highp float; + +attribute vec4 in_Position; +attribute vec3 in_Normal; +attribute vec2 in_TextureCoord0; +attribute vec4 in_TangentW; + +varying vec3 v_vNormal; +varying vec2 v_vTexCoord; + +void main() +{ + gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position; + v_vNormal = mat3(gm_Matrices[MATRIX_WORLD_VIEW]) * in_Normal; + v_vTexCoord = in_TextureCoord0; +} diff --git a/shaders/BBMOD_ShGizmo/BBMOD_ShGizmo.yy b/shaders/BBMOD_ShGizmo/BBMOD_ShGizmo.yy new file mode 100644 index 000000000..9eb4166d3 --- /dev/null +++ b/shaders/BBMOD_ShGizmo/BBMOD_ShGizmo.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShGizmo", + "parent": { + "name": "Gizmo", + "path": "folders/_Extensions/BBMOD/Gizmo.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShGizmoSelect/BBMOD_ShGizmoSelect.fsh b/shaders/BBMOD_ShGizmoSelect/BBMOD_ShGizmoSelect.fsh new file mode 100644 index 000000000..e3b218f82 --- /dev/null +++ b/shaders/BBMOD_ShGizmoSelect/BBMOD_ShGizmoSelect.fsh @@ -0,0 +1,6 @@ +varying vec2 v_vTexCoord; + +void main() +{ + gl_FragColor = texture2D(gm_BaseTexture, v_vTexCoord); +} diff --git a/shaders/BBMOD_ShGizmoSelect/BBMOD_ShGizmoSelect.vsh b/shaders/BBMOD_ShGizmoSelect/BBMOD_ShGizmoSelect.vsh new file mode 100644 index 000000000..282903f07 --- /dev/null +++ b/shaders/BBMOD_ShGizmoSelect/BBMOD_ShGizmoSelect.vsh @@ -0,0 +1,12 @@ +attribute vec4 in_Position; +attribute vec3 in_Normal; +attribute vec2 in_TextureCoord0; +attribute vec4 in_TangentW; + +varying vec2 v_vTexCoord; + +void main() +{ + gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position; + v_vTexCoord = in_TextureCoord0; +} diff --git a/shaders/BBMOD_ShGizmoSelect/BBMOD_ShGizmoSelect.yy b/shaders/BBMOD_ShGizmoSelect/BBMOD_ShGizmoSelect.yy new file mode 100644 index 000000000..aeb693bd5 --- /dev/null +++ b/shaders/BBMOD_ShGizmoSelect/BBMOD_ShGizmoSelect.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShGizmoSelect", + "parent": { + "name": "Gizmo", + "path": "folders/_Extensions/BBMOD/Gizmo.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShInstanceHighlight/BBMOD_ShInstanceHighlight.fsh b/shaders/BBMOD_ShInstanceHighlight/BBMOD_ShInstanceHighlight.fsh new file mode 100644 index 000000000..04953fa9c --- /dev/null +++ b/shaders/BBMOD_ShInstanceHighlight/BBMOD_ShInstanceHighlight.fsh @@ -0,0 +1,19 @@ +varying vec2 v_vTexCoord; + +uniform vec2 u_vTexel; +uniform vec4 u_vColor; + +float IsInstance(vec2 uv) +{ + return (dot(texture2D(gm_BaseTexture, uv), vec4(1.0)) > 0.0) ? 1.0 : 0.0; +} + +void main() +{ + float x = IsInstance(v_vTexCoord + vec2(-1.0, 0.0) * u_vTexel) + - IsInstance(v_vTexCoord + vec2(+1.0, 0.0) * u_vTexel); + float y = IsInstance(v_vTexCoord + vec2(0.0, -1.0) * u_vTexel) + - IsInstance(v_vTexCoord + vec2(0.0, +1.0) * u_vTexel); + gl_FragColor = u_vColor; + gl_FragColor.a *= sqrt((x * x) + (y * y)); +} diff --git a/shaders/BBMOD_ShInstanceHighlight/BBMOD_ShInstanceHighlight.vsh b/shaders/BBMOD_ShInstanceHighlight/BBMOD_ShInstanceHighlight.vsh new file mode 100644 index 000000000..6a6ea8cad --- /dev/null +++ b/shaders/BBMOD_ShInstanceHighlight/BBMOD_ShInstanceHighlight.vsh @@ -0,0 +1,10 @@ +attribute vec4 in_Position; +attribute vec2 in_TextureCoord; + +varying vec2 v_vTexCoord; + +void main() +{ + gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position; + v_vTexCoord = in_TextureCoord; +} diff --git a/shaders/BBMOD_ShInstanceHighlight/BBMOD_ShInstanceHighlight.yy b/shaders/BBMOD_ShInstanceHighlight/BBMOD_ShInstanceHighlight.yy new file mode 100644 index 000000000..2798eec9c --- /dev/null +++ b/shaders/BBMOD_ShInstanceHighlight/BBMOD_ShInstanceHighlight.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShInstanceHighlight", + "parent": { + "name": "Gizmo", + "path": "folders/_Extensions/BBMOD/Gizmo.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShInstanceID/BBMOD_ShInstanceID.fsh b/shaders/BBMOD_ShInstanceID/BBMOD_ShInstanceID.fsh new file mode 100644 index 000000000..fd6ee3911 --- /dev/null +++ b/shaders/BBMOD_ShInstanceID/BBMOD_ShInstanceID.fsh @@ -0,0 +1,278 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Instance IDs + +// The id of the instance that draws the mesh. +uniform vec4 bbmod_InstanceID; + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + gl_FragColor = bbmod_InstanceID; + +} diff --git a/shaders/BBMOD_ShInstanceID/BBMOD_ShInstanceID.vsh b/shaders/BBMOD_ShInstanceID/BBMOD_ShInstanceID.vsh new file mode 100644 index 000000000..07baa60f8 --- /dev/null +++ b/shaders/BBMOD_ShInstanceID/BBMOD_ShInstanceID.vsh @@ -0,0 +1,92 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_mTBN = mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShInstanceID/BBMOD_ShInstanceID.yy b/shaders/BBMOD_ShInstanceID/BBMOD_ShInstanceID.yy new file mode 100644 index 000000000..3ac84d9a4 --- /dev/null +++ b/shaders/BBMOD_ShInstanceID/BBMOD_ShInstanceID.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShInstanceID", + "parent": { + "name": "Gizmo", + "path": "folders/_Extensions/BBMOD/Gizmo.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShInstanceIDAnimated/BBMOD_ShInstanceIDAnimated.fsh b/shaders/BBMOD_ShInstanceIDAnimated/BBMOD_ShInstanceIDAnimated.fsh new file mode 100644 index 000000000..fd6ee3911 --- /dev/null +++ b/shaders/BBMOD_ShInstanceIDAnimated/BBMOD_ShInstanceIDAnimated.fsh @@ -0,0 +1,278 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Instance IDs + +// The id of the instance that draws the mesh. +uniform vec4 bbmod_InstanceID; + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + gl_FragColor = bbmod_InstanceID; + +} diff --git a/shaders/BBMOD_ShInstanceIDAnimated/BBMOD_ShInstanceIDAnimated.vsh b/shaders/BBMOD_ShInstanceIDAnimated/BBMOD_ShInstanceIDAnimated.vsh new file mode 100644 index 000000000..af46994ff --- /dev/null +++ b/shaders/BBMOD_ShInstanceIDAnimated/BBMOD_ShInstanceIDAnimated.vsh @@ -0,0 +1,148 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +attribute vec4 in_BoneIndex; +attribute vec4 in_BoneWeight; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +uniform vec4 bbmod_Bones[2 * BBMOD_MAX_BONES]; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +vec3 QuaternionRotate(vec4 q, vec3 v) +{ + return (v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v)); +} + +vec3 DualQuaternionTransform(vec4 real, vec4 dual, vec3 v) +{ + return (QuaternionRotate(real, v) + + 2.0 * (real.w * dual.xyz - dual.w * real.xyz + cross(real.xyz, dual.xyz))); +} + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + // Source: + // https://www.cs.utah.edu/~ladislav/kavan07skinning/kavan07skinning.pdf + // https://www.cs.utah.edu/~ladislav/dq/dqs.cg + ivec4 i = ivec4(in_BoneIndex) * 2; + ivec4 j = i + 1; + + vec4 real0 = bbmod_Bones[i.x]; + vec4 real1 = bbmod_Bones[i.y]; + vec4 real2 = bbmod_Bones[i.z]; + vec4 real3 = bbmod_Bones[i.w]; + + vec4 dual0 = bbmod_Bones[j.x]; + vec4 dual1 = bbmod_Bones[j.y]; + vec4 dual2 = bbmod_Bones[j.z]; + vec4 dual3 = bbmod_Bones[j.w]; + + if (dot(real0, real1) < 0.0) { real1 *= -1.0; dual1 *= -1.0; } + if (dot(real0, real2) < 0.0) { real2 *= -1.0; dual2 *= -1.0; } + if (dot(real0, real3) < 0.0) { real3 *= -1.0; dual3 *= -1.0; } + + vec4 blendReal = + real0 * in_BoneWeight.x + + real1 * in_BoneWeight.y + + real2 * in_BoneWeight.z + + real3 * in_BoneWeight.w; + + vec4 blendDual = + dual0 * in_BoneWeight.x + + dual1 * in_BoneWeight.y + + dual2 * in_BoneWeight.z + + dual3 * in_BoneWeight.w; + + float len = length(blendReal); + blendReal /= len; + blendDual /= len; + + vertex = vec4(DualQuaternionTransform(blendReal, blendDual, vertex.xyz), 1.0); + normal = QuaternionRotate(blendReal, normal); + tangent = QuaternionRotate(blendReal, tangent); + bitangent = QuaternionRotate(blendReal, bitangent); + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_mTBN = mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShInstanceIDAnimated/BBMOD_ShInstanceIDAnimated.yy b/shaders/BBMOD_ShInstanceIDAnimated/BBMOD_ShInstanceIDAnimated.yy new file mode 100644 index 000000000..56a66f4fa --- /dev/null +++ b/shaders/BBMOD_ShInstanceIDAnimated/BBMOD_ShInstanceIDAnimated.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShInstanceIDAnimated", + "parent": { + "name": "Gizmo", + "path": "folders/_Extensions/BBMOD/Gizmo.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShInstanceIDBatched/BBMOD_ShInstanceIDBatched.fsh b/shaders/BBMOD_ShInstanceIDBatched/BBMOD_ShInstanceIDBatched.fsh new file mode 100644 index 000000000..c250257cd --- /dev/null +++ b/shaders/BBMOD_ShInstanceIDBatched/BBMOD_ShInstanceIDBatched.fsh @@ -0,0 +1,274 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vInstanceID; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + gl_FragColor = v_vInstanceID; + +} diff --git a/shaders/BBMOD_ShInstanceIDBatched/BBMOD_ShInstanceIDBatched.vsh b/shaders/BBMOD_ShInstanceIDBatched/BBMOD_ShInstanceIDBatched.vsh new file mode 100644 index 000000000..4d76b4962 --- /dev/null +++ b/shaders/BBMOD_ShInstanceIDBatched/BBMOD_ShInstanceIDBatched.vsh @@ -0,0 +1,113 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +attribute float in_Id; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +uniform vec4 bbmod_BatchData[BBMOD_MAX_BATCH_VEC4S]; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vInstanceID; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +vec3 QuaternionRotate(vec4 q, vec3 v) +{ + return (v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v)); +} + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); + + int idx = int(in_Id) * 3; + vec4 posScale = bbmod_BatchData[idx]; + vec4 rot = bbmod_BatchData[idx + 1]; + + vertex = vec4(posScale.xyz + (QuaternionRotate(rot, vertex.xyz) * posScale.w), 1.0); + normal = QuaternionRotate(rot, normal); + tangent = QuaternionRotate(rot, tangent); + bitangent = QuaternionRotate(rot, bitangent); + +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_mTBN = mat3(tangent, bitangent, normal); + + v_vInstanceID = bbmod_BatchData[(int(in_Id) * 3) + 2]; + +} diff --git a/shaders/BBMOD_ShInstanceIDBatched/BBMOD_ShInstanceIDBatched.yy b/shaders/BBMOD_ShInstanceIDBatched/BBMOD_ShInstanceIDBatched.yy new file mode 100644 index 000000000..848d22ec7 --- /dev/null +++ b/shaders/BBMOD_ShInstanceIDBatched/BBMOD_ShInstanceIDBatched.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShInstanceIDBatched", + "parent": { + "name": "Gizmo", + "path": "folders/_Extensions/BBMOD/Gizmo.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShInstanceIDLightmap/BBMOD_ShInstanceIDLightmap.fsh b/shaders/BBMOD_ShInstanceIDLightmap/BBMOD_ShInstanceIDLightmap.fsh new file mode 100644 index 000000000..dcb81271c --- /dev/null +++ b/shaders/BBMOD_ShInstanceIDLightmap/BBMOD_ShInstanceIDLightmap.fsh @@ -0,0 +1,281 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying vec2 v_vTexCoord2; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Instance IDs + +// The id of the instance that draws the mesh. +uniform vec4 bbmod_InstanceID; + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// RGBA: RGBM encoded lightmap +uniform sampler2D bbmod_Lightmap; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texEmissive, + sampler2D texLightmap, + vec2 uvLightmap, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + // Lightmap + m.Lightmap = xGammaToLinear(xDecodeRGBM(texture2D(texLightmap, uvLightmap))); + + return m; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Emissive, + bbmod_Lightmap, + v_vTexCoord2, + v_mTBN, + v_vTexCoord); + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + gl_FragColor = bbmod_InstanceID; + +} diff --git a/shaders/BBMOD_ShInstanceIDLightmap/BBMOD_ShInstanceIDLightmap.vsh b/shaders/BBMOD_ShInstanceIDLightmap/BBMOD_ShInstanceIDLightmap.vsh new file mode 100644 index 000000000..ba47beda8 --- /dev/null +++ b/shaders/BBMOD_ShInstanceIDLightmap/BBMOD_ShInstanceIDLightmap.vsh @@ -0,0 +1,95 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; +attribute vec2 in_TextureCoord1; + +attribute vec4 in_TangentW; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying vec2 v_vTexCoord2; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((gm_Matrices[MATRIX_WORLD] * vec4(normal, 0.0)).xyz); + tangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(tangent, 0.0)).xyz); + bitangent = normalize((gm_Matrices[MATRIX_WORLD] * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + v_vTexCoord2 = in_TextureCoord1; + + v_mTBN = mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShInstanceIDLightmap/BBMOD_ShInstanceIDLightmap.yy b/shaders/BBMOD_ShInstanceIDLightmap/BBMOD_ShInstanceIDLightmap.yy new file mode 100644 index 000000000..31228233c --- /dev/null +++ b/shaders/BBMOD_ShInstanceIDLightmap/BBMOD_ShInstanceIDLightmap.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShInstanceIDLightmap", + "parent": { + "name": "Gizmo", + "path": "folders/_Extensions/BBMOD/Gizmo.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShParticleDepth/BBMOD_ShParticleDepth.fsh b/shaders/BBMOD_ShParticleDepth/BBMOD_ShParticleDepth.fsh new file mode 100644 index 000000000..dcbb7a7fe --- /dev/null +++ b/shaders/BBMOD_ShParticleDepth/BBMOD_ShParticleDepth.fsh @@ -0,0 +1,282 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec4 v_vColor; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} + +void DepthShader(float depth) +{ + gl_FragColor.rgb = xEncodeDepth(depth / bbmod_ZFar); + gl_FragColor.a = 1.0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + material.Base *= v_vColor.rgb; + material.Opacity *= v_vColor.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + DepthShader(v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShParticleDepth/BBMOD_ShParticleDepth.vsh b/shaders/BBMOD_ShParticleDepth/BBMOD_ShParticleDepth.vsh new file mode 100644 index 000000000..2282ac9d8 --- /dev/null +++ b/shaders/BBMOD_ShParticleDepth/BBMOD_ShParticleDepth.vsh @@ -0,0 +1,127 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec2 in_TextureCoord0; + +attribute float in_Id; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +uniform vec4 bbmod_BatchData[BBMOD_MAX_BATCH_VEC4S]; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec4 v_vColor; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +vec3 QuaternionRotate(vec4 q, vec3 v) +{ + return (v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v)); +} + +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec3 batchPosition = bbmod_BatchData[int(in_Id) * 4 + 0].xyz; + vec4 batchRot = bbmod_BatchData[int(in_Id) * 4 + 1]; + vec3 batchScale = bbmod_BatchData[int(in_Id) * 4 + 2].xyz; + vec4 batchColorAlpha = bbmod_BatchData[int(in_Id) * 4 + 3]; + v_vColor.rgb = xGammaToLinear(batchColorAlpha.rgb); + v_vColor.a = batchColorAlpha.a; + + vec4 position = in_Position; + position.xyz *= batchScale; + position.xyz = QuaternionRotate(batchRot, position.xyz); + vec3 normal = QuaternionRotate(batchRot, vec3(0.0, 0.0, -1.0)); + + mat4 W = mat4( + vec4(1.0, 0.0, 0.0, 0.0), + vec4(0.0, 1.0, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(0.0, 0.0, 0.0, 1.0)); + W[3].xyz += batchPosition; + mat4 V = gm_Matrices[MATRIX_VIEW]; + mat4 P = gm_Matrices[MATRIX_PROJECTION]; + + W[0][0] = V[0][0]; W[1][0] = -V[0][1]; W[2][0] = V[0][2]; + W[0][1] = V[1][0]; W[1][1] = -V[1][1]; W[2][1] = V[1][2]; + W[0][2] = V[2][0]; W[1][2] = -V[2][1]; W[2][2] = V[2][2]; + + mat4 WV = V * W; + vec4 positionWVP = (P * (WV * position)); + v_vVertex = (W * position).xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_vEye.xyz = normalize(-vec3( + gm_Matrices[MATRIX_VIEW][0][2], + gm_Matrices[MATRIX_VIEW][1][2], + gm_Matrices[MATRIX_VIEW][2][2] + )); + v_vEye.w = (gm_Matrices[MATRIX_PROJECTION][2][3] == 0.0) ? 1.0 : 0.0; + + vec3 tangent = QuaternionRotate(batchRot, vec3(1.0, 0.0, 0.0)); + vec3 bitangent = QuaternionRotate(batchRot, vec3(0.0, 1.0, 0.0)); + v_mTBN = mat3(W) * mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShParticleDepth/BBMOD_ShParticleDepth.yy b/shaders/BBMOD_ShParticleDepth/BBMOD_ShParticleDepth.yy new file mode 100644 index 000000000..55af62d01 --- /dev/null +++ b/shaders/BBMOD_ShParticleDepth/BBMOD_ShParticleDepth.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShParticleDepth", + "parent": { + "name": "Particles", + "path": "folders/_Extensions/BBMOD/Particles.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShParticleLit/BBMOD_ShParticleLit.fsh b/shaders/BBMOD_ShParticleLit/BBMOD_ShParticleLit.fsh new file mode 100644 index 000000000..2c5f359eb --- /dev/null +++ b/shaders/BBMOD_ShParticleLit/BBMOD_ShParticleLit.fsh @@ -0,0 +1,822 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec4 v_vColor; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Soft particles + +// G-buffer surface. +uniform sampler2D bbmod_GBuffer; + +// Distance over which the particle smoothly dissappears when getting closer to +// geometry rendered in the depth buffer. +uniform float bbmod_SoftDistance; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// SSAO + +// SSAO texture +uniform sampler2D bbmod_SSAO; + +//////////////////////////////////////////////////////////////////////////////// +// Image based lighting + +// Prefiltered octahedron env. map +uniform sampler2D bbmod_IBL; +// Texel size of one octahedron +uniform vec2 bbmod_IBLTexel; + +//////////////////////////////////////////////////////////////////////////////// +// Punctual lights + +// [(x, y, z, range), (r, g, b, m), ...] +uniform vec4 bbmod_LightPunctualDataA[2 * MAX_PUNCTUAL_LIGHTS]; +// [(isSpotLight, dcosInner, dcosOuter), (dX, dY, dZ), ...] +uniform vec3 bbmod_LightPunctualDataB[2 * MAX_PUNCTUAL_LIGHTS]; + +//////////////////////////////////////////////////////////////////////////////// +// Shadow mapping + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnablePS; +// Shadowmap texture +uniform sampler2D bbmod_Shadowmap; +// (1.0/shadowmapWidth, 1.0/shadowmapHeight) +uniform vec2 bbmod_ShadowmapTexel; +// The area that the shadowmap captures +uniform float bbmod_ShadowmapArea; +// The range over which meshes smoothly transition into shadow. +uniform float bbmod_ShadowmapBias; +// The index of the light that casts shadows. Use -1 for the directional light. +uniform float bbmod_ShadowCasterIndex; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +/// @param subsurface Color in RGB and thickness/intensity in A. +/// @source https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/ +vec3 xCheapSubsurface(vec4 subsurface, vec3 eye, vec3 normal, vec3 light, vec3 lightColor) +{ + const float fLTPower = 1.0; + const float fLTScale = 1.0; + vec3 vLTLight = light + normal; + float fLTDot = pow(clamp(dot(eye, -vLTLight), 0.0, 1.0), fLTPower) * fLTScale; + float fLT = fLTDot * subsurface.a; + return subsurface.rgb * lightColor * fLT; +} +#define X_PI 3.14159265359 +#define X_2_PI 6.28318530718 + +/// @return x^2 +#define xPow2(x) ((x) * (x)) + +/// @return x^3 +#define xPow3(x) ((x) * (x) * (x)) + +/// @return x^4 +#define xPow4(x) ((x) * (x) * (x) * (x)) + +/// @return x^5 +#define xPow5(x) ((x) * (x) * (x) * (x) * (x)) + +/// @return arctan2(x,y) +#define xAtan2(x, y) atan(y, x) + +/// @return Direction from point `from` to point `to` in degrees (0-360 range). +float xPointDirection(vec2 from, vec2 to) +{ + float x = xAtan2(from.x - to.x, from.y - to.y); + return ((x > 0.0) ? x : (2.0 * X_PI + x)) * 180.0 / X_PI; +} + +/// @desc Default specular color for dielectrics +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +#define X_F0_DEFAULT vec3(0.04, 0.04, 0.04) + +/// @desc Normal distribution function +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularD_GGX(float roughness, float NdotH) +{ + float r = xPow4(roughness); + float a = NdotH * NdotH * (r - 1.0) + 1.0; + return r / (X_PI * a * a); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xSpecularD_Approx(float roughness, float RdotL) +{ + float a = roughness * roughness; + float a2 = a * a; + float rcp_a2 = 1.0 / a2; + // 0.5 / ln(2), 0.275 / ln(2) + float c = (0.72134752 * rcp_a2) + 0.39674113; + return (rcp_a2 * exp2((c * RdotL) - c)); +} + +/// @desc Roughness remapping for analytic lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_Analytic(float roughness) +{ + return xPow2(roughness + 1.0) * 0.125; +} + +/// @desc Roughness remapping for IBL lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_IBL(float roughness) +{ + return xPow2(roughness) * 0.5; +} + +/// @desc Geometric attenuation +/// @param k Use either xK_Analytic for analytic lights or xK_IBL for image based lighting. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularG_Schlick(float k, float NdotL, float NdotV) +{ + return (NdotL / (NdotL * (1.0 - k) + k)) + * (NdotV / (NdotV * (1.0 - k) + k)); +} + +/// @desc Fresnel +/// @source https://en.wikipedia.org/wiki/Schlick%27s_approximation +vec3 xSpecularF_Schlick(vec3 f0, float VdotH) +{ + return f0 + (1.0 - f0) * xPow5(1.0 - VdotH); +} + +/// @desc Cook-Torrance microfacet specular shading +/// @note N = normalize(vertexNormal) +/// L = normalize(light - vertex) +/// V = normalize(camera - vertex) +/// H = normalize(L + V) +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xBRDF(vec3 f0, float roughness, float NdotL, float NdotV, float NdotH, float VdotH) +{ + vec3 specular = xSpecularD_GGX(roughness, NdotH) + * xSpecularF_Schlick(f0, VdotH) + * xSpecularG_Schlick(xK_Analytic(roughness), NdotL, NdotH); + return specular / ((4.0 * NdotL * NdotV) + 0.1); +} + +vec3 SpecularGGX(Material m, vec3 N, vec3 V, vec3 L) +{ + vec3 H = normalize(L + V); + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + float NdotH = max(dot(N, H), 0.0); + float VdotH = max(dot(V, H), 0.0); + return xBRDF(m.Specular, m.Roughness, NdotL, NdotV, NdotH, VdotH); +} + +void DoDirectionalLightPS( + vec3 direction, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = normalize(-direction); + float NdotL = max(dot(N, L), 0.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * NdotL; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoPointLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + att *= att; + float NdotL = max(dot(N, L), 0.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * NdotL * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoSpotLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 direction, + float dcosInner, + float dcosOuter, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + float theta = dot(L, normalize(-direction)); + float epsilon = dcosInner - dcosOuter; + float intensity = clamp((theta - dcosOuter) / epsilon, 0.0, 1.0); + subsurface += xCheapSubsurface(m.Subsurface, V, N, L, color); + color *= (1.0 - shadow) * intensity * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} +// Source: https://gamedev.stackexchange.com/questions/169508/octahedral-impostors-octahedral-mapping + +/// @param dir Sampling dir vector in world-space. +/// @return UV coordinates on an octahedron map. +vec2 xVec3ToOctahedronUv(vec3 dir) +{ + vec3 octant = sign(dir); + float sum = dot(dir, octant); + vec3 octahedron = dir / sum; + if (octahedron.z < 0.0) + { + vec3 absolute = abs(octahedron); + octahedron.xy = octant.xy * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return octahedron.xy * 0.5 + 0.5; +} + +/// @desc Converts octahedron UV into a world-space vector. +vec3 xOctahedronUvToVec3Normalized(vec2 uv) +{ + vec3 position = vec3(2.0 * (uv - 0.5), 0); + vec2 absolute = abs(position.xy); + position.z = 1.0 - absolute.x - absolute.y; + if (position.z < 0.0) + { + position.xy = sign(position.xy) * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return position; +} + +vec3 xDiffuseIBL(sampler2D ibl, vec2 texel, vec3 N) +{ + const float s = 1.0 / 8.0; + const float r2 = 7.0; + + vec2 uv0 = xVec3ToOctahedronUv(N); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + return xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +vec2 xEnvBRDFApprox(float roughness, float NdotV) +{ + const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); + vec4 r = (roughness * c0) + c1; + float a004 = (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; + return ((vec2(-1.04, 1.04) * a004) + r.zw); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xEnvBRDFApproxNonmetal(float roughness, float NdotV) +{ + // Same as EnvBRDFApprox(0.04, Roughness, NdotV) + const vec2 c0 = vec2(-1.0, -0.0275); + const vec2 c1 = vec2(1.0, 0.0425); + vec2 r = (roughness * c0) + c1; + return (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; +} + +// Fully rough optimization: +// xEnvBRDFApprox(SpecularColor, 1, 1) == SpecularColor * 0.4524 - 0.0024 +// DiffuseColor += SpecularColor * 0.45; +// SpecularColor = 0.0; + +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xSpecularIBL(sampler2D ibl, vec2 texel/*, sampler2D brdf*/, vec3 f0, float roughness, vec3 N, vec3 V) +{ + float NdotV = clamp(dot(N, V), 0.0, 1.0); + vec3 R = 2.0 * dot(V, N) * N - V; + // vec2 envBRDF = texture2D(brdf, vec2(roughness, NdotV)).xy; + vec2 envBRDF = xEnvBRDFApprox(roughness, NdotV); + + const float s = 1.0 / 8.0; + float r = roughness * 7.0; + float r2 = floor(r); + float rDiff = r - r2; + + vec2 uv0 = xVec3ToOctahedronUv(R); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + vec2 uv1 = uv0; + uv1.x = uv1.x + s; + + vec3 specular = f0 * envBRDF.x + envBRDF.y; + + vec3 col0 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))) * specular; + vec3 col1 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv1))) * specular; + + return mix(col0, col1, rDiff); +} +/// @param tanAspect (tanFovY*(screenWidth/screenHeight),-tanFovY), where +/// tanFovY = dtan(fov*0.5) +/// @param texCoord Sceen-space UV. +/// @param depth Scene depth at texCoord. +/// @return Point projected to view-space. +vec3 xProject(vec2 tanAspect, vec2 texCoord, float depth) +{ + return vec3(tanAspect * (texCoord * 2.0 - 1.0) * depth, depth); +} + +/// @param p A point in clip space (transformed by projection matrix, but not +/// normalized). +/// @return P's UV coordinates on the screen. +vec2 xUnproject(vec4 p) +{ + vec2 uv = p.xy / p.w; + uv = uv * 0.5 + 0.5; +#if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + uv.y = 1.0 - uv.y; +#endif + return uv; +} +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} +// Shadowmap filtering source: https://www.gamedev.net/tutorials/programming/graphics/contact-hardening-soft-shadows-made-fast-r4906/ +float InterleavedGradientNoise(vec2 positionScreen) +{ + vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); + return fract(magic.z * fract(dot(positionScreen, magic.xy))); +} +vec2 VogelDiskSample(int sampleIndex, int samplesCount, float phi) +{ + float GoldenAngle = 2.4; + float r = sqrt(float(sampleIndex) + 0.5) / sqrt(float(samplesCount)); + float theta = float(sampleIndex) * GoldenAngle + phi; + float sine = sin(theta); + float cosine = cos(theta); + return vec2(r * cosine, r * sine); +} + +float ShadowMap(sampler2D shadowMap, vec2 texel, vec2 uv, float compareZ) +{ + if (clamp(uv.xy, vec2(0.0), vec2(1.0)) != uv.xy) + { + return 0.0; + } + float shadow = 0.0; + float noise = 6.28 * InterleavedGradientNoise(gl_FragCoord.xy); + float bias = bbmod_ShadowmapBias / bbmod_ShadowmapArea; + for (int i = 0; i < SHADOWMAP_SAMPLE_COUNT; ++i) + { + vec2 uv2 = uv + VogelDiskSample(i, SHADOWMAP_SAMPLE_COUNT, noise) * texel * 4.0; + float depth = xDecodeDepth(texture2D(shadowMap, uv2).rgb); + if (bias != 0.0) + { + shadow += clamp((compareZ - depth) / bias, 0.0, 1.0); + } + else + { + shadow += step(depth, compareZ); + } + } + return (shadow / float(SHADOWMAP_SAMPLE_COUNT)); +} + +void PBRShader(Material material, float depth) +{ + vec3 N = material.Normal; + vec3 V = (v_vEye.w == 1.0) ? v_vEye.xyz : normalize(bbmod_CamPos - v_vVertex); + vec3 lightDiffuse = vec3(0.0); + vec3 lightSpecular = vec3(0.0); + vec3 lightSubsurface = vec3(0.0); + + // Ambient light + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + lightDiffuse += mix(ambientDown, ambientUp, N.z * 0.5 + 0.5); + + // Shadow mapping + float shadow = 0.0; + if (bbmod_ShadowmapEnablePS == 1.0) + { + vec4 shadowmapPos = v_vPosShadowmap; + shadowmapPos.xy /= shadowmapPos.w; + float shadowmapAtt = (bbmod_ShadowCasterIndex == -1.0) + ? clamp((1.0 - length(shadowmapPos.xy)) / 0.1, 0.0, 1.0) + : 1.0; + shadowmapPos.xy = shadowmapPos.xy * 0.5 + 0.5; + #if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + shadowmapPos.y = 1.0 - shadowmapPos.y; + #endif + shadowmapPos.z /= bbmod_ShadowmapArea; + + shadow = ShadowMap(bbmod_Shadowmap, bbmod_ShadowmapTexel, shadowmapPos.xy, shadowmapPos.z) + * shadowmapAtt; + } + + // IBL + lightDiffuse += xDiffuseIBL(bbmod_IBL, bbmod_IBLTexel, N); + lightSpecular += xSpecularIBL(bbmod_IBL, bbmod_IBLTexel, material.Specular, material.Roughness, N, V); + // TODO: Subsurface scattering for IBL + + // Directional light + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + DoDirectionalLightPS( + bbmod_LightDirectionalDir, + directionalLightColor, + (bbmod_ShadowCasterIndex == -1.0) ? shadow : 0.0, + v_vVertex, N, V, material, lightDiffuse, lightSpecular, lightSubsurface); + + // Punctual lights + for (int i = 0; i < MAX_PUNCTUAL_LIGHTS; ++i) + { + vec4 positionRange = bbmod_LightPunctualDataA[i * 2]; + vec4 colorAlpha = bbmod_LightPunctualDataA[(i * 2) + 1]; + vec3 isSpotInnerOuter = bbmod_LightPunctualDataB[i * 2]; + vec3 direction = bbmod_LightPunctualDataB[(i * 2) + 1]; + vec3 color = xGammaToLinear(colorAlpha.rgb) * colorAlpha.a; + + if (isSpotInnerOuter.x == 1.0) + { + DoSpotLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + direction, isSpotInnerOuter.y, isSpotInnerOuter.z, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + else + { + DoPointLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + } + + // Lightmap + + // Diffuse + gl_FragColor.rgb = material.Base * lightDiffuse; + // Specular + gl_FragColor.rgb += lightSpecular; + // Ambient occlusion + gl_FragColor.rgb *= material.AO; + // Emissive + gl_FragColor.rgb += material.Emissive; + // Subsurface scattering + gl_FragColor.rgb += lightSubsurface; + // Opacity + gl_FragColor.a = material.Opacity; + // Soft particles + if (bbmod_SoftDistance > 0.0) + { + float sceneDepth = xDecodeDepth(texture2D(bbmod_GBuffer, xUnproject(v_vPosition)).rgb) * bbmod_ZFar; + float softness = clamp((sceneDepth - v_vPosition.z) / bbmod_SoftDistance, 0.0, 1.0); + gl_FragColor.a *= softness; + } + // Fog + Fog(depth); + + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + material.Base *= v_vColor.rgb; + material.Opacity *= v_vColor.a; + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + PBRShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShParticleLit/BBMOD_ShParticleLit.vsh b/shaders/BBMOD_ShParticleLit/BBMOD_ShParticleLit.vsh new file mode 100644 index 000000000..15e0bcb1d --- /dev/null +++ b/shaders/BBMOD_ShParticleLit/BBMOD_ShParticleLit.vsh @@ -0,0 +1,143 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec2 in_TextureCoord0; + +attribute float in_Id; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +uniform vec4 bbmod_BatchData[BBMOD_MAX_BATCH_VEC4S]; + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnableVS; +// WORLD_VIEW_PROJECTION matrix used when rendering shadowmap +uniform mat4 bbmod_ShadowmapMatrix; +// Offsets vertex position by its normal scaled by this value +uniform float bbmod_ShadowmapNormalOffset; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec4 v_vColor; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +vec3 QuaternionRotate(vec4 q, vec3 v) +{ + return (v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v)); +} + +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec3 batchPosition = bbmod_BatchData[int(in_Id) * 4 + 0].xyz; + vec4 batchRot = bbmod_BatchData[int(in_Id) * 4 + 1]; + vec3 batchScale = bbmod_BatchData[int(in_Id) * 4 + 2].xyz; + vec4 batchColorAlpha = bbmod_BatchData[int(in_Id) * 4 + 3]; + v_vColor.rgb = xGammaToLinear(batchColorAlpha.rgb); + v_vColor.a = batchColorAlpha.a; + + vec4 position = in_Position; + position.xyz *= batchScale; + position.xyz = QuaternionRotate(batchRot, position.xyz); + vec3 normal = QuaternionRotate(batchRot, vec3(0.0, 0.0, -1.0)); + + mat4 W = mat4( + vec4(1.0, 0.0, 0.0, 0.0), + vec4(0.0, 1.0, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(0.0, 0.0, 0.0, 1.0)); + W[3].xyz += batchPosition; + mat4 V = gm_Matrices[MATRIX_VIEW]; + mat4 P = gm_Matrices[MATRIX_PROJECTION]; + + W[0][0] = V[0][0]; W[1][0] = -V[0][1]; W[2][0] = V[0][2]; + W[0][1] = V[1][0]; W[1][1] = -V[1][1]; W[2][1] = V[1][2]; + W[0][2] = V[2][0]; W[1][2] = -V[2][1]; W[2][2] = V[2][2]; + + mat4 WV = V * W; + vec4 positionWVP = (P * (WV * position)); + v_vVertex = (W * position).xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_vEye.xyz = normalize(-vec3( + gm_Matrices[MATRIX_VIEW][0][2], + gm_Matrices[MATRIX_VIEW][1][2], + gm_Matrices[MATRIX_VIEW][2][2] + )); + v_vEye.w = (gm_Matrices[MATRIX_PROJECTION][2][3] == 0.0) ? 1.0 : 0.0; + + vec3 tangent = QuaternionRotate(batchRot, vec3(1.0, 0.0, 0.0)); + vec3 bitangent = QuaternionRotate(batchRot, vec3(0.0, 1.0, 0.0)); + v_mTBN = mat3(W) * mat3(tangent, bitangent, normal); + + //////////////////////////////////////////////////////////////////////////// + // Vertex position in shadowmap + if (bbmod_ShadowmapEnableVS == 1.0) + { + v_vPosShadowmap = bbmod_ShadowmapMatrix + * vec4(v_vVertex + normal * bbmod_ShadowmapNormalOffset, 1.0); + } +} diff --git a/shaders/BBMOD_ShParticleLit/BBMOD_ShParticleLit.yy b/shaders/BBMOD_ShParticleLit/BBMOD_ShParticleLit.yy new file mode 100644 index 000000000..ba1428c37 --- /dev/null +++ b/shaders/BBMOD_ShParticleLit/BBMOD_ShParticleLit.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShParticleLit", + "parent": { + "name": "Particles", + "path": "folders/_Extensions/BBMOD/Particles.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShParticleUnlit/BBMOD_ShParticleUnlit.fsh b/shaders/BBMOD_ShParticleUnlit/BBMOD_ShParticleUnlit.fsh new file mode 100644 index 000000000..6faa38964 --- /dev/null +++ b/shaders/BBMOD_ShParticleUnlit/BBMOD_ShParticleUnlit.fsh @@ -0,0 +1,377 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec4 v_vColor; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// RGB: Subsurface color, A: Intensity +uniform sampler2D bbmod_Subsurface; +// RGBA: RGBM encoded emissive color +uniform sampler2D bbmod_Emissive; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Soft particles + +// G-buffer surface. +uniform sampler2D bbmod_GBuffer; + +// Distance over which the particle smoothly dissappears when getting closer to +// geometry rendered in the depth buffer. +uniform float bbmod_SoftDistance; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + sampler2D texSubsurface, + sampler2D texEmissive, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + // Subsurface (color and intensity) + vec4 subsurface = texture2D(texSubsurface, uv); + m.Subsurface = vec4(xGammaToLinear(subsurface.rgb).rgb, subsurface.a); + + // Emissive color + m.Emissive = xGammaToLinear(xDecodeRGBM(texture2D(texEmissive, uv))); + + return m; +} + +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} +/// @param tanAspect (tanFovY*(screenWidth/screenHeight),-tanFovY), where +/// tanFovY = dtan(fov*0.5) +/// @param texCoord Sceen-space UV. +/// @param depth Scene depth at texCoord. +/// @return Point projected to view-space. +vec3 xProject(vec2 tanAspect, vec2 texCoord, float depth) +{ + return vec3(tanAspect * (texCoord * 2.0 - 1.0) * depth, depth); +} + +/// @param p A point in clip space (transformed by projection matrix, but not +/// normalized). +/// @return P's UV coordinates on the screen. +vec2 xUnproject(vec4 p) +{ + vec2 uv = p.xy / p.w; + uv = uv * 0.5 + 0.5; +#if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + uv.y = 1.0 - uv.y; +#endif + return uv; +} +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} + +void UnlitShader(Material material, float depth) +{ + gl_FragColor.rgb = material.Base; + gl_FragColor.rgb += material.Emissive; + gl_FragColor.a = material.Opacity; + // Soft particles + if (bbmod_SoftDistance > 0.0) + { + float sceneDepth = xDecodeDepth(texture2D(bbmod_GBuffer, xUnproject(v_vPosition)).rgb) * bbmod_ZFar; + float softness = clamp((sceneDepth - v_vPosition.z) / bbmod_SoftDistance, 0.0, 1.0); + gl_FragColor.a *= softness; + } + Fog(depth); + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + bbmod_Subsurface, + bbmod_Emissive, + v_mTBN, + v_vTexCoord); + + material.Base *= v_vColor.rgb; + material.Opacity *= v_vColor.a; + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + UnlitShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShParticleUnlit/BBMOD_ShParticleUnlit.vsh b/shaders/BBMOD_ShParticleUnlit/BBMOD_ShParticleUnlit.vsh new file mode 100644 index 000000000..4b6632d15 --- /dev/null +++ b/shaders/BBMOD_ShParticleUnlit/BBMOD_ShParticleUnlit.vsh @@ -0,0 +1,120 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec2 in_TextureCoord0; + +attribute float in_Id; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +uniform vec4 bbmod_BatchData[BBMOD_MAX_BATCH_VEC4S]; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec4 v_vColor; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +vec3 QuaternionRotate(vec4 q, vec3 v) +{ + return (v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v)); +} + +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec3 batchPosition = bbmod_BatchData[int(in_Id) * 4 + 0].xyz; + vec4 batchRot = bbmod_BatchData[int(in_Id) * 4 + 1]; + vec3 batchScale = bbmod_BatchData[int(in_Id) * 4 + 2].xyz; + vec4 batchColorAlpha = bbmod_BatchData[int(in_Id) * 4 + 3]; + v_vColor.rgb = xGammaToLinear(batchColorAlpha.rgb); + v_vColor.a = batchColorAlpha.a; + + vec4 position = in_Position; + position.xyz *= batchScale; + position.xyz = QuaternionRotate(batchRot, position.xyz); + vec3 normal = QuaternionRotate(batchRot, vec3(0.0, 0.0, -1.0)); + + mat4 W = mat4( + vec4(1.0, 0.0, 0.0, 0.0), + vec4(0.0, 1.0, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(0.0, 0.0, 0.0, 1.0)); + W[3].xyz += batchPosition; + mat4 V = gm_Matrices[MATRIX_VIEW]; + mat4 P = gm_Matrices[MATRIX_PROJECTION]; + + W[0][0] = V[0][0]; W[1][0] = -V[0][1]; W[2][0] = V[0][2]; + W[0][1] = V[1][0]; W[1][1] = -V[1][1]; W[2][1] = V[1][2]; + W[0][2] = V[2][0]; W[1][2] = -V[2][1]; W[2][2] = V[2][2]; + + mat4 WV = V * W; + vec4 positionWVP = (P * (WV * position)); + v_vVertex = (W * position).xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + vec3 tangent = QuaternionRotate(batchRot, vec3(1.0, 0.0, 0.0)); + vec3 bitangent = QuaternionRotate(batchRot, vec3(0.0, 1.0, 0.0)); + v_mTBN = mat3(W) * mat3(tangent, bitangent, normal); + +} diff --git a/shaders/BBMOD_ShParticleUnlit/BBMOD_ShParticleUnlit.yy b/shaders/BBMOD_ShParticleUnlit/BBMOD_ShParticleUnlit.yy new file mode 100644 index 000000000..b8ebe054c --- /dev/null +++ b/shaders/BBMOD_ShParticleUnlit/BBMOD_ShParticleUnlit.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShParticleUnlit", + "parent": { + "name": "Particles", + "path": "folders/_Extensions/BBMOD/Particles.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShPostProcess/BBMOD_ShPostProcess.fsh b/shaders/BBMOD_ShPostProcess/BBMOD_ShPostProcess.fsh new file mode 100644 index 000000000..fbe594869 --- /dev/null +++ b/shaders/BBMOD_ShPostProcess/BBMOD_ShPostProcess.fsh @@ -0,0 +1,117 @@ +// FIXME: Temporary fix! +precision highp float; + +varying vec2 v_vTexCoord; + +uniform sampler2D u_texLut; // Color grading LUT +uniform vec2 u_vTexel; // 1/ScreenWidth, 1/ScreenHeight +uniform vec3 u_vOffset; // Chromatic aberration offset for each channel +uniform float u_fDistortion; // The strength of the chromatic aberration effect +uniform float u_fGrayscale; // The strength of the grayscale effect +uniform float u_fVignette; // The strength of the vignette effect +uniform vec3 u_vVignetteColor; // The color of the vignette effect + +/// @param color The original RGB color. +/// @param lut Texture of color-grading lookup table (256x16). +/// Needs to have interpolation enabled! +vec3 ColorGrade(vec3 color, sampler2D lut) +{ + // Fixes selecting wrong mips on HTML5. + const float bias = -5.0; + + const vec2 texel = 1.0 / vec2(256.0, 16.0); + + float x1 = floor(color.r * 15.0); + float y1 = floor(color.g * 15.0); + float z1 = floor(color.b * 15.0) * 16.0; + + float x2 = ceil(color.r * 15.0); + float y2 = ceil(color.g * 15.0); + float z2 = ceil(color.b * 15.0) * 16.0; + + vec2 uv1 = vec2(z1 + x1, y1) * texel; + vec2 uv2 = vec2(z2 + x2, y2) * texel; + + uv1 += 0.5 * texel; + uv2 += 0.5 * texel; + + vec3 color1 = texture2D(lut, uv1, bias).rgb; + vec3 color2 = texture2D(lut, uv2, bias).rgb; + + return vec3( + mix(color1.r, color2.r, fract(color.r * 15.0)), + mix(color1.g, color2.g, fract(color.g * 15.0)), + mix(color1.b, color2.b, fract(color.b * 15.0))); +} + +float Luminance(vec3 color) +{ + const vec3 weights = vec3(0.2125, 0.7154, 0.0721); + return dot(color, weights); +} + +//#pragma include("ChromaticAberration.xsh") +/// @param direction Direction of distortion. +/// @param distortion Per-channel distortion factor. +/// @source http://john-chapman-graphics.blogspot.cz/2013/02/pseudo-lens-flare.html +vec3 xChromaticAberration( + sampler2D tex, + vec2 uv, + vec2 direction, + vec3 distortion) +{ + return vec3( + texture2D(tex, uv + direction * distortion.r).r + + texture2D(tex, uv + direction * distortion.r * (1.0 / 4.0)).r + + texture2D(tex, uv + direction * distortion.r * (2.0 / 4.0)).r + + texture2D(tex, uv + direction * distortion.r * (3.0 / 4.0)).r, + texture2D(tex, uv + direction * distortion.g).g + + texture2D(tex, uv + direction * distortion.g * (1.0 / 4.0)).g + + texture2D(tex, uv + direction * distortion.g * (2.0 / 4.0)).g + + texture2D(tex, uv + direction * distortion.g * (3.0 / 4.0)).g, + texture2D(tex, uv + direction * distortion.b).b + + texture2D(tex, uv + direction * distortion.b * (1.0 / 4.0)).b + + texture2D(tex, uv + direction * distortion.b * (2.0 / 4.0)).b + + texture2D(tex, uv + direction * distortion.b * (3.0 / 4.0)).b + ) / 4.0; +} + +// include("ChromaticAberration.xsh") + +void main() +{ + vec2 vec = 0.5 - v_vTexCoord; + float vecLen = length(vec); + vec3 color; + + // Chromatic aberration + if (u_fDistortion != 0.0) + { + vec3 distortion = u_vOffset * u_vTexel.x * u_fDistortion * min(vecLen / 0.5, 1.0); + color = xChromaticAberration(gm_BaseTexture, v_vTexCoord, normalize(vec), distortion); + } + else + { + color = texture2D(gm_BaseTexture, v_vTexCoord).rgb; + } + + // Color grading +#ifndef _YY_GLSLES_ + color = ColorGrade(color, u_texLut); +#endif + + // Grayscale + if (u_fGrayscale != 0.0) + { + color = mix(color, vec3(Luminance(color)), u_fGrayscale); + } + + // Vignette + if (u_fVignette != 0.0) + { + color = mix(color, u_vVignetteColor, vecLen * vecLen * u_fVignette); + } + + gl_FragColor.rgb = color; + gl_FragColor.a = 1.0; +} diff --git a/shaders/BBMOD_ShPostProcess/BBMOD_ShPostProcess.vsh b/shaders/BBMOD_ShPostProcess/BBMOD_ShPostProcess.vsh new file mode 100644 index 000000000..917d5a5f5 --- /dev/null +++ b/shaders/BBMOD_ShPostProcess/BBMOD_ShPostProcess.vsh @@ -0,0 +1,10 @@ +attribute vec4 in_Position; // (x,y,z,w) +attribute vec2 in_TextureCoord; // (u,v) + +varying vec2 v_vTexCoord; + +void main() +{ + gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position; + v_vTexCoord = in_TextureCoord; +} diff --git a/shaders/BBMOD_ShPostProcess/BBMOD_ShPostProcess.yy b/shaders/BBMOD_ShPostProcess/BBMOD_ShPostProcess.yy new file mode 100644 index 000000000..fc3b4c7b5 --- /dev/null +++ b/shaders/BBMOD_ShPostProcess/BBMOD_ShPostProcess.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShPostProcess", + "parent": { + "name": "PostProcessing", + "path": "folders/_Extensions/BBMOD/Rendering/PostProcessing.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShSSAO/BBMOD_ShSSAO.yy b/shaders/BBMOD_ShSSAO/BBMOD_ShSSAO.yy new file mode 100644 index 000000000..1d545377e --- /dev/null +++ b/shaders/BBMOD_ShSSAO/BBMOD_ShSSAO.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShSSAO", + "parent": { + "name": "SSAO", + "path": "folders/_Extensions/BBMOD/Rendering/SSAO.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShSSAO/bbmod_shssao.fsh b/shaders/BBMOD_ShSSAO/bbmod_shssao.fsh new file mode 100644 index 000000000..07b4a109e --- /dev/null +++ b/shaders/BBMOD_ShSSAO/bbmod_shssao.fsh @@ -0,0 +1,188 @@ +// Reference: https://de45xmedrsdbp.cloudfront.net/__resources/files/The_Technology_Behind_the_Elemental_Demo_16x9-1248544805.pdf +// Reference: http://frederikaalund.com/wp-content/uploads/2013/05/A-Comparative-Study-of-Screen-Space-Ambient-Occlusion-Methods.pdf + +// The size of the SSAO kernel. +#define BBMOD_SSAO_KERNEL_SIZE 8 + +varying vec2 v_vTexCoord; + +// Texture of random rotations. +uniform sampler2D u_texNoise; + +// (1 / screenWidth, 1 / screenHeight) +uniform vec2 u_vTexel; + +// (dtan(fov / 2) * (screenWidth / screenHeight), -dtan(fov / 2)) +uniform vec2 u_vTanAspect; + +// Distance to the far clipping plane. +uniform float u_fClipFar; + +// Kernel of random vectors. +uniform vec2 u_vSampleKernel[BBMOD_SSAO_KERNEL_SIZE]; + +// (screenWidth, screenHeight) / noiseTextureSize +uniform vec2 u_vNoiseScale; + +// Strength of the occlusion effect. +uniform float u_fPower; + +// Screen-space radius of the occlusion effect. +uniform float u_fRadius; + +// Angle bias of the occlusion effect (in radians). +uniform float u_fAngleBias; + +// Maximum depth difference of samples taken into account. +uniform float u_fDepthRange; + +//#pragma include("DepthEncoding.xsh", "glsl") +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} +// include("DepthEncoding.xsh") + +//#pragma include("Projecting.xsh", "glsl") +/// @param tanAspect (tanFovY*(screenWidth/screenHeight),-tanFovY), where +/// tanFovY = dtan(fov*0.5) +/// @param texCoord Sceen-space UV. +/// @param depth Scene depth at texCoord. +/// @return Point projected to view-space. +vec3 xProject(vec2 tanAspect, vec2 texCoord, float depth) +{ + return vec3(tanAspect * (texCoord * 2.0 - 1.0) * depth, depth); +} + +/// @param p A point in clip space (transformed by projection matrix, but not +/// normalized). +/// @return P's UV coordinates on the screen. +vec2 xUnproject(vec4 p) +{ + vec2 uv = p.xy / p.w; + uv = uv * 0.5 + 0.5; + uv.y = 1.0 - uv.y; + return uv; +} +// include("Projecting.xsh") + +//#pragma include("Math.xsh", "glsl") +#define X_PI 3.14159265359 +#define X_2_PI 6.28318530718 + +/// @return x^2 +#define xPow2(x) ((x) * (x)) + +/// @return x^3 +#define xPow3(x) ((x) * (x) * (x)) + +/// @return x^4 +#define xPow4(x) ((x) * (x) * (x) * (x)) + +/// @return x^5 +#define xPow5(x) ((x) * (x) * (x) * (x) * (x)) + +/// @return arctan2(x,y) +#define xAtan2(x, y) atan(y, x) + +/// @return Direction from point `from` to point `to` in degrees (0-360 range). +float xPointDirection(vec2 from, vec2 to) +{ + float x = xAtan2(from.x - to.x, from.y - to.y); + return ((x > 0.0) ? x : (2.0 * X_PI + x)) * 180.0 / X_PI; +} +// include("Math.xsh") + +// Source: http://stackoverflow.com/a/3380723/554283 +float AcosApprox(float x) +{ + return (-0.69813170079773212 * x * x - 0.87266462599716477) * x + 1.5707963267948966; +} + +void main() +{ + // Origin + float depth = xDecodeDepth(texture2D(gm_BaseTexture, v_vTexCoord).rgb) * u_fClipFar; + + if (depth == 0.0 || depth == u_fClipFar) + { + gl_FragColor = vec4(1.0); + return; + } + + vec3 origin = xProject(u_vTanAspect, v_vTexCoord, depth); + vec2 noise = texture2D(u_texNoise, v_vTexCoord * u_vNoiseScale).xy * 2.0 - 1.0; + mat2 rot = mat2( + noise.x, -noise.y, + noise.y, noise.x + ); + + // Occlusion + float occlusion = 0.0; + + for (int i = 0; i < BBMOD_SSAO_KERNEL_SIZE; ++i) + { + vec2 dir = (rot * u_vSampleKernel[i].xy) * u_fRadius; + vec2 uv1 = v_vTexCoord + dir * u_vTexel; + vec2 uv2 = v_vTexCoord - dir * u_vTexel; + + float angle = 1.0; + + if (uv1.x > 0.0 && uv1.x < 1.0 + && uv1.y > 0.0 && uv1.y < 1.0 + && uv2.x > 0.0 && uv2.x < 1.0 + && uv2.y > 0.0 && uv2.y < 1.0) + { + float depth1 = xDecodeDepth(texture2D(gm_BaseTexture, uv1).rgb) * u_fClipFar; + vec3 pos1 = xProject(u_vTanAspect, uv1, depth1); + vec3 diff1 = pos1 - origin; + + float depth2 = xDecodeDepth(texture2D(gm_BaseTexture, uv2).rgb) * u_fClipFar; + vec3 pos2 = xProject(u_vTanAspect, uv2, depth2); + vec3 diff2 = pos2 - origin; + + float cosAngle = dot(diff1, diff2) / (length(diff1) * length(diff2)); + angle = max(AcosApprox(cosAngle - u_fAngleBias), 0.0) / X_PI; + + if (-diff1.z - diff2.z < 0.01) + { + angle = 1.0; + } + + float att = (abs(diff1.z) + abs(diff2.z)) / (u_fDepthRange * 2.0); + att = clamp(att * att, 0.0, 1.0); + angle = mix(angle, 1.0, att); + } + + occlusion += angle; + } + + occlusion /= float(BBMOD_SSAO_KERNEL_SIZE); + occlusion = pow(occlusion, u_fPower); + + // Output + gl_FragColor.rgb = vec3(occlusion); + gl_FragColor.a = 1.0; +} diff --git a/shaders/BBMOD_ShSSAO/bbmod_shssao.vsh b/shaders/BBMOD_ShSSAO/bbmod_shssao.vsh new file mode 100644 index 000000000..917d5a5f5 --- /dev/null +++ b/shaders/BBMOD_ShSSAO/bbmod_shssao.vsh @@ -0,0 +1,10 @@ +attribute vec4 in_Position; // (x,y,z,w) +attribute vec2 in_TextureCoord; // (u,v) + +varying vec2 v_vTexCoord; + +void main() +{ + gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position; + v_vTexCoord = in_TextureCoord; +} diff --git a/shaders/BBMOD_ShSSAOBlur/BBMOD_ShSSAOBlur.yy b/shaders/BBMOD_ShSSAOBlur/BBMOD_ShSSAOBlur.yy new file mode 100644 index 000000000..6c52cb021 --- /dev/null +++ b/shaders/BBMOD_ShSSAOBlur/BBMOD_ShSSAOBlur.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShSSAOBlur", + "parent": { + "name": "SSAO", + "path": "folders/_Extensions/BBMOD/Rendering/SSAO.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShSSAOBlur/bbmod_shssaoblur.fsh b/shaders/BBMOD_ShSSAOBlur/bbmod_shssaoblur.fsh new file mode 100644 index 000000000..d37da95af --- /dev/null +++ b/shaders/BBMOD_ShSSAOBlur/bbmod_shssaoblur.fsh @@ -0,0 +1,54 @@ +// Size of the SSAO noise texture. +#define BBMOD_SSAO_NOISE_TEXTURE_SIZE 4 + +varying vec2 v_vTexCoord; + +uniform sampler2D u_texDepth; +uniform vec2 u_vTexel; // (1 / screenWidth, 0) for horizontal blur, (0 , 1 / screenHeight) for vertical +uniform float u_fClipFar; + +//#pragma include("DepthEncoding.xsh", "glsl") +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} +// include("DepthEncoding.xsh") + +void main() +{ + gl_FragColor = vec4(0.0); + float depth = xDecodeDepth(texture2D(u_texDepth, v_vTexCoord).rgb) * u_fClipFar; + float weightSum = 0.001; + for (float i = 0.0; i < float(BBMOD_SSAO_NOISE_TEXTURE_SIZE); i += 1.0) + { + vec2 uv = v_vTexCoord + u_vTexel * i; + float sampleDepth = xDecodeDepth(texture2D(u_texDepth, uv).rgb) * u_fClipFar; + float weight = 1.0 - clamp(abs(depth - sampleDepth) / 2.0, 0.0, 1.0); // TODO: Configurable blur depth range? + gl_FragColor.rgb += texture2D(gm_BaseTexture, uv).rgb * weight; + weightSum += weight; + } + gl_FragColor.rgb /= weightSum; + gl_FragColor.a = 1.0; +} diff --git a/shaders/BBMOD_ShSSAOBlur/bbmod_shssaoblur.vsh b/shaders/BBMOD_ShSSAOBlur/bbmod_shssaoblur.vsh new file mode 100644 index 000000000..917d5a5f5 --- /dev/null +++ b/shaders/BBMOD_ShSSAOBlur/bbmod_shssaoblur.vsh @@ -0,0 +1,10 @@ +attribute vec4 in_Position; // (x,y,z,w) +attribute vec2 in_TextureCoord; // (u,v) + +varying vec2 v_vTexCoord; + +void main() +{ + gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position; + v_vTexCoord = in_TextureCoord; +} diff --git a/shaders/BBMOD_ShSky/BBMOD_ShSky.fsh b/shaders/BBMOD_ShSky/BBMOD_ShSky.fsh new file mode 100644 index 000000000..98e48753b --- /dev/null +++ b/shaders/BBMOD_ShSky/BBMOD_ShSky.fsh @@ -0,0 +1,79 @@ +varying vec3 v_vNormal; + +// Camera's exposure value +uniform float bbmod_Exposure; + +//#pragma include("EquirectangularMapping.xsh") +#define X_PI 3.14159265359 +#define X_2_PI 6.28318530718 + +/// @return x^2 +float xPow2(float x) { return (x * x); } + +/// @return x^3 +float xPow3(float x) { return (x * x * x); } + +/// @return x^4 +float xPow4(float x) { return (x * x * x * x); } + +/// @return x^5 +float xPow5(float x) { return (x * x * x * x * x); } + +/// @param dir A sampling direction in world space. +/// @return UV coordinates on an equirectangular map. +vec2 xVec3ToEquirectangularUv(vec3 dir) +{ + vec3 n = normalize(dir); + return vec2((atan(n.y, n.x) / X_2_PI) + 0.5, acos(n.z) / X_PI); +} +// include("EquirectangularMapping.xsh") + +//#pragma include("RGBM.xsh") +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} +// include("RGBM.xsh") + +//#pragma include("Color.xsh") +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +// include("Color.xsh") + +void main() +{ + gl_FragColor.rgb = xGammaToLinear(xDecodeRGBM(texture2D(gm_BaseTexture, xVec3ToEquirectangularUv(v_vNormal)))); + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); + gl_FragColor.a = 1.0; +} diff --git a/shaders/BBMOD_ShSky/BBMOD_ShSky.vsh b/shaders/BBMOD_ShSky/BBMOD_ShSky.vsh new file mode 100644 index 000000000..57fe5708d --- /dev/null +++ b/shaders/BBMOD_ShSky/BBMOD_ShSky.vsh @@ -0,0 +1,13 @@ +attribute vec4 in_Position; +attribute vec3 in_Normal; +attribute vec2 in_TextureCoord0; +//attribute vec4 in_Color; +attribute vec4 in_TangentW; + +varying vec3 v_vNormal; + +void main() +{ + gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position; + v_vNormal = normalize(in_Normal); //normalize((gm_Matrices[MATRIX_WORLD] * vec4(in_Normal, 0.0)).xyz); +} diff --git a/shaders/BBMOD_ShSky/BBMOD_ShSky.yy b/shaders/BBMOD_ShSky/BBMOD_ShSky.yy new file mode 100644 index 000000000..19a348ff2 --- /dev/null +++ b/shaders/BBMOD_ShSky/BBMOD_ShSky.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShSky", + "parent": { + "name": "Sky", + "path": "folders/_Extensions/BBMOD/Rendering/Sky.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShTerrain/BBMOD_ShTerrain.fsh b/shaders/BBMOD_ShTerrain/BBMOD_ShTerrain.fsh new file mode 100644 index 000000000..eb19f4c5f --- /dev/null +++ b/shaders/BBMOD_ShTerrain/BBMOD_ShTerrain.fsh @@ -0,0 +1,797 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec2 v_vSplatmapCoord; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// SSAO + +// SSAO texture +uniform sampler2D bbmod_SSAO; + +//////////////////////////////////////////////////////////////////////////////// +// Image based lighting + +// Prefiltered octahedron env. map +uniform sampler2D bbmod_IBL; +// Texel size of one octahedron +uniform vec2 bbmod_IBLTexel; + +//////////////////////////////////////////////////////////////////////////////// +// Punctual lights + +// [(x, y, z, range), (r, g, b, m), ...] +uniform vec4 bbmod_LightPunctualDataA[2 * MAX_PUNCTUAL_LIGHTS]; +// [(isSpotLight, dcosInner, dcosOuter), (dX, dY, dZ), ...] +uniform vec3 bbmod_LightPunctualDataB[2 * MAX_PUNCTUAL_LIGHTS]; + +//////////////////////////////////////////////////////////////////////////////// +// Terrain + +// Splatmap texture +uniform sampler2D bbmod_Splatmap; +// Splatmap channel to read. Use -1 for none. +uniform int bbmod_SplatmapIndex; + +//////////////////////////////////////////////////////////////////////////////// +// Shadow mapping + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnablePS; +// Shadowmap texture +uniform sampler2D bbmod_Shadowmap; +// (1.0/shadowmapWidth, 1.0/shadowmapHeight) +uniform vec2 bbmod_ShadowmapTexel; +// The area that the shadowmap captures +uniform float bbmod_ShadowmapArea; +// The range over which meshes smoothly transition into shadow. +uniform float bbmod_ShadowmapBias; +// The index of the light that casts shadows. Use -1 for the directional light. +uniform float bbmod_ShadowCasterIndex; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + return m; +} + +#define X_PI 3.14159265359 +#define X_2_PI 6.28318530718 + +/// @return x^2 +#define xPow2(x) ((x) * (x)) + +/// @return x^3 +#define xPow3(x) ((x) * (x) * (x)) + +/// @return x^4 +#define xPow4(x) ((x) * (x) * (x) * (x)) + +/// @return x^5 +#define xPow5(x) ((x) * (x) * (x) * (x) * (x)) + +/// @return arctan2(x,y) +#define xAtan2(x, y) atan(y, x) + +/// @return Direction from point `from` to point `to` in degrees (0-360 range). +float xPointDirection(vec2 from, vec2 to) +{ + float x = xAtan2(from.x - to.x, from.y - to.y); + return ((x > 0.0) ? x : (2.0 * X_PI + x)) * 180.0 / X_PI; +} + +/// @desc Default specular color for dielectrics +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +#define X_F0_DEFAULT vec3(0.04, 0.04, 0.04) + +/// @desc Normal distribution function +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularD_GGX(float roughness, float NdotH) +{ + float r = xPow4(roughness); + float a = NdotH * NdotH * (r - 1.0) + 1.0; + return r / (X_PI * a * a); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xSpecularD_Approx(float roughness, float RdotL) +{ + float a = roughness * roughness; + float a2 = a * a; + float rcp_a2 = 1.0 / a2; + // 0.5 / ln(2), 0.275 / ln(2) + float c = (0.72134752 * rcp_a2) + 0.39674113; + return (rcp_a2 * exp2((c * RdotL) - c)); +} + +/// @desc Roughness remapping for analytic lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_Analytic(float roughness) +{ + return xPow2(roughness + 1.0) * 0.125; +} + +/// @desc Roughness remapping for IBL lights. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xK_IBL(float roughness) +{ + return xPow2(roughness) * 0.5; +} + +/// @desc Geometric attenuation +/// @param k Use either xK_Analytic for analytic lights or xK_IBL for image based lighting. +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +float xSpecularG_Schlick(float k, float NdotL, float NdotV) +{ + return (NdotL / (NdotL * (1.0 - k) + k)) + * (NdotV / (NdotV * (1.0 - k) + k)); +} + +/// @desc Fresnel +/// @source https://en.wikipedia.org/wiki/Schlick%27s_approximation +vec3 xSpecularF_Schlick(vec3 f0, float VdotH) +{ + return f0 + (1.0 - f0) * xPow5(1.0 - VdotH); +} + +/// @desc Cook-Torrance microfacet specular shading +/// @note N = normalize(vertexNormal) +/// L = normalize(light - vertex) +/// V = normalize(camera - vertex) +/// H = normalize(L + V) +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xBRDF(vec3 f0, float roughness, float NdotL, float NdotV, float NdotH, float VdotH) +{ + vec3 specular = xSpecularD_GGX(roughness, NdotH) + * xSpecularF_Schlick(f0, VdotH) + * xSpecularG_Schlick(xK_Analytic(roughness), NdotL, NdotH); + return specular / ((4.0 * NdotL * NdotV) + 0.1); +} + +vec3 SpecularGGX(Material m, vec3 N, vec3 V, vec3 L) +{ + vec3 H = normalize(L + V); + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + float NdotH = max(dot(N, H), 0.0); + float VdotH = max(dot(V, H), 0.0); + return xBRDF(m.Specular, m.Roughness, NdotL, NdotV, NdotH, VdotH); +} + +void DoDirectionalLightPS( + vec3 direction, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = normalize(-direction); + float NdotL = max(dot(N, L), 0.0); + color *= (1.0 - shadow) * NdotL; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoPointLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + att *= att; + float NdotL = max(dot(N, L), 0.0); + color *= (1.0 - shadow) * NdotL * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} + +void DoSpotLightPS( + vec3 position, + float range, + vec3 color, + float shadow, + vec3 direction, + float dcosInner, + float dcosOuter, + vec3 vertex, + vec3 N, + vec3 V, + Material m, + inout vec3 diffuse, + inout vec3 specular, + inout vec3 subsurface) +{ + vec3 L = position - vertex; + float dist = length(L); + L = normalize(L); + float att = clamp(1.0 - (dist / range), 0.0, 1.0); + float theta = dot(L, normalize(-direction)); + float epsilon = dcosInner - dcosOuter; + float intensity = clamp((theta - dcosOuter) / epsilon, 0.0, 1.0); + color *= (1.0 - shadow) * intensity * att; + diffuse += color; + specular += color * SpecularGGX(m, N, V, L); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} +// Source: https://gamedev.stackexchange.com/questions/169508/octahedral-impostors-octahedral-mapping + +/// @param dir Sampling dir vector in world-space. +/// @return UV coordinates on an octahedron map. +vec2 xVec3ToOctahedronUv(vec3 dir) +{ + vec3 octant = sign(dir); + float sum = dot(dir, octant); + vec3 octahedron = dir / sum; + if (octahedron.z < 0.0) + { + vec3 absolute = abs(octahedron); + octahedron.xy = octant.xy * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return octahedron.xy * 0.5 + 0.5; +} + +/// @desc Converts octahedron UV into a world-space vector. +vec3 xOctahedronUvToVec3Normalized(vec2 uv) +{ + vec3 position = vec3(2.0 * (uv - 0.5), 0); + vec2 absolute = abs(position.xy); + position.z = 1.0 - absolute.x - absolute.y; + if (position.z < 0.0) + { + position.xy = sign(position.xy) * vec2(1.0 - absolute.y, 1.0 - absolute.x); + } + return position; +} + +vec3 xDiffuseIBL(sampler2D ibl, vec2 texel, vec3 N) +{ + const float s = 1.0 / 8.0; + const float r2 = 7.0; + + vec2 uv0 = xVec3ToOctahedronUv(N); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + return xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +vec2 xEnvBRDFApprox(float roughness, float NdotV) +{ + const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); + vec4 r = (roughness * c0) + c1; + float a004 = (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; + return ((vec2(-1.04, 1.04) * a004) + r.zw); +} + +/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +float xEnvBRDFApproxNonmetal(float roughness, float NdotV) +{ + // Same as EnvBRDFApprox(0.04, Roughness, NdotV) + const vec2 c0 = vec2(-1.0, -0.0275); + const vec2 c1 = vec2(1.0, 0.0425); + vec2 r = (roughness * c0) + c1; + return (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y; +} + +// Fully rough optimization: +// xEnvBRDFApprox(SpecularColor, 1, 1) == SpecularColor * 0.4524 - 0.0024 +// DiffuseColor += SpecularColor * 0.45; +// SpecularColor = 0.0; + +/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +vec3 xSpecularIBL(sampler2D ibl, vec2 texel/*, sampler2D brdf*/, vec3 f0, float roughness, vec3 N, vec3 V) +{ + float NdotV = clamp(dot(N, V), 0.0, 1.0); + vec3 R = 2.0 * dot(V, N) * N - V; + // vec2 envBRDF = texture2D(brdf, vec2(roughness, NdotV)).xy; + vec2 envBRDF = xEnvBRDFApprox(roughness, NdotV); + + const float s = 1.0 / 8.0; + float r = roughness * 7.0; + float r2 = floor(r); + float rDiff = r - r2; + + vec2 uv0 = xVec3ToOctahedronUv(R); + uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s; + uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y); + + vec2 uv1 = uv0; + uv1.x = uv1.x + s; + + vec3 specular = f0 * envBRDF.x + envBRDF.y; + + vec3 col0 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))) * specular; + vec3 col1 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv1))) * specular; + + return mix(col0, col1, rDiff); +} +/// @param tanAspect (tanFovY*(screenWidth/screenHeight),-tanFovY), where +/// tanFovY = dtan(fov*0.5) +/// @param texCoord Sceen-space UV. +/// @param depth Scene depth at texCoord. +/// @return Point projected to view-space. +vec3 xProject(vec2 tanAspect, vec2 texCoord, float depth) +{ + return vec3(tanAspect * (texCoord * 2.0 - 1.0) * depth, depth); +} + +/// @param p A point in clip space (transformed by projection matrix, but not +/// normalized). +/// @return P's UV coordinates on the screen. +vec2 xUnproject(vec4 p) +{ + vec2 uv = p.xy / p.w; + uv = uv * 0.5 + 0.5; +#if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + uv.y = 1.0 - uv.y; +#endif + return uv; +} +/// @param d Linearized depth to encode. +/// @return Encoded depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +vec3 xEncodeDepth(float d) +{ + const float inv255 = 1.0 / 255.0; + vec3 enc; + enc.x = d; + enc.y = d * 255.0; + enc.z = enc.y * 255.0; + enc = fract(enc); + float temp = enc.z * inv255; + enc.x -= enc.y * inv255; + enc.y -= temp; + enc.z -= temp; + return enc; +} + +/// @param c Encoded depth. +/// @return Docoded linear depth. +/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ +float xDecodeDepth(vec3 c) +{ + const float inv255 = 1.0 / 255.0; + return c.x + (c.y * inv255) + (c.z * inv255 * inv255); +} +// Shadowmap filtering source: https://www.gamedev.net/tutorials/programming/graphics/contact-hardening-soft-shadows-made-fast-r4906/ +float InterleavedGradientNoise(vec2 positionScreen) +{ + vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); + return fract(magic.z * fract(dot(positionScreen, magic.xy))); +} +vec2 VogelDiskSample(int sampleIndex, int samplesCount, float phi) +{ + float GoldenAngle = 2.4; + float r = sqrt(float(sampleIndex) + 0.5) / sqrt(float(samplesCount)); + float theta = float(sampleIndex) * GoldenAngle + phi; + float sine = sin(theta); + float cosine = cos(theta); + return vec2(r * cosine, r * sine); +} + +float ShadowMap(sampler2D shadowMap, vec2 texel, vec2 uv, float compareZ) +{ + if (clamp(uv.xy, vec2(0.0), vec2(1.0)) != uv.xy) + { + return 0.0; + } + float shadow = 0.0; + float noise = 6.28 * InterleavedGradientNoise(gl_FragCoord.xy); + float bias = bbmod_ShadowmapBias / bbmod_ShadowmapArea; + for (int i = 0; i < SHADOWMAP_SAMPLE_COUNT; ++i) + { + vec2 uv2 = uv + VogelDiskSample(i, SHADOWMAP_SAMPLE_COUNT, noise) * texel * 4.0; + float depth = xDecodeDepth(texture2D(shadowMap, uv2).rgb); + if (bias != 0.0) + { + shadow += clamp((compareZ - depth) / bias, 0.0, 1.0); + } + else + { + shadow += step(depth, compareZ); + } + } + return (shadow / float(SHADOWMAP_SAMPLE_COUNT)); +} + +void PBRShader(Material material, float depth) +{ + vec3 N = material.Normal; + vec3 V = (v_vEye.w == 1.0) ? v_vEye.xyz : normalize(bbmod_CamPos - v_vVertex); + vec3 lightDiffuse = vec3(0.0); + vec3 lightSpecular = vec3(0.0); + vec3 lightSubsurface = vec3(0.0); + + // Ambient light + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + lightDiffuse += mix(ambientDown, ambientUp, N.z * 0.5 + 0.5); + + // Shadow mapping + float shadow = 0.0; + if (bbmod_ShadowmapEnablePS == 1.0) + { + vec4 shadowmapPos = v_vPosShadowmap; + shadowmapPos.xy /= shadowmapPos.w; + float shadowmapAtt = (bbmod_ShadowCasterIndex == -1.0) + ? clamp((1.0 - length(shadowmapPos.xy)) / 0.1, 0.0, 1.0) + : 1.0; + shadowmapPos.xy = shadowmapPos.xy * 0.5 + 0.5; + #if defined(_YY_HLSL11_) || defined(_YY_PSSL_) + shadowmapPos.y = 1.0 - shadowmapPos.y; + #endif + shadowmapPos.z /= bbmod_ShadowmapArea; + + shadow = ShadowMap(bbmod_Shadowmap, bbmod_ShadowmapTexel, shadowmapPos.xy, shadowmapPos.z) + * shadowmapAtt; + } + + // IBL + lightDiffuse += xDiffuseIBL(bbmod_IBL, bbmod_IBLTexel, N); + lightSpecular += xSpecularIBL(bbmod_IBL, bbmod_IBLTexel, material.Specular, material.Roughness, N, V); + // TODO: Subsurface scattering for IBL + + // Directional light + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + DoDirectionalLightPS( + bbmod_LightDirectionalDir, + directionalLightColor, + (bbmod_ShadowCasterIndex == -1.0) ? shadow : 0.0, + v_vVertex, N, V, material, lightDiffuse, lightSpecular, lightSubsurface); + + // SSAO + float ssao = texture2D(bbmod_SSAO, xUnproject(v_vPosition)).r; + lightDiffuse *= ssao; + lightSpecular *= ssao; + + // Punctual lights + for (int i = 0; i < MAX_PUNCTUAL_LIGHTS; ++i) + { + vec4 positionRange = bbmod_LightPunctualDataA[i * 2]; + vec4 colorAlpha = bbmod_LightPunctualDataA[(i * 2) + 1]; + vec3 isSpotInnerOuter = bbmod_LightPunctualDataB[i * 2]; + vec3 direction = bbmod_LightPunctualDataB[(i * 2) + 1]; + vec3 color = xGammaToLinear(colorAlpha.rgb) * colorAlpha.a; + + if (isSpotInnerOuter.x == 1.0) + { + DoSpotLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + direction, isSpotInnerOuter.y, isSpotInnerOuter.z, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + else + { + DoPointLightPS( + positionRange.xyz, positionRange.w, color, + (bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0, + v_vVertex, N, V, material, + lightDiffuse, lightSpecular, lightSubsurface); + } + } + + // Lightmap + + // Diffuse + gl_FragColor.rgb = material.Base * lightDiffuse; + // Specular + gl_FragColor.rgb += lightSpecular; + // Ambient occlusion + gl_FragColor.rgb *= material.AO; + // Emissive + gl_FragColor.rgb += material.Emissive; + // Subsurface scattering + gl_FragColor.rgb += lightSubsurface; + // Opacity + gl_FragColor.a = material.Opacity; + // Soft particles + // Fog + Fog(depth); + + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + v_mTBN, + v_vTexCoord); + + // Splatmap + vec4 splatmap = texture2D(bbmod_Splatmap, v_vSplatmapCoord); + if (bbmod_SplatmapIndex >= 0) + { + // splatmap[bbmod_SplatmapIndex] does not work in HTML5 + material.Opacity *= ((bbmod_SplatmapIndex == 0) ? splatmap.r + : ((bbmod_SplatmapIndex == 1) ? splatmap.g + : ((bbmod_SplatmapIndex == 2) ? splatmap.b + : splatmap.a))); + } + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + PBRShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShTerrain/BBMOD_ShTerrain.vsh b/shaders/BBMOD_ShTerrain/BBMOD_ShTerrain.vsh new file mode 100644 index 000000000..74c96516e --- /dev/null +++ b/shaders/BBMOD_ShTerrain/BBMOD_ShTerrain.vsh @@ -0,0 +1,120 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// +uniform mat4 bbmod_NormalMatrix; + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +// 1.0 to enable shadows +uniform float bbmod_ShadowmapEnableVS; +// WORLD_VIEW_PROJECTION matrix used when rendering shadowmap +uniform mat4 bbmod_ShadowmapMatrix; +// Offsets vertex position by its normal scaled by this value +uniform float bbmod_ShadowmapNormalOffset; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec2 v_vSplatmapCoord; + +varying vec4 v_vEye; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((bbmod_NormalMatrix * vec4(normal, 0.0)).xyz); + tangent = normalize((bbmod_NormalMatrix * vec4(tangent, 0.0)).xyz); + bitangent = normalize((bbmod_NormalMatrix * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_vEye.xyz = normalize(-vec3( + gm_Matrices[MATRIX_VIEW][0][2], + gm_Matrices[MATRIX_VIEW][1][2], + gm_Matrices[MATRIX_VIEW][2][2] + )); + v_vEye.w = (gm_Matrices[MATRIX_PROJECTION][2][3] == 0.0) ? 1.0 : 0.0; + + v_mTBN = mat3(tangent, bitangent, normal); + + v_vSplatmapCoord = in_TextureCoord0; + + //////////////////////////////////////////////////////////////////////////// + // Vertex position in shadowmap + if (bbmod_ShadowmapEnableVS == 1.0) + { + v_vPosShadowmap = bbmod_ShadowmapMatrix + * vec4(v_vVertex + normal * bbmod_ShadowmapNormalOffset, 1.0); + } +} diff --git a/shaders/BBMOD_ShTerrain/BBMOD_ShTerrain.yy b/shaders/BBMOD_ShTerrain/BBMOD_ShTerrain.yy new file mode 100644 index 000000000..149dcccce --- /dev/null +++ b/shaders/BBMOD_ShTerrain/BBMOD_ShTerrain.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShTerrain", + "parent": { + "name": "Terrain", + "path": "folders/_Extensions/BBMOD/Terrain.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/BBMOD_ShTerrainUnlit/BBMOD_ShTerrainUnlit.fsh b/shaders/BBMOD_ShTerrainUnlit/BBMOD_ShTerrainUnlit.fsh new file mode 100644 index 000000000..173897a5d --- /dev/null +++ b/shaders/BBMOD_ShTerrainUnlit/BBMOD_ShTerrainUnlit.fsh @@ -0,0 +1,313 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of point lights +#define MAX_PUNCTUAL_LIGHTS 8 +// Number of samples used when computing shadows +#define SHADOWMAP_SAMPLE_COUNT 12 + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// + +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec2 v_vSplatmapCoord; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// + +//////////////////////////////////////////////////////////////////////////////// +// Material + +// Material index +// uniform float bbmod_MaterialIndex; + +// RGB: Base color, A: Opacity +#define bbmod_BaseOpacity gm_BaseTexture + +// RGBA +uniform vec4 bbmod_BaseOpacityMultiplier; + +// If 1.0 then the material uses roughness +uniform float bbmod_IsRoughness; +// If 1.0 then the material uses metallic workflow +uniform float bbmod_IsMetallic; +// RGB: Tangent-space normal, A: Smoothness or roughness +uniform sampler2D bbmod_NormalW; +// RGB: specular color / R: Metallic, G: ambient occlusion +uniform sampler2D bbmod_Material; + +// Pixels with alpha less than this value will be discarded +uniform float bbmod_AlphaTest; + +//////////////////////////////////////////////////////////////////////////////// +// Camera + +// Camera's position in world space +uniform vec3 bbmod_CamPos; +// Distance to the far clipping plane +uniform float bbmod_ZFar; +// Camera's exposure value +uniform float bbmod_Exposure; + +//////////////////////////////////////////////////////////////////////////////// +// Fog + +// The color of the fog +uniform vec4 bbmod_FogColor; +// Maximum fog intensity +uniform float bbmod_FogIntensity; +// Distance at which the fog starts +uniform float bbmod_FogStart; +// 1.0 / (fogEnd - fogStart) +uniform float bbmod_FogRcpRange; + +//////////////////////////////////////////////////////////////////////////////// +// Ambient light + +// RGBM encoded ambient light color on the upper hemisphere. +uniform vec4 bbmod_LightAmbientUp; +// RGBM encoded ambient light color on the lower hemisphere. +uniform vec4 bbmod_LightAmbientDown; + +//////////////////////////////////////////////////////////////////////////////// +// Directional light + +// Direction of the directional light +uniform vec3 bbmod_LightDirectionalDir; +// RGBM encoded color of the directional light +uniform vec4 bbmod_LightDirectionalColor; + +//////////////////////////////////////////////////////////////////////////////// +// Terrain + +// Splatmap texture +uniform sampler2D bbmod_Splatmap; +// Splatmap channel to read. Use -1 for none. +uniform int bbmod_SplatmapIndex; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// +struct Material +{ + vec3 Base; + float Opacity; + vec3 Normal; + float Metallic; + float Roughness; + vec3 Specular; + float Smoothness; + float SpecularPower; + float AO; + vec3 Emissive; + vec4 Subsurface; + vec3 Lightmap; +}; + +Material CreateMaterial(mat3 TBN) +{ + Material m; + m.Base = vec3(1.0); + m.Opacity = 1.0; + m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0)); + m.Metallic = 0.0; + m.Roughness = 1.0; + m.Specular = vec3(0.0); + m.Smoothness = 0.0; + m.SpecularPower = 1.0; + m.AO = 1.0; + m.Emissive = vec3(0.0); + m.Subsurface = vec4(0.0); + m.Lightmap = vec3(0.0); + return m; +} +#define F0_DEFAULT vec3(0.04) +#define X_GAMMA 2.2 + +/// @desc Converts gamma space color to linear space. +vec3 xGammaToLinear(vec3 rgb) +{ + return pow(rgb, vec3(X_GAMMA)); +} + +/// @desc Converts linear space color to gamma space. +vec3 xLinearToGamma(vec3 rgb) +{ + return pow(rgb, vec3(1.0 / X_GAMMA)); +} + +/// @desc Gets color's luminance. +float xLuminance(vec3 rgb) +{ + return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b); +} +/// @note Input color should be in gamma space. +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec4 xEncodeRGBM(vec3 color) +{ + vec4 rgbm; + color *= 1.0 / 6.0; + rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0); + rgbm.a = ceil(rgbm.a * 255.0) / 255.0; + rgbm.rgb = color / rgbm.a; + return rgbm; +} + +/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html +vec3 xDecodeRGBM(vec4 rgbm) +{ + return 6.0 * rgbm.rgb * rgbm.a; +} + +/// @desc Unpacks material from textures. +/// @param texBaseOpacity RGB: base color, A: opacity +/// @param isRoughness +/// @param texNormalW +/// @param isMetallic +/// @param texMaterial +/// @param texSubsurface RGB: subsurface color, A: intensity +/// @param texEmissive RGBA: RGBM encoded emissive color +/// @param texLightmap RGBA: RGBM encoded lightmap +/// @param uvLightmap Lightmap texture coordinates +/// @param TBN Tangent-bitangent-normal matrix +/// @param uv Texture coordinates +Material UnpackMaterial( + sampler2D texBaseOpacity, + float isRoughness, + sampler2D texNormalW, + float isMetallic, + sampler2D texMaterial, + mat3 TBN, + vec2 uv) +{ + Material m = CreateMaterial(TBN); + + // Base color and opacity + vec4 baseOpacity = texture2D(texBaseOpacity, + uv + ); + m.Base = xGammaToLinear(baseOpacity.rgb); + m.Opacity = baseOpacity.a; + + // Normal vector and smoothness/roughness + vec4 normalW = texture2D(texNormalW, + uv + ); + m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0)); + + if (isRoughness == 1.0) + { + m.Roughness = mix(0.1, 0.9, normalW.a); + m.Smoothness = 1.0 - m.Roughness; + } + else + { + m.Smoothness = mix(0.1, 0.9, normalW.a); + m.Roughness = 1.0 - m.Smoothness; + } + + // Material properties + vec4 materialProps = texture2D(texMaterial, + uv + ); + + if (isMetallic == 1.0) + { + m.Metallic = materialProps.r; + m.AO = materialProps.g; + m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic); + m.Base *= (1.0 - m.Metallic); + } + else + { + m.Specular = materialProps.rgb; + m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0)); + } + + return m; +} + +void Fog(float depth) +{ + vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a; + vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a; + vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a; + vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor); + float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a; + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity); +} +void Exposure() +{ + gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure); +} + +void GammaCorrect() +{ + gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb); +} + +void UnlitShader(Material material, float depth) +{ + gl_FragColor.rgb = material.Base; + gl_FragColor.rgb += material.Emissive; + gl_FragColor.a = material.Opacity; + // Soft particles + Fog(depth); + Exposure(); + GammaCorrect(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + Material material = UnpackMaterial( + bbmod_BaseOpacity, + bbmod_IsRoughness, + bbmod_NormalW, + bbmod_IsMetallic, + bbmod_Material, + v_mTBN, + v_vTexCoord); + + // Splatmap + vec4 splatmap = texture2D(bbmod_Splatmap, v_vSplatmapCoord); + if (bbmod_SplatmapIndex >= 0) + { + // splatmap[bbmod_SplatmapIndex] does not work in HTML5 + material.Opacity *= ((bbmod_SplatmapIndex == 0) ? splatmap.r + : ((bbmod_SplatmapIndex == 1) ? splatmap.g + : ((bbmod_SplatmapIndex == 2) ? splatmap.b + : splatmap.a))); + } + + material.Base *= bbmod_BaseOpacityMultiplier.rgb; + material.Opacity *= bbmod_BaseOpacityMultiplier.a; + + if (material.Opacity < bbmod_AlphaTest) + { + discard; + } + + UnlitShader(material, v_vPosition.z); + +} diff --git a/shaders/BBMOD_ShTerrainUnlit/BBMOD_ShTerrainUnlit.vsh b/shaders/BBMOD_ShTerrainUnlit/BBMOD_ShTerrainUnlit.vsh new file mode 100644 index 000000000..d1f9da3f6 --- /dev/null +++ b/shaders/BBMOD_ShTerrainUnlit/BBMOD_ShTerrainUnlit.vsh @@ -0,0 +1,97 @@ +// FIXME: Temporary fix! +precision highp float; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +// Maximum number of bones of animated models +#define BBMOD_MAX_BONES 128 +// Maximum number of vec4 uniforms for dynamic batch data +#define BBMOD_MAX_BATCH_VEC4S 192 + +//////////////////////////////////////////////////////////////////////////////// +// +// Attributes +// +attribute vec4 in_Position; + +attribute vec3 in_Normal; + +attribute vec2 in_TextureCoord0; + +attribute vec4 in_TangentW; + +//////////////////////////////////////////////////////////////////////////////// +// +// Uniforms +// +uniform mat4 bbmod_NormalMatrix; + +uniform vec2 bbmod_TextureOffset; +uniform vec2 bbmod_TextureScale; + +//////////////////////////////////////////////////////////////////////////////// +// +// Varyings +// +varying vec3 v_vVertex; + +varying vec2 v_vTexCoord; +varying mat3 v_mTBN; +varying vec4 v_vPosition; + +varying vec4 v_vPosShadowmap; + +varying vec2 v_vSplatmapCoord; + +//////////////////////////////////////////////////////////////////////////////// +// +// Includes +// + +/// @desc Transforms vertex and normal by animation and/or batch data. +/// +/// @param vertex Variable to hold the transformed vertex. +/// @param normal Variable to hold the transformed normal. +/// @param tangent Variable to hold the transformed tangent. +/// @param bitangent Variable to hold the transformed bitangent. +void Transform( + inout vec4 vertex, + inout vec3 normal, + inout vec3 tangent, + inout vec3 bitangent) +{ + + vertex = gm_Matrices[MATRIX_WORLD] * vertex; + normal = normalize((bbmod_NormalMatrix * vec4(normal, 0.0)).xyz); + tangent = normalize((bbmod_NormalMatrix * vec4(tangent, 0.0)).xyz); + bitangent = normalize((bbmod_NormalMatrix * vec4(bitangent, 0.0)).xyz); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Main +// +void main() +{ + vec4 position = in_Position; + vec3 normal = in_Normal; + vec3 tangent = in_TangentW.xyz; + vec3 bitangent = cross(normal, tangent) * in_TangentW.w; + + Transform(position, normal, tangent, bitangent); + + vec4 positionWVP = gm_Matrices[MATRIX_PROJECTION] * (gm_Matrices[MATRIX_VIEW] * position); + v_vVertex = position.xyz; + + gl_Position = positionWVP; + v_vPosition = positionWVP; + v_vTexCoord = bbmod_TextureOffset + in_TextureCoord0 * bbmod_TextureScale; + + v_mTBN = mat3(tangent, bitangent, normal); + + v_vSplatmapCoord = in_TextureCoord0; + +} diff --git a/shaders/BBMOD_ShTerrainUnlit/BBMOD_ShTerrainUnlit.yy b/shaders/BBMOD_ShTerrainUnlit/BBMOD_ShTerrainUnlit.yy new file mode 100644 index 000000000..cd9361051 --- /dev/null +++ b/shaders/BBMOD_ShTerrainUnlit/BBMOD_ShTerrainUnlit.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "BBMOD_ShTerrainUnlit", + "parent": { + "name": "Terrain", + "path": "folders/_Extensions/BBMOD/Terrain.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/shaders/__BBMOD_ShCheckVTF/__BBMOD_ShCheckVTF.fsh b/shaders/__BBMOD_ShCheckVTF/__BBMOD_ShCheckVTF.fsh new file mode 100644 index 000000000..ead291a4e --- /dev/null +++ b/shaders/__BBMOD_ShCheckVTF/__BBMOD_ShCheckVTF.fsh @@ -0,0 +1,6 @@ +varying vec4 v_vColour; + +void main() +{ + gl_FragColor = v_vColour; +} diff --git a/shaders/__BBMOD_ShCheckVTF/__BBMOD_ShCheckVTF.vsh b/shaders/__BBMOD_ShCheckVTF/__BBMOD_ShCheckVTF.vsh new file mode 100644 index 000000000..d24ad91a5 --- /dev/null +++ b/shaders/__BBMOD_ShCheckVTF/__BBMOD_ShCheckVTF.vsh @@ -0,0 +1,12 @@ +attribute vec4 in_Position; +attribute vec2 in_TextureCoord; + +varying vec4 v_vColour; + +uniform sampler2D u_texTest; + +void main() +{ + gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position; + v_vColour = texture2DLod(u_texTest, in_TextureCoord, 0.0); +} diff --git a/shaders/__BBMOD_ShCheckVTF/__BBMOD_ShCheckVTF.yy b/shaders/__BBMOD_ShCheckVTF/__BBMOD_ShCheckVTF.yy new file mode 100644 index 000000000..ee311b01b --- /dev/null +++ b/shaders/__BBMOD_ShCheckVTF/__BBMOD_ShCheckVTF.yy @@ -0,0 +1,10 @@ +{ + "resourceType": "GMShader", + "resourceVersion": "1.0", + "name": "__BBMOD_ShCheckVTF", + "parent": { + "name": "Utils", + "path": "folders/_Extensions/BBMOD/Core/Utils.yy", + }, + "type": 1, +} \ No newline at end of file diff --git a/sprites/BBMOD_SprBlack/2c48178d-8c71-49c0-98e1-a2f4e1a5189b.png b/sprites/BBMOD_SprBlack/2c48178d-8c71-49c0-98e1-a2f4e1a5189b.png new file mode 100644 index 000000000..8ef45ceab Binary files /dev/null and b/sprites/BBMOD_SprBlack/2c48178d-8c71-49c0-98e1-a2f4e1a5189b.png differ diff --git a/sprites/BBMOD_SprBlack/BBMOD_SprBlack.yy b/sprites/BBMOD_SprBlack/BBMOD_SprBlack.yy new file mode 100644 index 000000000..c0a8be188 --- /dev/null +++ b/sprites/BBMOD_SprBlack/BBMOD_SprBlack.yy @@ -0,0 +1,77 @@ +{ + "resourceType": "GMSprite", + "resourceVersion": "1.0", + "name": "BBMOD_SprBlack", + "bbox_bottom": 0, + "bbox_left": 0, + "bbox_right": 0, + "bbox_top": 0, + "bboxMode": 0, + "collisionKind": 1, + "collisionTolerance": 0, + "ConfigValues": { + "Itch": {"textureGroupId":"{\"name\":\"Default\",\"path\":\"texturegroups/Default\"}",}, + }, + "DynamicTexturePage": false, + "edgeFiltering": false, + "For3D": true, + "frames": [ + {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"2c48178d-8c71-49c0-98e1-a2f4e1a5189b",}, + ], + "gridX": 0, + "gridY": 0, + "height": 1, + "HTile": false, + "layers": [ + {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"d29f13d3-8eff-440e-989a-926bdce1dac5","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,}, + ], + "nineSlice": null, + "origin": 0, + "parent": { + "name": "Core", + "path": "folders/_Extensions/BBMOD/Core.yy", + }, + "preMultiplyAlpha": false, + "sequence": { + "resourceType": "GMSequence", + "resourceVersion": "1.4", + "name": "BBMOD_SprBlack", + "autoRecord": true, + "backdropHeight": 768, + "backdropImageOpacity": 0.5, + "backdropImagePath": "", + "backdropWidth": 1366, + "backdropXOffset": 0.0, + "backdropYOffset": 0.0, + "events": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "eventStubScript": null, + "eventToFunction": {}, + "length": 1.0, + "lockOrigin": false, + "moments": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "playback": 1, + "playbackSpeed": 30.0, + "playbackSpeedType": 0, + "showBackdrop": true, + "showBackdropImage": false, + "timeUnits": 1, + "tracks": [ + {"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[ + {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"2c48178d-8c71-49c0-98e1-a2f4e1a5189b","path":"sprites/BBMOD_SprBlack/BBMOD_SprBlack.yy",},},},"Disabled":false,"id":"a56bb709-64a1-4d2d-a372-6df57f35c6b8","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,}, + ],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,}, + ], + "visibleRange": null, + "volume": 1.0, + "xorigin": 0, + "yorigin": 0, + }, + "swatchColours": null, + "swfPrecision": 2.525, + "textureGroupId": { + "name": "Default", + "path": "texturegroups/Default", + }, + "type": 0, + "VTile": false, + "width": 1, +} \ No newline at end of file diff --git a/sprites/BBMOD_SprBlack/layers/2c48178d-8c71-49c0-98e1-a2f4e1a5189b/d29f13d3-8eff-440e-989a-926bdce1dac5.png b/sprites/BBMOD_SprBlack/layers/2c48178d-8c71-49c0-98e1-a2f4e1a5189b/d29f13d3-8eff-440e-989a-926bdce1dac5.png new file mode 100644 index 000000000..8ef45ceab Binary files /dev/null and b/sprites/BBMOD_SprBlack/layers/2c48178d-8c71-49c0-98e1-a2f4e1a5189b/d29f13d3-8eff-440e-989a-926bdce1dac5.png differ diff --git a/sprites/BBMOD_SprColorGradingLUT/9c1088e4-4abf-42a7-ba51-7768972da5f4.png b/sprites/BBMOD_SprColorGradingLUT/9c1088e4-4abf-42a7-ba51-7768972da5f4.png new file mode 100644 index 000000000..c47824630 Binary files /dev/null and b/sprites/BBMOD_SprColorGradingLUT/9c1088e4-4abf-42a7-ba51-7768972da5f4.png differ diff --git a/sprites/BBMOD_SprColorGradingLUT/BBMOD_SprColorGradingLUT.yy b/sprites/BBMOD_SprColorGradingLUT/BBMOD_SprColorGradingLUT.yy new file mode 100644 index 000000000..c2f80ddfd --- /dev/null +++ b/sprites/BBMOD_SprColorGradingLUT/BBMOD_SprColorGradingLUT.yy @@ -0,0 +1,100 @@ +{ + "resourceType": "GMSprite", + "resourceVersion": "1.0", + "name": "BBMOD_SprColorGradingLUT", + "bbox_bottom": 15, + "bbox_left": 0, + "bbox_right": 255, + "bbox_top": 0, + "bboxMode": 0, + "collisionKind": 1, + "collisionTolerance": 0, + "ConfigValues": { + "Itch": {"textureGroupId":"{\"name\":\"Default\",\"path\":\"texturegroups/Default\"}",}, + }, + "DynamicTexturePage": false, + "edgeFiltering": false, + "For3D": true, + "frames": [ + {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"9c1088e4-4abf-42a7-ba51-7768972da5f4",}, + ], + "gridX": 0, + "gridY": 0, + "height": 16, + "HTile": false, + "layers": [ + {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"609ab95a-bc6f-4dd9-8bd2-12e84733b60c","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,}, + ], + "nineSlice": { + "resourceType": "GMNineSliceData", + "resourceVersion": "1.0", + "bottom": 0, + "enabled": false, + "guideColour": [ + 4294902015, + 4294902015, + 4294902015, + 4294902015, + ], + "highlightColour": 1728023040, + "highlightStyle": 0, + "left": 0, + "right": 0, + "tileMode": [ + 0, + 0, + 0, + 0, + 0, + ], + "top": 0, + }, + "origin": 0, + "parent": { + "name": "PostProcessing", + "path": "folders/_Extensions/BBMOD/Rendering/PostProcessing.yy", + }, + "preMultiplyAlpha": false, + "sequence": { + "resourceType": "GMSequence", + "resourceVersion": "1.4", + "name": "BBMOD_SprColorGradingLUT", + "autoRecord": true, + "backdropHeight": 768, + "backdropImageOpacity": 0.5, + "backdropImagePath": "", + "backdropWidth": 1366, + "backdropXOffset": 0.0, + "backdropYOffset": 0.0, + "events": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "eventStubScript": null, + "eventToFunction": {}, + "length": 1.0, + "lockOrigin": false, + "moments": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "playback": 1, + "playbackSpeed": 30.0, + "playbackSpeedType": 0, + "showBackdrop": true, + "showBackdropImage": false, + "timeUnits": 1, + "tracks": [ + {"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[ + {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"9c1088e4-4abf-42a7-ba51-7768972da5f4","path":"sprites/BBMOD_SprColorGradingLUT/BBMOD_SprColorGradingLUT.yy",},},},"Disabled":false,"id":"5de23214-9ac5-48eb-a9d3-e3e223a66791","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,}, + ],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,}, + ], + "visibleRange": null, + "volume": 1.0, + "xorigin": 0, + "yorigin": 0, + }, + "swatchColours": null, + "swfPrecision": 2.525, + "textureGroupId": { + "name": "Default", + "path": "texturegroups/Default", + }, + "type": 0, + "VTile": false, + "width": 256, +} \ No newline at end of file diff --git a/sprites/BBMOD_SprColorGradingLUT/layers/9c1088e4-4abf-42a7-ba51-7768972da5f4/609ab95a-bc6f-4dd9-8bd2-12e84733b60c.png b/sprites/BBMOD_SprColorGradingLUT/layers/9c1088e4-4abf-42a7-ba51-7768972da5f4/609ab95a-bc6f-4dd9-8bd2-12e84733b60c.png new file mode 100644 index 000000000..c47824630 Binary files /dev/null and b/sprites/BBMOD_SprColorGradingLUT/layers/9c1088e4-4abf-42a7-ba51-7768972da5f4/609ab95a-bc6f-4dd9-8bd2-12e84733b60c.png differ diff --git a/sprites/BBMOD_SprDefaultBaseOpacity/BBMOD_SprDefaultBaseOpacity.yy b/sprites/BBMOD_SprDefaultBaseOpacity/BBMOD_SprDefaultBaseOpacity.yy new file mode 100644 index 000000000..1c822c97f --- /dev/null +++ b/sprites/BBMOD_SprDefaultBaseOpacity/BBMOD_SprDefaultBaseOpacity.yy @@ -0,0 +1,77 @@ +{ + "resourceType": "GMSprite", + "resourceVersion": "1.0", + "name": "BBMOD_SprDefaultBaseOpacity", + "bbox_bottom": 63, + "bbox_left": 0, + "bbox_right": 63, + "bbox_top": 0, + "bboxMode": 0, + "collisionKind": 1, + "collisionTolerance": 0, + "ConfigValues": { + "Itch": {"textureGroupId":"{\"name\":\"Default\",\"path\":\"texturegroups/Default\"}",}, + }, + "DynamicTexturePage": false, + "edgeFiltering": false, + "For3D": true, + "frames": [ + {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"f32abfde-987f-47df-8dc5-6501ccd1cffe",}, + ], + "gridX": 0, + "gridY": 0, + "height": 64, + "HTile": false, + "layers": [ + {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"7871b65d-a5ff-4d19-bcb3-5887144e2eca","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,}, + ], + "nineSlice": null, + "origin": 0, + "parent": { + "name": "Sprites", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Sprites.yy", + }, + "preMultiplyAlpha": false, + "sequence": { + "resourceType": "GMSequence", + "resourceVersion": "1.4", + "name": "BBMOD_SprDefaultBaseOpacity", + "autoRecord": true, + "backdropHeight": 1080, + "backdropImageOpacity": 0.5, + "backdropImagePath": "", + "backdropWidth": 1920, + "backdropXOffset": 0.0, + "backdropYOffset": 0.0, + "events": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "eventStubScript": null, + "eventToFunction": {}, + "length": 1.0, + "lockOrigin": false, + "moments": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "playback": 1, + "playbackSpeed": 15.0, + "playbackSpeedType": 0, + "showBackdrop": true, + "showBackdropImage": false, + "timeUnits": 1, + "tracks": [ + {"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[ + {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"f32abfde-987f-47df-8dc5-6501ccd1cffe","path":"sprites/BBMOD_SprDefaultBaseOpacity/BBMOD_SprDefaultBaseOpacity.yy",},},},"Disabled":false,"id":"7021cfb7-96b5-4338-ae5a-dc725c69dc88","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,}, + ],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,}, + ], + "visibleRange": null, + "volume": 1.0, + "xorigin": 0, + "yorigin": 0, + }, + "swatchColours": null, + "swfPrecision": 2.525, + "textureGroupId": { + "name": "Default", + "path": "texturegroups/Default", + }, + "type": 0, + "VTile": false, + "width": 64, +} \ No newline at end of file diff --git a/sprites/BBMOD_SprDefaultBaseOpacity/f32abfde-987f-47df-8dc5-6501ccd1cffe.png b/sprites/BBMOD_SprDefaultBaseOpacity/f32abfde-987f-47df-8dc5-6501ccd1cffe.png new file mode 100644 index 000000000..c14d8e7ba Binary files /dev/null and b/sprites/BBMOD_SprDefaultBaseOpacity/f32abfde-987f-47df-8dc5-6501ccd1cffe.png differ diff --git a/sprites/BBMOD_SprDefaultBaseOpacity/layers/f32abfde-987f-47df-8dc5-6501ccd1cffe/7871b65d-a5ff-4d19-bcb3-5887144e2eca.png b/sprites/BBMOD_SprDefaultBaseOpacity/layers/f32abfde-987f-47df-8dc5-6501ccd1cffe/7871b65d-a5ff-4d19-bcb3-5887144e2eca.png new file mode 100644 index 000000000..c14d8e7ba Binary files /dev/null and b/sprites/BBMOD_SprDefaultBaseOpacity/layers/f32abfde-987f-47df-8dc5-6501ccd1cffe/7871b65d-a5ff-4d19-bcb3-5887144e2eca.png differ diff --git a/sprites/BBMOD_SprDefaultNormalW/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4.png b/sprites/BBMOD_SprDefaultNormalW/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4.png new file mode 100644 index 000000000..97d7bab32 Binary files /dev/null and b/sprites/BBMOD_SprDefaultNormalW/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4.png differ diff --git a/sprites/BBMOD_SprDefaultNormalW/BBMOD_SprDefaultNormalW.yy b/sprites/BBMOD_SprDefaultNormalW/BBMOD_SprDefaultNormalW.yy new file mode 100644 index 000000000..cc68d05a9 --- /dev/null +++ b/sprites/BBMOD_SprDefaultNormalW/BBMOD_SprDefaultNormalW.yy @@ -0,0 +1,77 @@ +{ + "resourceType": "GMSprite", + "resourceVersion": "1.0", + "name": "BBMOD_SprDefaultNormalW", + "bbox_bottom": 0, + "bbox_left": 0, + "bbox_right": 0, + "bbox_top": 0, + "bboxMode": 0, + "collisionKind": 1, + "collisionTolerance": 0, + "ConfigValues": { + "Itch": {"textureGroupId":"{\"name\":\"Default\",\"path\":\"texturegroups/Default\"}",}, + }, + "DynamicTexturePage": false, + "edgeFiltering": false, + "For3D": true, + "frames": [ + {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"2c0dbe20-e924-4d3b-bd43-b7fcd89299e4",}, + ], + "gridX": 0, + "gridY": 0, + "height": 1, + "HTile": false, + "layers": [ + {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"ddc0cec4-9ac7-4182-80c8-ca47f583360f","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,}, + ], + "nineSlice": null, + "origin": 0, + "parent": { + "name": "Sprites", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Sprites.yy", + }, + "preMultiplyAlpha": false, + "sequence": { + "resourceType": "GMSequence", + "resourceVersion": "1.4", + "name": "BBMOD_SprDefaultNormalW", + "autoRecord": true, + "backdropHeight": 768, + "backdropImageOpacity": 0.5, + "backdropImagePath": "", + "backdropWidth": 1366, + "backdropXOffset": 0.0, + "backdropYOffset": 0.0, + "events": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "eventStubScript": null, + "eventToFunction": {}, + "length": 1.0, + "lockOrigin": false, + "moments": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "playback": 1, + "playbackSpeed": 30.0, + "playbackSpeedType": 0, + "showBackdrop": true, + "showBackdropImage": false, + "timeUnits": 1, + "tracks": [ + {"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[ + {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"2c0dbe20-e924-4d3b-bd43-b7fcd89299e4","path":"sprites/BBMOD_SprDefaultNormalW/BBMOD_SprDefaultNormalW.yy",},},},"Disabled":false,"id":"d77b6cf4-83e7-4cd4-a27b-a39d0038944b","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,}, + ],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,}, + ], + "visibleRange": null, + "volume": 1.0, + "xorigin": 0, + "yorigin": 0, + }, + "swatchColours": null, + "swfPrecision": 2.525, + "textureGroupId": { + "name": "Default", + "path": "texturegroups/Default", + }, + "type": 0, + "VTile": false, + "width": 1, +} \ No newline at end of file diff --git a/sprites/BBMOD_SprDefaultNormalW/layers/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4/ddc0cec4-9ac7-4182-80c8-ca47f583360f.png b/sprites/BBMOD_SprDefaultNormalW/layers/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4/ddc0cec4-9ac7-4182-80c8-ca47f583360f.png new file mode 100644 index 000000000..97d7bab32 Binary files /dev/null and b/sprites/BBMOD_SprDefaultNormalW/layers/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4/ddc0cec4-9ac7-4182-80c8-ca47f583360f.png differ diff --git a/sprites/BBMOD_SprDefaultSpecularColor/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4.png b/sprites/BBMOD_SprDefaultSpecularColor/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4.png new file mode 100644 index 000000000..f9dfed575 Binary files /dev/null and b/sprites/BBMOD_SprDefaultSpecularColor/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4.png differ diff --git a/sprites/BBMOD_SprDefaultSpecularColor/BBMOD_SprDefaultSpecularColor.yy b/sprites/BBMOD_SprDefaultSpecularColor/BBMOD_SprDefaultSpecularColor.yy new file mode 100644 index 000000000..3479c0e8d --- /dev/null +++ b/sprites/BBMOD_SprDefaultSpecularColor/BBMOD_SprDefaultSpecularColor.yy @@ -0,0 +1,77 @@ +{ + "resourceType": "GMSprite", + "resourceVersion": "1.0", + "name": "BBMOD_SprDefaultSpecularColor", + "bbox_bottom": 0, + "bbox_left": 0, + "bbox_right": 0, + "bbox_top": 0, + "bboxMode": 0, + "collisionKind": 1, + "collisionTolerance": 0, + "ConfigValues": { + "Itch": {"textureGroupId":"{\"name\":\"Default\",\"path\":\"texturegroups/Default\"}",}, + }, + "DynamicTexturePage": false, + "edgeFiltering": false, + "For3D": true, + "frames": [ + {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"2c0dbe20-e924-4d3b-bd43-b7fcd89299e4",}, + ], + "gridX": 0, + "gridY": 0, + "height": 1, + "HTile": false, + "layers": [ + {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"ddc0cec4-9ac7-4182-80c8-ca47f583360f","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,}, + ], + "nineSlice": null, + "origin": 0, + "parent": { + "name": "Sprites", + "path": "folders/_Extensions/BBMOD/Core/DefaultRenderer/Sprites.yy", + }, + "preMultiplyAlpha": false, + "sequence": { + "resourceType": "GMSequence", + "resourceVersion": "1.4", + "name": "BBMOD_SprDefaultSpecularColor", + "autoRecord": true, + "backdropHeight": 768, + "backdropImageOpacity": 0.5, + "backdropImagePath": "", + "backdropWidth": 1366, + "backdropXOffset": 0.0, + "backdropYOffset": 0.0, + "events": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "eventStubScript": null, + "eventToFunction": {}, + "length": 1.0, + "lockOrigin": false, + "moments": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "playback": 1, + "playbackSpeed": 30.0, + "playbackSpeedType": 0, + "showBackdrop": true, + "showBackdropImage": false, + "timeUnits": 1, + "tracks": [ + {"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[ + {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"2c0dbe20-e924-4d3b-bd43-b7fcd89299e4","path":"sprites/BBMOD_SprDefaultSpecularColor/BBMOD_SprDefaultSpecularColor.yy",},},},"Disabled":false,"id":"05d4b02e-509e-41ca-882a-abecf37dd045","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,}, + ],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,}, + ], + "visibleRange": null, + "volume": 1.0, + "xorigin": 0, + "yorigin": 0, + }, + "swatchColours": null, + "swfPrecision": 2.525, + "textureGroupId": { + "name": "Default", + "path": "texturegroups/Default", + }, + "type": 0, + "VTile": false, + "width": 1, +} \ No newline at end of file diff --git a/sprites/BBMOD_SprDefaultSpecularColor/layers/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4/ddc0cec4-9ac7-4182-80c8-ca47f583360f.png b/sprites/BBMOD_SprDefaultSpecularColor/layers/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4/ddc0cec4-9ac7-4182-80c8-ca47f583360f.png new file mode 100644 index 000000000..f9dfed575 Binary files /dev/null and b/sprites/BBMOD_SprDefaultSpecularColor/layers/2c0dbe20-e924-4d3b-bd43-b7fcd89299e4/ddc0cec4-9ac7-4182-80c8-ca47f583360f.png differ diff --git a/sprites/BBMOD_SprGizmo/198f2eb3-626a-4665-a2b0-881cce537b25.png b/sprites/BBMOD_SprGizmo/198f2eb3-626a-4665-a2b0-881cce537b25.png new file mode 100644 index 000000000..a394bba06 Binary files /dev/null and b/sprites/BBMOD_SprGizmo/198f2eb3-626a-4665-a2b0-881cce537b25.png differ diff --git a/sprites/BBMOD_SprGizmo/BBMOD_SprGizmo.yy b/sprites/BBMOD_SprGizmo/BBMOD_SprGizmo.yy new file mode 100644 index 000000000..9ca9b9a2c --- /dev/null +++ b/sprites/BBMOD_SprGizmo/BBMOD_SprGizmo.yy @@ -0,0 +1,79 @@ +{ + "resourceType": "GMSprite", + "resourceVersion": "1.0", + "name": "BBMOD_SprGizmo", + "bbox_bottom": 255, + "bbox_left": 0, + "bbox_right": 255, + "bbox_top": 0, + "bboxMode": 0, + "collisionKind": 1, + "collisionTolerance": 0, + "ConfigValues": { + "Itch": {"textureGroupId":"{\"name\":\"Default\",\"path\":\"texturegroups/Default\"}",}, + }, + "DynamicTexturePage": false, + "edgeFiltering": false, + "For3D": true, + "frames": [ + {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"f6406058-c156-411a-b6b4-53268f2116b4",}, + {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"198f2eb3-626a-4665-a2b0-881cce537b25",}, + ], + "gridX": 0, + "gridY": 0, + "height": 256, + "HTile": false, + "layers": [ + {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"cf694e91-149c-40b5-8c3e-977ad4b52c84","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,}, + ], + "nineSlice": null, + "origin": 0, + "parent": { + "name": "Gizmo", + "path": "folders/_Extensions/BBMOD/Gizmo.yy", + }, + "preMultiplyAlpha": false, + "sequence": { + "resourceType": "GMSequence", + "resourceVersion": "1.4", + "name": "BBMOD_SprGizmo", + "autoRecord": true, + "backdropHeight": 768, + "backdropImageOpacity": 0.5, + "backdropImagePath": "", + "backdropWidth": 1366, + "backdropXOffset": 0.0, + "backdropYOffset": 0.0, + "events": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "eventStubScript": null, + "eventToFunction": {}, + "length": 2.0, + "lockOrigin": false, + "moments": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "playback": 1, + "playbackSpeed": 30.0, + "playbackSpeedType": 0, + "showBackdrop": true, + "showBackdropImage": false, + "timeUnits": 1, + "tracks": [ + {"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[ + {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"f6406058-c156-411a-b6b4-53268f2116b4","path":"sprites/BBMOD_SprGizmo/BBMOD_SprGizmo.yy",},},},"Disabled":false,"id":"bd39bd7b-9600-40ff-ad8e-9197a5745ae5","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,}, + {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"198f2eb3-626a-4665-a2b0-881cce537b25","path":"sprites/BBMOD_SprGizmo/BBMOD_SprGizmo.yy",},},},"Disabled":false,"id":"389a0ad3-f6ad-4c07-ae36-d3b01e8c3804","IsCreationKey":false,"Key":1.0,"Length":1.0,"Stretch":false,}, + ],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,}, + ], + "visibleRange": null, + "volume": 1.0, + "xorigin": 0, + "yorigin": 0, + }, + "swatchColours": null, + "swfPrecision": 2.525, + "textureGroupId": { + "name": "Default", + "path": "texturegroups/Default", + }, + "type": 0, + "VTile": false, + "width": 256, +} \ No newline at end of file diff --git a/sprites/BBMOD_SprGizmo/f6406058-c156-411a-b6b4-53268f2116b4.png b/sprites/BBMOD_SprGizmo/f6406058-c156-411a-b6b4-53268f2116b4.png new file mode 100644 index 000000000..fe0e4cd19 Binary files /dev/null and b/sprites/BBMOD_SprGizmo/f6406058-c156-411a-b6b4-53268f2116b4.png differ diff --git a/sprites/BBMOD_SprGizmo/layers/198f2eb3-626a-4665-a2b0-881cce537b25/cf694e91-149c-40b5-8c3e-977ad4b52c84.png b/sprites/BBMOD_SprGizmo/layers/198f2eb3-626a-4665-a2b0-881cce537b25/cf694e91-149c-40b5-8c3e-977ad4b52c84.png new file mode 100644 index 000000000..a394bba06 Binary files /dev/null and b/sprites/BBMOD_SprGizmo/layers/198f2eb3-626a-4665-a2b0-881cce537b25/cf694e91-149c-40b5-8c3e-977ad4b52c84.png differ diff --git a/sprites/BBMOD_SprGizmo/layers/f6406058-c156-411a-b6b4-53268f2116b4/cf694e91-149c-40b5-8c3e-977ad4b52c84.png b/sprites/BBMOD_SprGizmo/layers/f6406058-c156-411a-b6b4-53268f2116b4/cf694e91-149c-40b5-8c3e-977ad4b52c84.png new file mode 100644 index 000000000..fe0e4cd19 Binary files /dev/null and b/sprites/BBMOD_SprGizmo/layers/f6406058-c156-411a-b6b4-53268f2116b4/cf694e91-149c-40b5-8c3e-977ad4b52c84.png differ diff --git a/sprites/BBMOD_SprParticle/3aac4271-58c7-44de-9a18-62ce002f4288.png b/sprites/BBMOD_SprParticle/3aac4271-58c7-44de-9a18-62ce002f4288.png new file mode 100644 index 000000000..56ef41ebf Binary files /dev/null and b/sprites/BBMOD_SprParticle/3aac4271-58c7-44de-9a18-62ce002f4288.png differ diff --git a/sprites/BBMOD_SprParticle/BBMOD_SprParticle.yy b/sprites/BBMOD_SprParticle/BBMOD_SprParticle.yy new file mode 100644 index 000000000..d85c9a9f9 --- /dev/null +++ b/sprites/BBMOD_SprParticle/BBMOD_SprParticle.yy @@ -0,0 +1,77 @@ +{ + "resourceType": "GMSprite", + "resourceVersion": "1.0", + "name": "BBMOD_SprParticle", + "bbox_bottom": 501, + "bbox_left": 10, + "bbox_right": 501, + "bbox_top": 10, + "bboxMode": 0, + "collisionKind": 1, + "collisionTolerance": 0, + "ConfigValues": { + "Itch": {"textureGroupId":"{\"name\":\"Default\",\"path\":\"texturegroups/Default\"}",}, + }, + "DynamicTexturePage": false, + "edgeFiltering": false, + "For3D": true, + "frames": [ + {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"3aac4271-58c7-44de-9a18-62ce002f4288",}, + ], + "gridX": 0, + "gridY": 0, + "height": 512, + "HTile": false, + "layers": [ + {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"e0fec503-965d-4d6c-8c50-8ddca8cc8815","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,}, + ], + "nineSlice": null, + "origin": 0, + "parent": { + "name": "Particles", + "path": "folders/_Extensions/BBMOD/Particles.yy", + }, + "preMultiplyAlpha": false, + "sequence": { + "resourceType": "GMSequence", + "resourceVersion": "1.4", + "name": "BBMOD_SprParticle", + "autoRecord": true, + "backdropHeight": 768, + "backdropImageOpacity": 0.5, + "backdropImagePath": "", + "backdropWidth": 1366, + "backdropXOffset": 0.0, + "backdropYOffset": 0.0, + "events": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "eventStubScript": null, + "eventToFunction": {}, + "length": 1.0, + "lockOrigin": false, + "moments": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "playback": 1, + "playbackSpeed": 30.0, + "playbackSpeedType": 0, + "showBackdrop": true, + "showBackdropImage": false, + "timeUnits": 1, + "tracks": [ + {"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[ + {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"3aac4271-58c7-44de-9a18-62ce002f4288","path":"sprites/BBMOD_SprParticle/BBMOD_SprParticle.yy",},},},"Disabled":false,"id":"11823368-0d8e-4982-b908-e5d72f132dbe","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,}, + ],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,}, + ], + "visibleRange": null, + "volume": 1.0, + "xorigin": 0, + "yorigin": 0, + }, + "swatchColours": null, + "swfPrecision": 2.525, + "textureGroupId": { + "name": "Default", + "path": "texturegroups/Default", + }, + "type": 0, + "VTile": false, + "width": 512, +} \ No newline at end of file diff --git a/sprites/BBMOD_SprParticle/layers/3aac4271-58c7-44de-9a18-62ce002f4288/e0fec503-965d-4d6c-8c50-8ddca8cc8815.png b/sprites/BBMOD_SprParticle/layers/3aac4271-58c7-44de-9a18-62ce002f4288/e0fec503-965d-4d6c-8c50-8ddca8cc8815.png new file mode 100644 index 000000000..56ef41ebf Binary files /dev/null and b/sprites/BBMOD_SprParticle/layers/3aac4271-58c7-44de-9a18-62ce002f4288/e0fec503-965d-4d6c-8c50-8ddca8cc8815.png differ diff --git a/sprites/BBMOD_SprWhite/2c48178d-8c71-49c0-98e1-a2f4e1a5189b.png b/sprites/BBMOD_SprWhite/2c48178d-8c71-49c0-98e1-a2f4e1a5189b.png new file mode 100644 index 000000000..58a2d46a9 Binary files /dev/null and b/sprites/BBMOD_SprWhite/2c48178d-8c71-49c0-98e1-a2f4e1a5189b.png differ diff --git a/sprites/BBMOD_SprWhite/BBMOD_SprWhite.yy b/sprites/BBMOD_SprWhite/BBMOD_SprWhite.yy new file mode 100644 index 000000000..d4eba80ef --- /dev/null +++ b/sprites/BBMOD_SprWhite/BBMOD_SprWhite.yy @@ -0,0 +1,77 @@ +{ + "resourceType": "GMSprite", + "resourceVersion": "1.0", + "name": "BBMOD_SprWhite", + "bbox_bottom": 0, + "bbox_left": 0, + "bbox_right": 0, + "bbox_top": 0, + "bboxMode": 0, + "collisionKind": 1, + "collisionTolerance": 0, + "ConfigValues": { + "Itch": {"textureGroupId":"{\"name\":\"Default\",\"path\":\"texturegroups/Default\"}",}, + }, + "DynamicTexturePage": false, + "edgeFiltering": false, + "For3D": true, + "frames": [ + {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"2c48178d-8c71-49c0-98e1-a2f4e1a5189b",}, + ], + "gridX": 0, + "gridY": 0, + "height": 1, + "HTile": false, + "layers": [ + {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"d29f13d3-8eff-440e-989a-926bdce1dac5","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,}, + ], + "nineSlice": null, + "origin": 0, + "parent": { + "name": "Core", + "path": "folders/_Extensions/BBMOD/Core.yy", + }, + "preMultiplyAlpha": false, + "sequence": { + "resourceType": "GMSequence", + "resourceVersion": "1.4", + "name": "BBMOD_SprWhite", + "autoRecord": true, + "backdropHeight": 768, + "backdropImageOpacity": 0.5, + "backdropImagePath": "", + "backdropWidth": 1366, + "backdropXOffset": 0.0, + "backdropYOffset": 0.0, + "events": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "eventStubScript": null, + "eventToFunction": {}, + "length": 1.0, + "lockOrigin": false, + "moments": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "playback": 1, + "playbackSpeed": 30.0, + "playbackSpeedType": 0, + "showBackdrop": true, + "showBackdropImage": false, + "timeUnits": 1, + "tracks": [ + {"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[ + {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"2c48178d-8c71-49c0-98e1-a2f4e1a5189b","path":"sprites/BBMOD_SprWhite/BBMOD_SprWhite.yy",},},},"Disabled":false,"id":"e675229c-931e-4b05-842f-6a78a76681c5","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,}, + ],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,}, + ], + "visibleRange": null, + "volume": 1.0, + "xorigin": 0, + "yorigin": 0, + }, + "swatchColours": null, + "swfPrecision": 2.525, + "textureGroupId": { + "name": "Default", + "path": "texturegroups/Default", + }, + "type": 0, + "VTile": false, + "width": 1, +} \ No newline at end of file diff --git a/sprites/BBMOD_SprWhite/layers/2c48178d-8c71-49c0-98e1-a2f4e1a5189b/d29f13d3-8eff-440e-989a-926bdce1dac5.png b/sprites/BBMOD_SprWhite/layers/2c48178d-8c71-49c0-98e1-a2f4e1a5189b/d29f13d3-8eff-440e-989a-926bdce1dac5.png new file mode 100644 index 000000000..58a2d46a9 Binary files /dev/null and b/sprites/BBMOD_SprWhite/layers/2c48178d-8c71-49c0-98e1-a2f4e1a5189b/d29f13d3-8eff-440e-989a-926bdce1dac5.png differ diff --git a/sprites/s_node_sort_array/7567fbc2-738d-48a4-8412-87cadaa3a444.png b/sprites/s_node_sort_array/7567fbc2-738d-48a4-8412-87cadaa3a444.png new file mode 100644 index 000000000..cceb55379 Binary files /dev/null and b/sprites/s_node_sort_array/7567fbc2-738d-48a4-8412-87cadaa3a444.png differ diff --git a/sprites/s_node_sort_array/layers/7567fbc2-738d-48a4-8412-87cadaa3a444/1420d747-9e8c-4c5a-884a-c4202dca29f7.png b/sprites/s_node_sort_array/layers/7567fbc2-738d-48a4-8412-87cadaa3a444/1420d747-9e8c-4c5a-884a-c4202dca29f7.png new file mode 100644 index 000000000..cceb55379 Binary files /dev/null and b/sprites/s_node_sort_array/layers/7567fbc2-738d-48a4-8412-87cadaa3a444/1420d747-9e8c-4c5a-884a-c4202dca29f7.png differ diff --git a/sprites/s_node_sort_array/s_node_sort_array.yy b/sprites/s_node_sort_array/s_node_sort_array.yy new file mode 100644 index 000000000..a3e4fdeef --- /dev/null +++ b/sprites/s_node_sort_array/s_node_sort_array.yy @@ -0,0 +1,74 @@ +{ + "resourceType": "GMSprite", + "resourceVersion": "1.0", + "name": "s_node_sort_array", + "bbox_bottom": 63, + "bbox_left": 0, + "bbox_right": 63, + "bbox_top": 0, + "bboxMode": 0, + "collisionKind": 1, + "collisionTolerance": 0, + "DynamicTexturePage": false, + "edgeFiltering": false, + "For3D": false, + "frames": [ + {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"7567fbc2-738d-48a4-8412-87cadaa3a444",}, + ], + "gridX": 0, + "gridY": 0, + "height": 64, + "HTile": false, + "layers": [ + {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"1420d747-9e8c-4c5a-884a-c4202dca29f7","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,}, + ], + "nineSlice": null, + "origin": 4, + "parent": { + "name": "node", + "path": "folders/nodes/icons/node.yy", + }, + "preMultiplyAlpha": false, + "sequence": { + "resourceType": "GMSequence", + "resourceVersion": "1.4", + "name": "s_node_sort_array", + "autoRecord": true, + "backdropHeight": 768, + "backdropImageOpacity": 0.5, + "backdropImagePath": "", + "backdropWidth": 1366, + "backdropXOffset": 0.0, + "backdropYOffset": 0.0, + "events": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "eventStubScript": null, + "eventToFunction": {}, + "length": 1.0, + "lockOrigin": false, + "moments": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],}, + "playback": 1, + "playbackSpeed": 30.0, + "playbackSpeedType": 0, + "showBackdrop": true, + "showBackdropImage": false, + "timeUnits": 1, + "tracks": [ + {"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[ + {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"7567fbc2-738d-48a4-8412-87cadaa3a444","path":"sprites/s_node_sort_array/s_node_sort_array.yy",},},},"Disabled":false,"id":"29ec1333-8ff0-4cee-a4bf-e3e766cf6b64","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,}, + ],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,}, + ], + "visibleRange": null, + "volume": 1.0, + "xorigin": 32, + "yorigin": 32, + }, + "swatchColours": null, + "swfPrecision": 2.525, + "textureGroupId": { + "name": "Default", + "path": "texturegroups/Default", + }, + "type": 0, + "VTile": false, + "width": 64, +} \ No newline at end of file