diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..417da75 --- /dev/null +++ b/.clang-format @@ -0,0 +1,744 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# clang-format configuration file. Intended for clang-format >= 11. +# +# For more information, see: +# +# Documentation/process/clang-format.rst +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +--- +AccessModifierOffset: -8 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false + +# Taken from: +# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ tools/ \ +# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ +# | LC_ALL=C sort -u +ForEachMacros: + - '__ata_qc_for_each' + - '__bio_for_each_bvec' + - '__bio_for_each_segment' + - '__evlist__for_each_entry' + - '__evlist__for_each_entry_continue' + - '__evlist__for_each_entry_from' + - '__evlist__for_each_entry_reverse' + - '__evlist__for_each_entry_safe' + - '__for_each_mem_range' + - '__for_each_mem_range_rev' + - '__for_each_thread' + - '__hlist_for_each_rcu' + - '__map__for_each_symbol_by_name' + - '__pci_bus_for_each_res0' + - '__pci_bus_for_each_res1' + - '__pci_dev_for_each_res0' + - '__pci_dev_for_each_res1' + - '__perf_evlist__for_each_entry' + - '__perf_evlist__for_each_entry_reverse' + - '__perf_evlist__for_each_entry_safe' + - '__rq_for_each_bio' + - '__shost_for_each_device' + - '__sym_for_each' + - 'apei_estatus_for_each_section' + - 'ata_for_each_dev' + - 'ata_for_each_link' + - 'ata_qc_for_each' + - 'ata_qc_for_each_raw' + - 'ata_qc_for_each_with_internal' + - 'ax25_for_each' + - 'ax25_uid_for_each' + - 'bio_for_each_bvec' + - 'bio_for_each_bvec_all' + - 'bio_for_each_folio_all' + - 'bio_for_each_integrity_vec' + - 'bio_for_each_segment' + - 'bio_for_each_segment_all' + - 'bio_list_for_each' + - 'bip_for_each_vec' + - 'bond_for_each_slave' + - 'bond_for_each_slave_rcu' + - 'bpf_for_each' + - 'bpf_for_each_reg_in_vstate' + - 'bpf_for_each_reg_in_vstate_mask' + - 'bpf_for_each_spilled_reg' + - 'bpf_object__for_each_map' + - 'bpf_object__for_each_program' + - 'btree_for_each_safe128' + - 'btree_for_each_safe32' + - 'btree_for_each_safe64' + - 'btree_for_each_safel' + - 'card_for_each_dev' + - 'cgroup_taskset_for_each' + - 'cgroup_taskset_for_each_leader' + - 'cpu_aggr_map__for_each_idx' + - 'cpufreq_for_each_efficient_entry_idx' + - 'cpufreq_for_each_entry' + - 'cpufreq_for_each_entry_idx' + - 'cpufreq_for_each_valid_entry' + - 'cpufreq_for_each_valid_entry_idx' + - 'css_for_each_child' + - 'css_for_each_descendant_post' + - 'css_for_each_descendant_pre' + - 'damon_for_each_region' + - 'damon_for_each_region_from' + - 'damon_for_each_region_safe' + - 'damon_for_each_scheme' + - 'damon_for_each_scheme_safe' + - 'damon_for_each_target' + - 'damon_for_each_target_safe' + - 'damos_for_each_filter' + - 'damos_for_each_filter_safe' + - 'data__for_each_file' + - 'data__for_each_file_new' + - 'data__for_each_file_start' + - 'device_for_each_child_node' + - 'displayid_iter_for_each' + - 'dma_fence_array_for_each' + - 'dma_fence_chain_for_each' + - 'dma_fence_unwrap_for_each' + - 'dma_resv_for_each_fence' + - 'dma_resv_for_each_fence_unlocked' + - 'do_for_each_ftrace_op' + - 'drm_atomic_crtc_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane_state' + - 'drm_atomic_for_each_plane_damage' + - 'drm_client_for_each_connector_iter' + - 'drm_client_for_each_modeset' + - 'drm_connector_for_each_possible_encoder' + - 'drm_exec_for_each_locked_object' + - 'drm_exec_for_each_locked_object_reverse' + - 'drm_for_each_bridge_in_chain' + - 'drm_for_each_connector_iter' + - 'drm_for_each_crtc' + - 'drm_for_each_crtc_reverse' + - 'drm_for_each_encoder' + - 'drm_for_each_encoder_mask' + - 'drm_for_each_fb' + - 'drm_for_each_legacy_plane' + - 'drm_for_each_plane' + - 'drm_for_each_plane_mask' + - 'drm_for_each_privobj' + - 'drm_gem_for_each_gpuva' + - 'drm_gem_for_each_gpuva_safe' + - 'drm_gpuva_for_each_op' + - 'drm_gpuva_for_each_op_from_reverse' + - 'drm_gpuva_for_each_op_safe' + - 'drm_gpuvm_for_each_va' + - 'drm_gpuvm_for_each_va_range' + - 'drm_gpuvm_for_each_va_range_safe' + - 'drm_gpuvm_for_each_va_safe' + - 'drm_mm_for_each_hole' + - 'drm_mm_for_each_node' + - 'drm_mm_for_each_node_in_range' + - 'drm_mm_for_each_node_safe' + - 'dsa_switch_for_each_available_port' + - 'dsa_switch_for_each_cpu_port' + - 'dsa_switch_for_each_cpu_port_continue_reverse' + - 'dsa_switch_for_each_port' + - 'dsa_switch_for_each_port_continue_reverse' + - 'dsa_switch_for_each_port_safe' + - 'dsa_switch_for_each_user_port' + - 'dsa_tree_for_each_cpu_port' + - 'dsa_tree_for_each_user_port' + - 'dsa_tree_for_each_user_port_continue_reverse' + - 'dso__for_each_symbol' + - 'dsos__for_each_with_build_id' + - 'elf_hash_for_each_possible' + - 'elf_symtab__for_each_symbol' + - 'evlist__for_each_cpu' + - 'evlist__for_each_entry' + - 'evlist__for_each_entry_continue' + - 'evlist__for_each_entry_from' + - 'evlist__for_each_entry_reverse' + - 'evlist__for_each_entry_safe' + - 'flow_action_for_each' + - 'for_each_acpi_consumer_dev' + - 'for_each_acpi_dev_match' + - 'for_each_active_dev_scope' + - 'for_each_active_drhd_unit' + - 'for_each_active_iommu' + - 'for_each_active_route' + - 'for_each_aggr_pgid' + - 'for_each_and_bit' + - 'for_each_andnot_bit' + - 'for_each_available_child_of_node' + - 'for_each_bench' + - 'for_each_bio' + - 'for_each_board_func_rsrc' + - 'for_each_btf_ext_rec' + - 'for_each_btf_ext_sec' + - 'for_each_bvec' + - 'for_each_card_auxs' + - 'for_each_card_auxs_safe' + - 'for_each_card_components' + - 'for_each_card_dapms' + - 'for_each_card_pre_auxs' + - 'for_each_card_prelinks' + - 'for_each_card_rtds' + - 'for_each_card_rtds_safe' + - 'for_each_card_widgets' + - 'for_each_card_widgets_safe' + - 'for_each_cgroup_storage_type' + - 'for_each_child_of_node' + - 'for_each_clear_bit' + - 'for_each_clear_bit_from' + - 'for_each_clear_bitrange' + - 'for_each_clear_bitrange_from' + - 'for_each_cmd' + - 'for_each_cmsghdr' + - 'for_each_collection' + - 'for_each_comp_order' + - 'for_each_compatible_node' + - 'for_each_component_dais' + - 'for_each_component_dais_safe' + - 'for_each_conduit' + - 'for_each_console' + - 'for_each_console_srcu' + - 'for_each_cpu' + - 'for_each_cpu_and' + - 'for_each_cpu_andnot' + - 'for_each_cpu_or' + - 'for_each_cpu_wrap' + - 'for_each_dapm_widgets' + - 'for_each_dedup_cand' + - 'for_each_dev_addr' + - 'for_each_dev_scope' + - 'for_each_dma_cap_mask' + - 'for_each_dpcm_be' + - 'for_each_dpcm_be_rollback' + - 'for_each_dpcm_be_safe' + - 'for_each_dpcm_fe' + - 'for_each_drhd_unit' + - 'for_each_dss_dev' + - 'for_each_efi_memory_desc' + - 'for_each_efi_memory_desc_in_map' + - 'for_each_element' + - 'for_each_element_extid' + - 'for_each_element_id' + - 'for_each_endpoint_of_node' + - 'for_each_event' + - 'for_each_event_tps' + - 'for_each_evictable_lru' + - 'for_each_fib6_node_rt_rcu' + - 'for_each_fib6_walker_rt' + - 'for_each_free_mem_pfn_range_in_zone' + - 'for_each_free_mem_pfn_range_in_zone_from' + - 'for_each_free_mem_range' + - 'for_each_free_mem_range_reverse' + - 'for_each_func_rsrc' + - 'for_each_gpiochip_node' + - 'for_each_group_evsel' + - 'for_each_group_evsel_head' + - 'for_each_group_member' + - 'for_each_group_member_head' + - 'for_each_hstate' + - 'for_each_if' + - 'for_each_inject_fn' + - 'for_each_insn' + - 'for_each_insn_prefix' + - 'for_each_intid' + - 'for_each_iommu' + - 'for_each_ip_tunnel_rcu' + - 'for_each_irq_nr' + - 'for_each_lang' + - 'for_each_link_codecs' + - 'for_each_link_cpus' + - 'for_each_link_platforms' + - 'for_each_lru' + - 'for_each_matching_node' + - 'for_each_matching_node_and_match' + - 'for_each_media_entity_data_link' + - 'for_each_mem_pfn_range' + - 'for_each_mem_range' + - 'for_each_mem_range_rev' + - 'for_each_mem_region' + - 'for_each_member' + - 'for_each_memory' + - 'for_each_migratetype_order' + - 'for_each_missing_reg' + - 'for_each_mle_subelement' + - 'for_each_mod_mem_type' + - 'for_each_net' + - 'for_each_net_continue_reverse' + - 'for_each_net_rcu' + - 'for_each_netdev' + - 'for_each_netdev_continue' + - 'for_each_netdev_continue_rcu' + - 'for_each_netdev_continue_reverse' + - 'for_each_netdev_dump' + - 'for_each_netdev_feature' + - 'for_each_netdev_in_bond_rcu' + - 'for_each_netdev_rcu' + - 'for_each_netdev_reverse' + - 'for_each_netdev_safe' + - 'for_each_new_connector_in_state' + - 'for_each_new_crtc_in_state' + - 'for_each_new_mst_mgr_in_state' + - 'for_each_new_plane_in_state' + - 'for_each_new_plane_in_state_reverse' + - 'for_each_new_private_obj_in_state' + - 'for_each_new_reg' + - 'for_each_node' + - 'for_each_node_by_name' + - 'for_each_node_by_type' + - 'for_each_node_mask' + - 'for_each_node_state' + - 'for_each_node_with_cpus' + - 'for_each_node_with_property' + - 'for_each_nonreserved_multicast_dest_pgid' + - 'for_each_numa_hop_mask' + - 'for_each_of_allnodes' + - 'for_each_of_allnodes_from' + - 'for_each_of_cpu_node' + - 'for_each_of_pci_range' + - 'for_each_old_connector_in_state' + - 'for_each_old_crtc_in_state' + - 'for_each_old_mst_mgr_in_state' + - 'for_each_old_plane_in_state' + - 'for_each_old_private_obj_in_state' + - 'for_each_oldnew_connector_in_state' + - 'for_each_oldnew_crtc_in_state' + - 'for_each_oldnew_mst_mgr_in_state' + - 'for_each_oldnew_plane_in_state' + - 'for_each_oldnew_plane_in_state_reverse' + - 'for_each_oldnew_private_obj_in_state' + - 'for_each_online_cpu' + - 'for_each_online_node' + - 'for_each_online_pgdat' + - 'for_each_or_bit' + - 'for_each_path' + - 'for_each_pci_bridge' + - 'for_each_pci_dev' + - 'for_each_pcm_streams' + - 'for_each_physmem_range' + - 'for_each_populated_zone' + - 'for_each_possible_cpu' + - 'for_each_present_blessed_reg' + - 'for_each_present_cpu' + - 'for_each_prime_number' + - 'for_each_prime_number_from' + - 'for_each_probe_cache_entry' + - 'for_each_process' + - 'for_each_process_thread' + - 'for_each_prop_codec_conf' + - 'for_each_prop_dai_codec' + - 'for_each_prop_dai_cpu' + - 'for_each_prop_dlc_codecs' + - 'for_each_prop_dlc_cpus' + - 'for_each_prop_dlc_platforms' + - 'for_each_property_of_node' + - 'for_each_reg' + - 'for_each_reg_filtered' + - 'for_each_reloc' + - 'for_each_reloc_from' + - 'for_each_requested_gpio' + - 'for_each_requested_gpio_in_range' + - 'for_each_reserved_mem_range' + - 'for_each_reserved_mem_region' + - 'for_each_rtd_codec_dais' + - 'for_each_rtd_components' + - 'for_each_rtd_cpu_dais' + - 'for_each_rtd_dais' + - 'for_each_sband_iftype_data' + - 'for_each_script' + - 'for_each_sec' + - 'for_each_set_bit' + - 'for_each_set_bit_from' + - 'for_each_set_bit_wrap' + - 'for_each_set_bitrange' + - 'for_each_set_bitrange_from' + - 'for_each_set_clump8' + - 'for_each_sg' + - 'for_each_sg_dma_page' + - 'for_each_sg_page' + - 'for_each_sgtable_dma_page' + - 'for_each_sgtable_dma_sg' + - 'for_each_sgtable_page' + - 'for_each_sgtable_sg' + - 'for_each_sibling_event' + - 'for_each_sta_active_link' + - 'for_each_subelement' + - 'for_each_subelement_extid' + - 'for_each_subelement_id' + - 'for_each_sublist' + - 'for_each_subsystem' + - 'for_each_supported_activate_fn' + - 'for_each_supported_inject_fn' + - 'for_each_sym' + - 'for_each_test' + - 'for_each_thread' + - 'for_each_token' + - 'for_each_unicast_dest_pgid' + - 'for_each_valid_link' + - 'for_each_vif_active_link' + - 'for_each_vma' + - 'for_each_vma_range' + - 'for_each_vsi' + - 'for_each_wakeup_source' + - 'for_each_zone' + - 'for_each_zone_zonelist' + - 'for_each_zone_zonelist_nodemask' + - 'func_for_each_insn' + - 'fwnode_for_each_available_child_node' + - 'fwnode_for_each_child_node' + - 'fwnode_for_each_parent_node' + - 'fwnode_graph_for_each_endpoint' + - 'gadget_for_each_ep' + - 'genradix_for_each' + - 'genradix_for_each_from' + - 'genradix_for_each_reverse' + - 'hash_for_each' + - 'hash_for_each_possible' + - 'hash_for_each_possible_rcu' + - 'hash_for_each_possible_rcu_notrace' + - 'hash_for_each_possible_safe' + - 'hash_for_each_rcu' + - 'hash_for_each_safe' + - 'hashmap__for_each_entry' + - 'hashmap__for_each_entry_safe' + - 'hashmap__for_each_key_entry' + - 'hashmap__for_each_key_entry_safe' + - 'hctx_for_each_ctx' + - 'hists__for_each_format' + - 'hists__for_each_sort_list' + - 'hlist_bl_for_each_entry' + - 'hlist_bl_for_each_entry_rcu' + - 'hlist_bl_for_each_entry_safe' + - 'hlist_for_each' + - 'hlist_for_each_entry' + - 'hlist_for_each_entry_continue' + - 'hlist_for_each_entry_continue_rcu' + - 'hlist_for_each_entry_continue_rcu_bh' + - 'hlist_for_each_entry_from' + - 'hlist_for_each_entry_from_rcu' + - 'hlist_for_each_entry_rcu' + - 'hlist_for_each_entry_rcu_bh' + - 'hlist_for_each_entry_rcu_notrace' + - 'hlist_for_each_entry_safe' + - 'hlist_for_each_entry_srcu' + - 'hlist_for_each_safe' + - 'hlist_nulls_for_each_entry' + - 'hlist_nulls_for_each_entry_from' + - 'hlist_nulls_for_each_entry_rcu' + - 'hlist_nulls_for_each_entry_safe' + - 'i3c_bus_for_each_i2cdev' + - 'i3c_bus_for_each_i3cdev' + - 'idr_for_each_entry' + - 'idr_for_each_entry_continue' + - 'idr_for_each_entry_continue_ul' + - 'idr_for_each_entry_ul' + - 'in_dev_for_each_ifa_rcu' + - 'in_dev_for_each_ifa_rtnl' + - 'inet_bind_bucket_for_each' + - 'interval_tree_for_each_span' + - 'intlist__for_each_entry' + - 'intlist__for_each_entry_safe' + - 'kcore_copy__for_each_phdr' + - 'key_for_each' + - 'key_for_each_safe' + - 'klp_for_each_func' + - 'klp_for_each_func_safe' + - 'klp_for_each_func_static' + - 'klp_for_each_object' + - 'klp_for_each_object_safe' + - 'klp_for_each_object_static' + - 'kunit_suite_for_each_test_case' + - 'kvm_for_each_memslot' + - 'kvm_for_each_memslot_in_gfn_range' + - 'kvm_for_each_vcpu' + - 'libbpf_nla_for_each_attr' + - 'list_for_each' + - 'list_for_each_codec' + - 'list_for_each_codec_safe' + - 'list_for_each_continue' + - 'list_for_each_entry' + - 'list_for_each_entry_continue' + - 'list_for_each_entry_continue_rcu' + - 'list_for_each_entry_continue_reverse' + - 'list_for_each_entry_from' + - 'list_for_each_entry_from_rcu' + - 'list_for_each_entry_from_reverse' + - 'list_for_each_entry_lockless' + - 'list_for_each_entry_rcu' + - 'list_for_each_entry_reverse' + - 'list_for_each_entry_safe' + - 'list_for_each_entry_safe_continue' + - 'list_for_each_entry_safe_from' + - 'list_for_each_entry_safe_reverse' + - 'list_for_each_entry_srcu' + - 'list_for_each_from' + - 'list_for_each_prev' + - 'list_for_each_prev_safe' + - 'list_for_each_rcu' + - 'list_for_each_reverse' + - 'list_for_each_safe' + - 'llist_for_each' + - 'llist_for_each_entry' + - 'llist_for_each_entry_safe' + - 'llist_for_each_safe' + - 'lwq_for_each_safe' + - 'map__for_each_symbol' + - 'map__for_each_symbol_by_name' + - 'maps__for_each_entry' + - 'maps__for_each_entry_safe' + - 'mas_for_each' + - 'mci_for_each_dimm' + - 'media_device_for_each_entity' + - 'media_device_for_each_intf' + - 'media_device_for_each_link' + - 'media_device_for_each_pad' + - 'media_entity_for_each_pad' + - 'media_pipeline_for_each_entity' + - 'media_pipeline_for_each_pad' + - 'mlx5_lag_for_each_peer_mdev' + - 'msi_domain_for_each_desc' + - 'msi_for_each_desc' + - 'mt_for_each' + - 'nanddev_io_for_each_page' + - 'netdev_for_each_lower_dev' + - 'netdev_for_each_lower_private' + - 'netdev_for_each_lower_private_rcu' + - 'netdev_for_each_mc_addr' + - 'netdev_for_each_synced_mc_addr' + - 'netdev_for_each_synced_uc_addr' + - 'netdev_for_each_uc_addr' + - 'netdev_for_each_upper_dev_rcu' + - 'netdev_hw_addr_list_for_each' + - 'nft_rule_for_each_expr' + - 'nla_for_each_attr' + - 'nla_for_each_nested' + - 'nlmsg_for_each_attr' + - 'nlmsg_for_each_msg' + - 'nr_neigh_for_each' + - 'nr_neigh_for_each_safe' + - 'nr_node_for_each' + - 'nr_node_for_each_safe' + - 'of_for_each_phandle' + - 'of_property_for_each_string' + - 'of_property_for_each_u32' + - 'pci_bus_for_each_resource' + - 'pci_dev_for_each_resource' + - 'pcl_for_each_chunk' + - 'pcl_for_each_segment' + - 'pcm_for_each_format' + - 'perf_config_items__for_each_entry' + - 'perf_config_sections__for_each_entry' + - 'perf_config_set__for_each_entry' + - 'perf_cpu_map__for_each_cpu' + - 'perf_cpu_map__for_each_idx' + - 'perf_evlist__for_each_entry' + - 'perf_evlist__for_each_entry_reverse' + - 'perf_evlist__for_each_entry_safe' + - 'perf_evlist__for_each_evsel' + - 'perf_evlist__for_each_mmap' + - 'perf_hpp_list__for_each_format' + - 'perf_hpp_list__for_each_format_safe' + - 'perf_hpp_list__for_each_sort_list' + - 'perf_hpp_list__for_each_sort_list_safe' + - 'perf_tool_event__for_each_event' + - 'plist_for_each' + - 'plist_for_each_continue' + - 'plist_for_each_entry' + - 'plist_for_each_entry_continue' + - 'plist_for_each_entry_safe' + - 'plist_for_each_safe' + - 'pnp_for_each_card' + - 'pnp_for_each_dev' + - 'protocol_for_each_card' + - 'protocol_for_each_dev' + - 'queue_for_each_hw_ctx' + - 'radix_tree_for_each_slot' + - 'radix_tree_for_each_tagged' + - 'rb_for_each' + - 'rbtree_postorder_for_each_entry_safe' + - 'rdma_for_each_block' + - 'rdma_for_each_port' + - 'rdma_umem_for_each_dma_block' + - 'resort_rb__for_each_entry' + - 'resource_list_for_each_entry' + - 'resource_list_for_each_entry_safe' + - 'rhl_for_each_entry_rcu' + - 'rhl_for_each_rcu' + - 'rht_for_each' + - 'rht_for_each_entry' + - 'rht_for_each_entry_from' + - 'rht_for_each_entry_rcu' + - 'rht_for_each_entry_rcu_from' + - 'rht_for_each_entry_safe' + - 'rht_for_each_from' + - 'rht_for_each_rcu' + - 'rht_for_each_rcu_from' + - 'rq_for_each_bvec' + - 'rq_for_each_segment' + - 'rq_list_for_each' + - 'rq_list_for_each_safe' + - 'sample_read_group__for_each' + - 'scsi_for_each_prot_sg' + - 'scsi_for_each_sg' + - 'sctp_for_each_hentry' + - 'sctp_skb_for_each' + - 'sec_for_each_insn' + - 'sec_for_each_insn_continue' + - 'sec_for_each_insn_from' + - 'sec_for_each_sym' + - 'shdma_for_each_chan' + - 'shost_for_each_device' + - 'sk_for_each' + - 'sk_for_each_bound' + - 'sk_for_each_bound_bhash2' + - 'sk_for_each_entry_offset_rcu' + - 'sk_for_each_from' + - 'sk_for_each_rcu' + - 'sk_for_each_safe' + - 'sk_nulls_for_each' + - 'sk_nulls_for_each_from' + - 'sk_nulls_for_each_rcu' + - 'snd_array_for_each' + - 'snd_pcm_group_for_each_entry' + - 'snd_soc_dapm_widget_for_each_path' + - 'snd_soc_dapm_widget_for_each_path_safe' + - 'snd_soc_dapm_widget_for_each_sink_path' + - 'snd_soc_dapm_widget_for_each_source_path' + - 'strlist__for_each_entry' + - 'strlist__for_each_entry_safe' + - 'sym_for_each_insn' + - 'sym_for_each_insn_continue_reverse' + - 'symbols__for_each_entry' + - 'tb_property_for_each' + - 'tcf_act_for_each_action' + - 'tcf_exts_for_each_action' + - 'ttm_resource_manager_for_each_res' + - 'twsk_for_each_bound_bhash2' + - 'udp_portaddr_for_each_entry' + - 'udp_portaddr_for_each_entry_rcu' + - 'usb_hub_for_each_child' + - 'v4l2_device_for_each_subdev' + - 'v4l2_m2m_for_each_dst_buf' + - 'v4l2_m2m_for_each_dst_buf_safe' + - 'v4l2_m2m_for_each_src_buf' + - 'v4l2_m2m_for_each_src_buf_safe' + - 'virtio_device_for_each_vq' + - 'while_for_each_ftrace_op' + - 'xa_for_each' + - 'xa_for_each_marked' + - 'xa_for_each_range' + - 'xa_for_each_start' + - 'xas_for_each' + - 'xas_for_each_conflict' + - 'xas_for_each_marked' + - 'xbc_array_for_each_value' + - 'xbc_for_each_key_value' + - 'xbc_node_for_each_array_value' + - 'xbc_node_for_each_child' + - 'xbc_node_for_each_key_value' + - 'xbc_node_for_each_subkey' + - 'zorro_for_each_dev' + +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 8 +IndentAccessModifiers: false +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 8 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatementsExceptForEachMacros +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..68b0f60 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,76 @@ +{ + "cmake.generator": "Unix Makefiles", + "cmake.configureSettings": { + "CMAKE_PREFIX_PATH": "/media/slapin/library/ogre3/ogre-sdk", + "CMAKE_VERBOSE_MAKEFILE": "ON" + }, + "cmake.buildDirectory": "${workspaceFolder}/build-vscode", + "files.associations": { + "istream": "cpp", + "variant": "cpp", + "tuple": "cpp", + "iostream": "cpp", + "string_view": "cpp", + "atomic": "cpp", + "memory_resource": "cpp", + "stop_token": "cpp", + "random": "cpp", + "future": "cpp", + "array": "cpp", + "deque": "cpp", + "list": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "initializer_list": "cpp", + "span": "cpp", + "chrono": "cpp", + "format": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "bit": "cpp", + "bitset": "cpp", + "charconv": "cpp", + "compare": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "cstdint": "cpp", + "map": "cpp", + "set": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "optional": "cpp", + "ratio": "cpp", + "system_error": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "numeric": "cpp", + "ostream": "cpp", + "semaphore": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "typeinfo": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..8657548 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,67 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "cmake", + "label": "CMake: configure", + "command": "configure", + "problemMatcher": [], + "detail": "CMake config" + }, + { + "type": "cmake", + "label": "CMake: clean rebuild", + "command": "build", + "targets": [ + "all" + ], + "group": "build", + "problemMatcher": [], + "detail": "CMake clean rebuild task" + }, + { + "type": "cmake", + "label": "CMake: clean rebuild", + "command": "cleanRebuild", + "targets": [ + "all" + ], + "group": "build", + "problemMatcher": [], + "detail": "CMake clean rebuild task" + }, + { + "type": "cmake", + "label": "CMake: complete build", + "command": "build", + "targets": [ + "all" + ], + "group": "build", + "problemMatcher": [], + "detail": "CMake build" + }, + { + "type": "cmake", + "label": "CMake: complete rebuild", + "command": "cleanRebuild", + "targets": [ + "all" + ], + "group": "build", + "problemMatcher": [], + "detail": "CMake clean rebuild task" + }, + { + "type": "cmake", + "label": "CMake: build", + "command": "build", + "targets": [ + "all" + ], + "group": "build", + "problemMatcher": [], + "detail": "CMake build task" + } + ] +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ee98a6..e158110 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,10 @@ cmake_minimum_required(VERSION 3.13.0) project(jolt-physics) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) # The COMPONENTS part checks that OGRE was built the way we need it # The CONFIG flag makes sure we get OGRE instead of OGRE-next +find_package(Jolt REQUIRED) find_package(OGRE REQUIRED COMPONENTS Bites Paging Terrain CONFIG) find_package(ZLIB) find_package(SDL2) @@ -37,8 +38,39 @@ set_target_properties(fix::pugixml PROPERTIES INTERFACE_LINK_LIBRARIES "pugixml" INTERFACE_LINK_DIRECTORIES "${CMAKE_PREFIX_PATH}/lib" ) +set(STAGE_DIRECTORIES + resources/main + resources/shaderlib + resources/terrain + skybox) +set(STAGE_FILES_OUTPUT) +foreach(STAGE_DIR ${STAGE_DIRECTORIES}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${STAGE_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/${STAGE_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/${STAGE_DIR} ${CMAKE_BINARY_DIR}/${STAGE_DIR} + ) + list(APPEND STAGE_FILES_OUTPUT ${CMAKE_BINARY_DIR}/${STAGE_DIR}) +endforeach() + +add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/resources.cfg + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg + DEPENDS ${CMAKE_SOURCE_DIR}/resources.cfg +) +add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/resources/world/world.glb + COMMAND ${CMAKE_COMMAND} + -E copy + ${CMAKE_SOURCE_DIR}/assets/blender/jolt-test/world.glb + ${CMAKE_BINARY_DIR}/resources/world/world.glb + DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/jolt-test/world.glb +) +list(APPEND STAGE_FILES_OUTPUT ${CMAKE_BINARY_DIR}/resources/world/world.glb) + +add_custom_target(stage_files ALL DEPENDS ${STAGE_FILES_OUTPUT} ${CMAKE_BINARY_DIR}/resources.cfg) # add the source files as usual +add_library(physics STATIC physics.cpp) +target_link_libraries(physics PUBLIC OgreMain Jolt::Jolt) add_executable(jolt-demo main.cpp) +target_link_libraries(jolt-demo physics) # this also sets the includes and pulls third party dependencies target_link_libraries(jolt-demo OgreBites OgrePaging ${ASSIMP_LIBRARIES} @@ -47,5 +79,7 @@ target_link_libraries(jolt-demo OgreBites OgrePaging ${ASSIMP_LIBRARIES} if(OGRE_STATIC) target_link_options(jolt-demo PRIVATE -static-libstdc++ -static-libgcc) endif() -#add_dependencies(0_Bootstrap stage_files import_vrm) +add_dependencies(jolt-demo stage_files) + +install(TARGETS jolt-demo DESTINATION bin) diff --git a/assets/blender/jolt-test/world.blend b/assets/blender/jolt-test/world.blend new file mode 100644 index 0000000..39750a0 Binary files /dev/null and b/assets/blender/jolt-test/world.blend differ diff --git a/assets/blender/jolt-test/world.blend1 b/assets/blender/jolt-test/world.blend1 new file mode 100644 index 0000000..5031a57 Binary files /dev/null and b/assets/blender/jolt-test/world.blend1 differ diff --git a/assets/blender/jolt-test/world.glb b/assets/blender/jolt-test/world.glb new file mode 100644 index 0000000..b103240 Binary files /dev/null and b/assets/blender/jolt-test/world.glb differ diff --git a/main.cpp b/main.cpp index ec13b4f..71a1b12 100644 --- a/main.cpp +++ b/main.cpp @@ -3,14 +3,28 @@ #include #include #include +#include "physics.h" #include "main.h" +class KeyHandler : public OgreBites::InputListener { + bool keyPressed(const OgreBites::KeyboardEvent &evt) override + { + if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) { + Ogre::Root::getSingleton().queueEndRendering(); + } + return true; + } + void frameRendered(const Ogre::FrameEvent &evt) override + { + JoltPhysicsWrapper::getSingleton().update( + evt.timeSinceLastFrame); + } +}; + class App : public OgreBites::ApplicationContext { Ogre::SceneNode *mCameraNode; Ogre::SceneManager *mScnMgr; - std::unique_ptr mCharacter; KeyHandler mKeyHandler; - MainWorld mWorld; public: App(); @@ -29,7 +43,7 @@ public: }; App::App() - : OgreBites::ApplicationContext("App") + : OgreBites::ApplicationContext("JoltPhysics") { } void App::setup() @@ -65,18 +79,11 @@ void App::initCamera() void App::setupWorld() { addInputListener(&mKeyHandler); - mWorld.setup(); - getRoot()->addFrameListener(&mWorld); } void App::createCharacter() { Ogre::Camera *cam = static_cast( mCameraNode->getAttachedObject("tps_camera")); - mCharacter = std::make_unique(mCameraNode, cam, - mScnMgr, &mWorld); - // mInputListenerChain = TouchAgnosticInputListenerChain(getRenderWindow(), {&mKeyHandler, mCharacter.get()}); - addInputListener(mCharacter.get()); - WorldData::get_singleton()->initPagedWorld(cam); } void App::createContent() { @@ -92,6 +99,41 @@ void App::createContent() light->setDiffuseColour(Ogre::ColourValue::White); light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4)); mScnMgr->setSkyBox(true, "Skybox", 490); + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton().load("world.glb", "General"); + new JoltPhysicsWrapper(mScnMgr, mScnMgr->getRootSceneNode()); + JPH::ShapeRefC shape = + JoltPhysicsWrapper::getSingleton().createMeshShape(mesh); + JPH::ObjectLayer layer = Layers::NON_MOVING; + JPH::ObjectLayer layerMoving = Layers::MOVING; + JPH::ShapeRefC shapeBox1 = + JoltPhysicsWrapper::getSingleton().createBoxShape( + { 0.5f, 1.7f, 0.5f }); + JPH::ShapeRefC shapeBox2 = + JoltPhysicsWrapper::getSingleton().createBoxShape( + { 0.5f, 1.7f, 0.5f }); + JPH::ShapeRefC shapeBox3 = + JoltPhysicsWrapper::getSingleton().createBoxShape( + { 0.5f, 1.7f, 0.5f }); + JoltPhysics::CompoundShapeBuilder builder; + builder.addShape(shapeBox1, { -0.1f, 0.0f, 0.0f }, + Ogre::Quaternion::IDENTITY); + builder.addShape(shapeBox2, { 0.0f, 0.0f, 0.0f }, + Ogre::Quaternion::IDENTITY); + builder.addShape(shapeBox3, { 0.1f, 0.0f, 0.0f }, + Ogre::Quaternion::IDENTITY); + JPH::ShapeRefC compound = builder.build(); + auto body = JoltPhysicsWrapper::getSingleton().createBody( + shape.GetPtr(), 0, Ogre::Vector3::ZERO, Ogre::Quaternion::IDENTITY, + JPH::EMotionType::Static, layer); + auto compoundBody = JoltPhysicsWrapper::getSingleton().createBody( + compound.GetPtr(), 0, Ogre::Vector3(0, 0, -3), + Ogre::Quaternion::IDENTITY, JPH::EMotionType::Kinematic, + layerMoving); + JoltPhysicsWrapper::getSingleton().addBody( + body, JPH::EActivation::DontActivate); + JoltPhysicsWrapper::getSingleton().addBody( + compoundBody, JPH::EActivation::DontActivate); } int main(int argc, char *argv[]) { @@ -105,7 +147,6 @@ int main(int argc, char *argv[]) Ogre::RTShader::ShaderGenerator *shadergen = Ogre::RTShader::ShaderGenerator::getSingletonPtr(); shadergen->addSceneManager(scnMgr); - WorldData::init(root, scnMgr); ctx.setWindowGrab(true); ctx.createContent(); ctx.initCamera(); @@ -118,7 +159,5 @@ int main(int argc, char *argv[]) ctx.getRoot()->startRendering(); ctx.setWindowGrab(false); ctx.closeApp(); - WorldData::cleanup(); return 0; } - diff --git a/physics.cpp b/physics.cpp new file mode 100644 index 0000000..cc71854 --- /dev/null +++ b/physics.cpp @@ -0,0 +1,1804 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2025 Jorrit Rouwe +// SPDX-License-Identifier: CC0-1.0 +// This file is in the public domain. It serves as an example to start building your own application using Jolt Physics. Feel free to copy paste without attribution! + +// The Jolt headers don't include Jolt.h. Always include Jolt.h before including any other Jolt header. +// You can use Jolt.h in your precompiled header to speed up compilation. +#include +#include +#include + +// Jolt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// STL includes +#include +#include +#include +#include "physics.h" + +// Disable common warnings triggered by Jolt, you can use JPH_SUPPRESS_WARNING_PUSH / JPH_SUPPRESS_WARNING_POP to store and restore the warning state +JPH_SUPPRESS_WARNINGS + +// All Jolt symbols are in the JPH namespace + +// Callback for traces, connect this to your own trace function if you have one +static void TraceImpl(const char *inFMT, ...) +{ + // Format the message + va_list list; + va_start(list, inFMT); + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), inFMT, list); + va_end(list); + + // Print to the TTY + std::cout << buffer << std::endl; +} + +#ifdef JPH_ENABLE_ASSERTS + +// Callback for asserts, connect this to your own assert handler if you have one +static bool AssertFailedImpl(const char *inExpression, const char *inMessage, + const char *inFile, uint inLine) +{ + // Print to the TTY + std::cout << inFile << ":" << inLine << ": (" << inExpression << ") " + << (inMessage != nullptr ? inMessage : "") << std::endl; + + // Breakpoint + return true; +}; + +#endif // JPH_ENABLE_ASSERTS + +/// Class that determines if two object layers can collide +class ObjectLayerPairFilterImpl : public JPH::ObjectLayerPairFilter { +public: + virtual bool ShouldCollide(JPH::ObjectLayer inObject1, + JPH::ObjectLayer inObject2) const override + { + switch (inObject1) { + case Layers::NON_MOVING: + return inObject2 == + Layers::MOVING; // Non moving only collides with moving + case Layers::MOVING: + return true; // Moving collides with everything + case Layers::SENSORS: + return inObject2 == + Layers::MOVING; // Non moving only collides with moving + default: + JPH_ASSERT(false); + return false; + } + } +}; + +// BroadPhaseLayerInterface implementation +// This defines a mapping between object and broadphase layers. +class BPLayerInterfaceImpl final : public JPH::BroadPhaseLayerInterface { +public: + BPLayerInterfaceImpl() + { + // Create a mapping table from object to broad phase layer + mObjectToBroadPhase[Layers::NON_MOVING] = + BroadPhaseLayers::NON_MOVING; + mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING; + mObjectToBroadPhase[Layers::SENSORS] = BroadPhaseLayers::MOVING; + } + + virtual uint GetNumBroadPhaseLayers() const override + { + return BroadPhaseLayers::NUM_LAYERS; + } + + virtual JPH::BroadPhaseLayer + GetBroadPhaseLayer(JPH::ObjectLayer inLayer) const override + { + JPH_ASSERT(inLayer < Layers::NUM_LAYERS); + return mObjectToBroadPhase[inLayer]; + } + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + virtual const char * + GetBroadPhaseLayerName(JPH::BroadPhaseLayer inLayer) const override + { + switch ((JPH::BroadPhaseLayer::Type)inLayer) { + case (JPH::BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING: + return "NON_MOVING"; + case (JPH::BroadPhaseLayer::Type)BroadPhaseLayers::MOVING: + return "MOVING"; + default: + JPH_ASSERT(false); + return "INVALID"; + } + } +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED + +private: + JPH::BroadPhaseLayer mObjectToBroadPhase[Layers::NUM_LAYERS]; +}; + +/// Class that determines if an object layer can collide with a broadphase layer +class ObjectVsBroadPhaseLayerFilterImpl + : public JPH::ObjectVsBroadPhaseLayerFilter { +public: + virtual bool ShouldCollide(JPH::ObjectLayer inLayer1, + JPH::BroadPhaseLayer inLayer2) const override + { + switch (inLayer1) { + case Layers::NON_MOVING: + return inLayer2 == BroadPhaseLayers::MOVING; + case Layers::MOVING: + return true; + default: + JPH_ASSERT(false); + return false; + } + } +}; + +#if 0 +// An example contact listener +class MyContactListener : public JPH::ContactListener { +public: + // See: ContactListener + virtual JPH::ValidateResult OnContactValidate( + const JPH::Body &inBody1, const JPH::Body &inBody2, + JPH::RVec3Arg inBaseOffset, + const JPH::CollideShapeResult &inCollisionResult) override + { + std::cout << "Contact validate callback" << std::endl; + + // Allows you to ignore a contact before it is created (using layers to not make objects collide is cheaper!) + return JPH::ValidateResult::AcceptAllContactsForThisBodyPair; + } + + virtual void OnContactAdded(const JPH::Body &inBody1, + const JPH::Body &inBody2, + const JPH::ContactManifold &inManifold, + JPH::ContactSettings &ioSettings) override + { + std::cout << "A contact was added" << std::endl; + } + + virtual void + OnContactPersisted(const JPH::Body &inBody1, const JPH::Body &inBody2, + const JPH::ContactManifold &inManifold, + JPH::ContactSettings &ioSettings) override + { + std::cout << "A contact was persisted" << std::endl; + } + + virtual void + OnContactRemoved(const JPH::SubShapeIDPair &inSubShapePair) override + { + std::cout << "A contact was removed" << std::endl; + } +}; + +// An example activation listener +class MyBodyActivationListener : public JPH::BodyActivationListener { +public: + virtual void OnBodyActivated(const JPH::BodyID &inBodyID, + JPH::uint64 inBodyUserData) override + { + std::cout << "A body got activated" << std::endl; + } + + virtual void OnBodyDeactivated(const JPH::BodyID &inBodyID, + JPH::uint64 inBodyUserData) override + { + std::cout << "A body went to sleep" << std::endl; + } +}; +#endif +class MyCollector : public JPH::CollideShapeBodyCollector { +public: + MyCollector(JPH::PhysicsSystem *inSystem, + JPH::RVec3Arg inSurfacePosition, + JPH::Vec3Arg inSurfaceNormal, float inDeltaTime) + : mSystem(inSystem) + , mSurfacePosition(inSurfacePosition) + , mSurfaceNormal(inSurfaceNormal) + , mDeltaTime(inDeltaTime) + { + } + + virtual void AddHit(const JPH::BodyID &inBodyID) override + { + mInWater.insert(inBodyID); + } + +private: + JPH::PhysicsSystem *mSystem; + JPH::RVec3 mSurfacePosition; + float mDeltaTime; + +public: + std::set mInWater; + JPH::Vec3 mSurfaceNormal; +}; + +class DebugRenderer final : public JPH::DebugRendererSimple { + Ogre::ManualObject *mObject; + Ogre::SceneManager *mScnMgr; + Ogre::SceneNode *mCameraNode; + Ogre::MaterialPtr mat; + struct line { + Ogre::Vector3 from; + Ogre::Vector3 to; + Ogre::ColourValue c; + }; + std::vector mLines; + struct tri { + Ogre::Vector3 p[3]; + Ogre::ColourValue c; + }; + std::vector mTriangles; + +public: + DebugRenderer(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode); + ~DebugRenderer() override; + void DrawLine(JPH::RVec3Arg inFrom, JPH::RVec3Arg inTo, + JPH::ColorArg inColor) override + { + JPH::Vec4 color = inColor.ToVec4(); + mLines.push_back( + { { (float)inFrom[0], (float)inFrom[1], + (float)inFrom[2] }, + { (float)inTo[0], (float)inTo[1], (float)inTo[2] }, + Ogre::ColourValue(color[0], color[1], color[2], + color[3]) }); + } + void DrawTriangle(JPH::RVec3Arg inV1, JPH::RVec3Arg inV2, + JPH::RVec3Arg inV3, JPH::ColorArg inColor, + ECastShadow inCastShadow = ECastShadow::Off) override + { + Ogre::Vector3 d = mCameraNode->_getDerivedOrientation() * + Ogre::Vector3(0, 0, -1); + JPH::Vec4 color = inColor.ToVec4(); + Ogre::Vector3 p1 = JoltPhysics::convert(inV1); + Ogre::Vector3 p2 = JoltPhysics::convert(inV2); + Ogre::Vector3 p3 = JoltPhysics::convert(inV3); + Ogre::ColourValue cv(color[0], color[1], color[2], color[3]); + +#if 0 + float dproj1 = p1.dotProduct(d); + float dproj2 = p2.dotProduct(d); + float dproj3 = p3.dotProduct(d); + if (dproj1 < 0 && dproj2 < 0 && dproj3 < 0) + return; + if (dproj1 > 50 && dproj2 > 50 && dproj3 > 50) + return; +#endif + mLines.push_back({ p1, p2, cv }); +#if 0 + mTriangles.push_back({ { { inV1[0], inV1[1], inV1[2] }, + { inV2[0], inV2[1], inV2[2] }, + { inV3[0], inV3[1], inV3[2] } }, + Ogre::ColourValue(color[0], color[1], + color[2], color[3]) }); +#endif + } +#if 0 + Batch CreateTriangleBatch(const Triangle *inTriangles, + int inTriangleCount) override + { + return Batch(); + } + Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, + const JPH::uint32 *inIndices, + int inIndexCount) override + { + return Batch(); + } +#endif + void DrawText3D(JPH::RVec3Arg inPosition, + const std::string_view &inString, + JPH::ColorArg inColor = JPH::Color::sWhite, + float inHeight = 0.5f) override + { + std::cout << "text\n"; + } +#if 0 + void DrawGeometry(JPH::RMat44Arg inModelMatrix, + const JPH::AABox &inWorldSpaceBounds, + float inLODScaleSq, JPH::ColorArg inModelColor, + const GeometryRef &inGeometry, + ECullMode inCullMode = ECullMode::CullBackFace, + ECastShadow inCastShadow = ECastShadow::On, + EDrawMode inDrawMode = EDrawMode::Solid) override + { + std::cout << "geometry\n"; + } +#endif + void finish() + { + Ogre::Vector3 d = mCameraNode->_getDerivedOrientation() * + Ogre::Vector3(0, 0, -1); + int i; + mObject->clear(); + mObject->begin(mat, Ogre::RenderOperation::OT_LINE_LIST); + for (i = 0; i < mLines.size(); i++) { +#if 0 + float dproj1 = mLines[i].from.dotProduct(d); + float dproj2 = mLines[i].to.dotProduct(d); + if (dproj1 < 0 && dproj2 < 0) + continue; + if (dproj1 > 50 && dproj2 > 50) + continue; +#endif + mObject->position(mLines[i].from); + mObject->colour(mLines[i].c); + mObject->position(mLines[i].to); + mObject->colour(mLines[i].c); + } + mObject->end(); + mLines.clear(); +#if 0 + mObject->begin(mat, Ogre::RenderOperation::OT_TRIANGLE_LIST); + for (i = 0; i < mTriangles.size(); i++) { + mObject->position(mTriangles[i].p[0]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[1]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[2]); + mObject->colour(mTriangles[i].c); + mObject->triangle(0, 1, 2); + mObject->triangle(2, 1, 0); + std::cout << mTriangles[i].p[0] << " "; + std::cout << mTriangles[i].p[1] << " "; + std::cout << mTriangles[i].p[2] << " " << std::endl; + } + mObject->end(); +#else +#if 0 + mObject->begin(mat, Ogre::RenderOperation::OT_LINE_LIST); + for (i = 0; i < mTriangles.size(); i++) { + float dproj1 = mTriangles[i].p[0].dotProduct(d); + float dproj2 = mTriangles[i].p[1].dotProduct(d); + float dproj3 = mTriangles[i].p[2].dotProduct(d); + if (dproj1 < 0 && dproj2 < 0 && dproj3 < 0) + continue; + if (dproj1 > 50 && dproj2 > 50 && dproj3 > 50) + continue; + mObject->position(mTriangles[i].p[0]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[1]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[1]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[2]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[2]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[0]); + mObject->colour(mTriangles[i].c); + } + mObject->end(); +#endif +#endif + mTriangles.clear(); + } +}; +DebugRenderer::DebugRenderer(Ogre::SceneManager *scnMgr, + Ogre::SceneNode *cameraNode) + : mObject(scnMgr->createManualObject("joltDebugRenderer")) + , mScnMgr(scnMgr) + , mCameraNode(cameraNode) + , mat(Ogre::MaterialManager::getSingleton().create("joltDebugDraw", + "General")) +{ + Ogre::Technique *technique = mat->getTechnique(0); + Ogre::Pass *pass = technique->getPass(0); + Ogre::ColourValue color(1, 0, 0, 1); + pass->setCullingMode(Ogre::CullingMode::CULL_NONE); + pass->setVertexColourTracking(Ogre::TVC_AMBIENT); + pass->setLightingEnabled(false); + pass->setDepthWriteEnabled(false); + pass->setDepthCheckEnabled(false); + DebugRenderer::Initialize(); + scnMgr->getRootSceneNode()->attachObject(mObject); + mLines.reserve(6000); + mObject->estimateVertexCount(64000); + mObject->estimateIndexCount(8000); + mObject->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY); +} +DebugRenderer::~DebugRenderer() +{ + mObject->getParentSceneNode()->detachObject(mObject); + mScnMgr->destroyManualObject(mObject); +} + +namespace JoltPhysics +{ +struct ShapeData { + JPH::ShapeRefC shape; +}; +Ogre::Vector3 convert(const JPH::Vec3Arg &vec) +{ + return { vec[0], vec[1], vec[2] }; +} +JPH::RVec3 convert(const Ogre::Vector3 &vec) +{ + return { vec.x, vec.y, vec.z }; +} +Ogre::Quaternion convert(const JPH::QuatArg &rot) +{ + return { rot.GetW(), rot.GetX(), rot.GetY(), rot.GetZ() }; +} +JPH::Quat convert(const Ogre::Quaternion &rot) +{ + return { rot.x, rot.y, rot.z, rot.w }; +} +void CompoundShapeBuilder::addShape(JPH::ShapeRefC shape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation) +{ + shapeSettings.AddShape(JoltPhysics::convert(position), + JoltPhysics::convert(rotation), shape.GetPtr()); +} +JPH::ShapeRefC CompoundShapeBuilder::build() +{ + JPH::ShapeSettings::ShapeResult result = shapeSettings.Create(); + return result.Get(); +} +ContactListener::ContactListener() + : JPH::ContactListener() + , dispatch(nullptr) +{ +} +JPH::ValidateResult ContactListener::OnContactValidate( + const JPH::Body &inBody1, const JPH::Body &inBody2, + JPH::RVec3Arg inBaseOffset, + const JPH::CollideShapeResult &inCollisionResult) +{ + return JPH::ValidateResult::AcceptAllContactsForThisBodyPair; +} +void ContactListener::OnContactAdded(const JPH::Body &inBody1, + const JPH::Body &inBody2, + const JPH::ContactManifold &inManifold, + JPH::ContactSettings &ioSettings) +{ + reports.push_back({ true, inBody1.GetID(), inBody2.GetID(), inManifold, + ioSettings }); +} +void ContactListener::OnContactPersisted(const JPH::Body &inBody1, + const JPH::Body &inBody2, + const JPH::ContactManifold &inManifold, + JPH::ContactSettings &ioSettings) +{ +} +void ContactListener::OnContactRemoved(const JPH::SubShapeIDPair &inSubShapePair) +{ + reports.push_back({ false, inSubShapePair.GetBody1ID(), + inSubShapePair.GetBody2ID(), JPH::ContactManifold(), + JPH::ContactSettings() }); +} +void ContactListener::update() +{ + for (auto contact : reports) { + bool handled = false; + if (listeners.find(contact.id1) != listeners.end()) { + listeners[contact.id1](contact); + handled = true; + } + if (listeners.find(contact.id2) != listeners.end()) { + listeners[contact.id2](contact); + handled = true; + } + if (!handled && dispatch) { + dispatch(contact); + } + } + reports.clear(); +} +} + +class Physics { + JPH::PhysicsSystem physics_system; + // We need a temp allocator for temporary allocations during the physics update. We're + // pre-allocating 10 MB to avoid having to do allocations during the physics update. + // B.t.w. 10 MB is way too much for this example but it is a typical value you can use. + // If you don't want to pre-allocate you can also use TempAllocatorMalloc to fall back to + // malloc / free. + JPH::TempAllocatorImpl temp_allocator; + // We need a job system that will execute physics jobs on multiple threads. Typically + // you would implement the JobSystem interface yourself and let Jolt Physics run on top + // of your own job scheduler. JobSystemThreadPool is an example implementation. + JPH::JobSystemThreadPool job_system; + + // Create mapping table from object layer to broadphase layer + // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! + // Also have a look at BroadPhaseLayerInterfaceTable or BroadPhaseLayerInterfaceMask for a simpler interface. + BPLayerInterfaceImpl broad_phase_layer_interface; + + // Create class that filters object vs broadphase layers + // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! + // Also have a look at ObjectVsBroadPhaseLayerFilterTable or ObjectVsBroadPhaseLayerFilterMask for a simpler interface. + ObjectVsBroadPhaseLayerFilterImpl object_vs_broadphase_layer_filter; + + // Create class that filters object vs object layers + // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! + // Also have a look at ObjectLayerPairFilterTable or ObjectLayerPairFilterMask for a simpler interface. + ObjectLayerPairFilterImpl object_vs_object_layer_filter; + + DebugRenderer *mDebugRenderer; + std::map id2node; + std::map node2id; + std::set characters; + std::set characterBodies; + bool debugDraw; + +public: + class ActivationListener : public JPH::BodyActivationListener { + public: + virtual void OnBodyActivated(const JPH::BodyID &inBodyID, + JPH::uint64 inBodyUserData) = 0; + virtual void OnBodyDeactivated(const JPH::BodyID &inBodyID, + JPH::uint64 inBodyUserData) = 0; + }; + Physics(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, + ActivationListener *activationListener = nullptr, + JPH::ContactListener *contactListener = nullptr) + : temp_allocator(10 * 1024 * 1024) + , job_system(JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsBarriers, + std::thread::hardware_concurrency() - 1) + , mDebugRenderer(new DebugRenderer(scnMgr, cameraNode)) + , debugDraw(false) + { + // Create a factory, this class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data. + // It is not directly used in this example but still required. + JPH::Factory::sInstance = new JPH::Factory(); + + // Register all physics types with the factory and install their collision handlers with the CollisionDispatch class. + // If you have your own custom shape types you probably need to register their handlers with the CollisionDispatch before calling this function. + // If you implement your own default material (PhysicsMaterial::sDefault) make sure to initialize it before this function or else this function will create one for you. + JPH::RegisterTypes(); + + // This is the max amount of rigid bodies that you can add to the physics system. If you try to add more you'll get an error. + // Note: This value is low because this is a simple test. For a real project use something in the order of 65536. + const uint cMaxBodies = 1024; + + // This determines how many mutexes to allocate to protect rigid bodies from concurrent access. Set it to 0 for the default settings. + const uint cNumBodyMutexes = 0; + + // This is the max amount of body pairs that can be queued at any time (the broad phase will detect overlapping + // body pairs based on their bounding boxes and will insert them into a queue for the narrowphase). If you make this buffer + // too small the queue will fill up and the broad phase jobs will start to do narrow phase work. This is slightly less efficient. + // Note: This value is low because this is a simple test. For a real project use something in the order of 65536. + const uint cMaxBodyPairs = 1024; + + // This is the maximum size of the contact constraint buffer. If more contacts (collisions between bodies) are detected than this + // number then these contacts will be ignored and bodies will start interpenetrating / fall through the world. + // Note: This value is low because this is a simple test. For a real project use something in the order of 10240. + const uint cMaxContactConstraints = 1024; + + // Now we can create the actual physics system. + physics_system.Init(cMaxBodies, cNumBodyMutexes, cMaxBodyPairs, + cMaxContactConstraints, + broad_phase_layer_interface, + object_vs_broadphase_layer_filter, + object_vs_object_layer_filter); + + // A body activation listener gets notified when bodies activate and go to sleep + // Note that this is called from a job so whatever you do here needs to be thread safe. + // Registering one is entirely optional. + if (activationListener) + physics_system.SetBodyActivationListener( + activationListener); + + // A contact listener gets notified when bodies (are about to) collide, and when they separate again. + // Note that this is called from a job so whatever you do here needs to be thread safe. + // Registering one is entirely optional. + if (contactListener) + physics_system.SetContactListener(contactListener); + + // The main way to interact with the bodies in the physics system is through the body interface. There is a locking and a non-locking + // variant of this. We're going to use the locking version (even though we're not planning to access bodies from multiple threads) + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); +#if 0 + + // Next we can create a rigid body to serve as the floor, we make a large box + // Create the settings for the collision volume (the shape). + // Note that for simple shapes (like boxes) you can also directly construct a BoxShape. + JPH::BoxShapeSettings floor_shape_settings( + JPH::Vec3(100.0f, 1.0f, 100.0f)); + floor_shape_settings + .SetEmbedded(); // A ref counted object on the stack (base class RefTarget) should be marked as such to prevent it from being freed when its reference count goes to 0. + + // Create the shape + JPH::ShapeSettings::ShapeResult floor_shape_result = + floor_shape_settings.Create(); + JPH::ShapeRefC floor_shape = + floor_shape_result + .Get(); // We don't expect an error here, but you can check floor_shape_result for HasError() / GetError() + + // Create the settings for the body itself. Note that here you can also set other properties like the restitution / friction. + JPH::Body *floor; + JPH::BodyID sphere_id; + { + using namespace JPH::literals; + JPH::BodyCreationSettings floor_settings( + floor_shape, JPH::RVec3(0.0_r, -1.0_r, 0.0_r), + JPH::Quat::sIdentity(), + JPH::EMotionType::Static, Layers::NON_MOVING); + floor = body_interface.CreateBody( + floor_settings); // Note that if we run out of bodies this can return nullptr + // Create the actual rigid body + // Add it to the world + body_interface.AddBody(floor->GetID(), + JPH::EActivation::DontActivate); + + // Now create a dynamic body to bounce on the floor + // Note that this uses the shorthand version of creating and adding a body to the world + JPH::BodyCreationSettings sphere_settings( + new JPH::SphereShape(0.5f), + JPH::RVec3(0.0_r, 2.0_r, 0.0_r), + JPH::Quat::sIdentity(), + JPH::EMotionType::Dynamic, Layers::MOVING); + sphere_id = body_interface.CreateAndAddBody( + sphere_settings, JPH::EActivation::Activate); + + // Now you can interact with the dynamic body, in this case we're going to give it a velocity. + // (note that if we had used CreateBody then we could have set the velocity straight on the body before adding it to the physics system) + body_interface.SetLinearVelocity( + sphere_id, JPH::Vec3(0.0f, -5.0f, 0.0f)); + } + + // We simulate the physics world in discrete time steps. 60 Hz is a good rate to update the physics system. + const float cDeltaTime = 1.0f / 60.0f; + + // Optional step: Before starting the physics simulation you can optimize the broad phase. This improves collision detection performance (it's pointless here because we only have 2 bodies). + // You should definitely not call this every frame or when e.g. streaming in a new level section as it is an expensive operation. + // Instead insert all new objects in batches instead of 1 at a time to keep the broad phase efficient. + physics_system.OptimizeBroadPhase(); + + // Now we're ready to simulate the body, keep simulating until it goes to sleep + uint step = 0; + while (body_interface.IsActive(sphere_id)) { + // Next step + ++step; + + // Output current position and velocity of the sphere + JPH::RVec3 position = + body_interface.GetCenterOfMassPosition( + sphere_id); + JPH::Vec3 velocity = + body_interface.GetLinearVelocity(sphere_id); + std::cout << "Step " << step << ": Position = (" + << position.GetX() << ", " << position.GetY() + << ", " << position.GetZ() + << "), Velocity = (" << velocity.GetX() + << ", " << velocity.GetY() << ", " + << velocity.GetZ() << ")" << std::endl; + + // If you take larger steps than 1 / 60th of a second you need to do multiple collision steps in order to keep the simulation stable. Do 1 collision step per 1 / 60th of a second (round up). + const int cCollisionSteps = 1; + + // Step the world + physics_system.Update(cDeltaTime, cCollisionSteps, + &temp_allocator, &job_system); + } + + // Remove the sphere from the physics system. Note that the sphere itself keeps all of its state and can be re-added at any time. + body_interface.RemoveBody(sphere_id); + + // Destroy the sphere. After this the sphere ID is no longer valid. + body_interface.DestroyBody(sphere_id); + + // Remove and destroy the floor + body_interface.RemoveBody(floor->GetID()); + body_interface.DestroyBody(floor->GetID()); +#endif + physics_system.SetGravity(JPH::Vec3(0, -0.1f, 0)); + } + ~Physics() + { + // Unregisters all types with the factory and cleans up the default material + JPH::UnregisterTypes(); + + // Destroy the factory + delete JPH::Factory::sInstance; + JPH::Factory::sInstance = nullptr; + } + float timeAccumulator = 0.0f; + float fixedDeltaTime = 1.0f / 60.0f; + void update(float dt) + { + JPH::BodyIDVector bodies; + physics_system.GetBodies(bodies); + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + for (JPH::BodyID bID : bodies) { + if (id2node.find(bID) == id2node.end()) + continue; + if (body_interface.GetMotionType(bID) == + JPH::EMotionType::Kinematic) { + Ogre::SceneNode *node = id2node[bID]; + body_interface.SetPositionAndRotationWhenChanged( + bID, + JoltPhysics::convert( + node->_getDerivedPosition()), + JoltPhysics::convert( + node->_getDerivedOrientation()), + JPH::EActivation::Activate); + } + } + for (JPH::Character *ch : characters) { + JPH::BodyID bID = ch->GetBodyID(); + if (id2node.find(bID) == id2node.end()) + continue; + Ogre::SceneNode *node = id2node[bID]; + ch->SetRotation(JoltPhysics::convert( + node->_getDerivedOrientation())); + } + int cCollisionSteps = 1; + timeAccumulator += dt; + if (debugDraw) + cCollisionSteps = 4; + while (timeAccumulator >= fixedDeltaTime) { + physics_system.Update(dt, cCollisionSteps, + &temp_allocator, &job_system); + timeAccumulator -= fixedDeltaTime; + } + for (JPH::BodyID bID : bodies) { + JPH::RVec3 p; + JPH::Quat q; + if (id2node.find(bID) == id2node.end()) + continue; + if (!body_interface.IsAdded(bID)) + continue; + if (!body_interface.IsActive(bID)) + continue; + if (body_interface.GetMotionType(bID) != + JPH::EMotionType::Dynamic) + continue; + body_interface.GetPositionAndRotation(bID, p, q); + Ogre::SceneNode *node = id2node[bID]; + node->_setDerivedPosition(JoltPhysics::convert(p)); + node->_setDerivedOrientation(JoltPhysics::convert(q)); + } + for (JPH::Character *ch : characters) { + if (body_interface.IsAdded(ch->GetBodyID())) + ch->PostSimulation(0.1f); + } + + if (debugDraw) + physics_system.DrawBodies( + JPH::BodyManager::DrawSettings(), + mDebugRenderer); + mDebugRenderer->finish(); + mDebugRenderer->NextFrame(); +#if 0 + std::cout << "bodies: " << physics_system.GetNumBodies() + << " / " + << physics_system.GetNumActiveBodies( + JPH::EBodyType::RigidBody) + << std::endl; +#endif + } + void setDebugDraw(bool enable) + { + debugDraw = enable; + } + static JPH::ShapeRefC createBoxShape(float x, float y, float z) + { + return new JPH::BoxShape(JPH::Vec3(x, y, z)); + } + static JPH::ShapeRefC createCylinderShape(float halfHeight, + float radius) + { + return new JPH::CylinderShape(halfHeight, radius); + } + JPH::BodyCreationSettings createBodyCreationSettings( + JPH::Shape *shape, JPH::RVec3 &position, JPH::Quat &rotation, + JPH::EMotionType motionType, JPH::ObjectLayer layer) + { + JPH::BodyCreationSettings body_settings( + shape, position, rotation, motionType, layer); + return body_settings; + } + JPH::BodyCreationSettings + createBodyCreationSettings(JPH::ShapeSettings *shapeSettings, + JPH::RVec3 &position, JPH::Quat &rotation, + JPH::EMotionType motionType, + JPH::ObjectLayer layer) + { + JPH::BodyCreationSettings body_settings( + shapeSettings, position, rotation, motionType, layer); + return body_settings; + } + void addBody(const JPH::BodyID &body, JPH::EActivation activation) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.AddBody(body, activation); + } + bool isAdded(const JPH::BodyID &body) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + return body_interface.IsAdded(body); + } + void addAngularImpulse(const JPH::BodyID &id, + const Ogre::Vector3 &impulse) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.AddAngularImpulse( + id, JoltPhysics::convert(impulse)); + } + JPH::BodyID createBody(const JPH::BodyCreationSettings &settings, + ActivationListener *listener = nullptr) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + JPH::Body *body = body_interface.CreateBody(settings); + if (!body) + return JPH::BodyID(); + return body->GetID(); + } + void removeBody(const JPH::BodyID &id) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.RemoveBody(id); + } + void destroyBody(const JPH::BodyID &id) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.DestroyBody(id); + node2id.erase(id2node[id]); + id2node.erase(id); + } + JPH::ShapeRefC createShape(const JPH::ShapeSettings &settings) + { + JPH::ShapeSettings::ShapeResult result = settings.Create(); + JPH::ShapeRefC shape = result.Get(); + return shape; + } + JPH::BodyID createBody(const JPH::Shape *shape, float mass, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, JPH::ObjectLayer layer, + ActivationListener *listener = nullptr) + { + JPH::BodyCreationSettings bodySettings( + shape, JoltPhysics::convert(position), + JoltPhysics::convert(rotation), motion, layer); + if (mass > 0.001f) { + JPH::MassProperties msp; + msp.ScaleToMass(mass); + bodySettings.mMassPropertiesOverride = msp; + } + JPH::BodyID id = createBody(bodySettings, listener); + if (shape->GetType() == JPH::EShapeType::HeightField) { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.SetFriction(id, 1.0f); + } + return id; + } + JPH::BodyID createBody(const JPH::Shape *shape, float mass, + Ogre::SceneNode *node, JPH::EMotionType motion, + JPH::ObjectLayer layer, + ActivationListener *listener = nullptr) + { + const Ogre::Vector3 &position = node->_getDerivedPosition(); + const Ogre::Quaternion &rotation = + node->_getDerivedOrientation(); + std::cout << "body position: " << position << std::endl; + JPH::BodyID id = createBody(shape, mass, position, rotation, + motion, layer, listener); + id2node[id] = node; + node2id[node] = id; + return id; + } + JPH::BodyID createSensor(const JPH::Shape *shape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, + JPH::ObjectLayer layer) + { + JPH::BodyCreationSettings bodySettings( + shape, JoltPhysics::convert(position), + JoltPhysics::convert(rotation), motion, layer); + bodySettings.mIsSensor = true; + return createBody(bodySettings, nullptr); + } + JPH::BodyID createSensor(const JPH::Shape *shape, Ogre::SceneNode *node, + JPH::EMotionType motion, + JPH::ObjectLayer layer) + { + const Ogre::Vector3 &position = node->_getDerivedPosition(); + const Ogre::Quaternion &rotation = + node->_getDerivedOrientation(); + std::cout << "body position: " << position << std::endl; + JPH::BodyCreationSettings bodySettings( + shape, JoltPhysics::convert(position), + JoltPhysics::convert(rotation), motion, layer); + bodySettings.mIsSensor = true; + JPH::BodyID id = createBody(bodySettings); + if (shape->GetType() == JPH::EShapeType::HeightField) { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.SetFriction(id, 0.7f); + } + id2node[id] = node; + node2id[node] = id; + return id; + } + void addShapeToCompound(JPH::Ref compoundShape, + JPH::ShapeRefC childShape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation) + { + JPH::MutableCompoundShape *master = + static_cast( + compoundShape.GetPtr()); + master->AddShape(JoltPhysics::convert(position), + JoltPhysics::convert(rotation), + childShape.GetPtr()); + } + class CharacterListener : public JPH::ContactListener {}; + JPH::CharacterBase *createCharacter(Ogre::SceneNode *node, + float characterHeight, + float characterRadius) + { + JPH::CharacterSettings settings; + settings.mLayer = Layers::MOVING; + float characterRadiusStanding = 0.2f; + settings.mSupportingVolume = + JPH::Plane(JPH::Vec3::sAxisY(), -0.2f); + settings.mShape = + JPH::RotatedTranslatedShapeSettings( + JPH::Vec3(0, + 0.5f * characterHeight + + characterRadius, + 0), + JPH::Quat::sIdentity(), + new JPH::CapsuleShape(0.5f * characterHeight, + characterRadius)) + .Create() + .Get(); + settings.mSupportingVolume = + JPH::Plane(JPH::Vec3::sAxisY(), -characterRadius); + JPH::Character *ch = new JPH::Character( + &settings, + JoltPhysics::convert(node->_getDerivedPosition()), + JoltPhysics::convert(node->_getDerivedOrientation()), 0, + &physics_system); + JPH::BodyID id = ch->GetBodyID(); + id2node[id] = node; + node2id[node] = id; + characterBodies.insert(id); + characters.insert(ch); + return ch; + } + JPH::ShapeRefC createBoxShape(Ogre::Vector3 extents) + { + JPH::Vec3 h(extents.x, extents.y, extents.z); + return new JPH::BoxShape(h); + } + JPH::ShapeRefC createSphereShape(float radius) + { + return new JPH::SphereShape(radius); + } + JPH::ShapeRefC createCapsuleShape(float halfHeightOfCylinder, + float radius) + { + return new JPH::CapsuleShape(halfHeightOfCylinder, radius); + } + JPH::ShapeRefC createMeshShape(Ogre::MeshPtr mesh) + { + JPH::VertexList vertices; + JPH::IndexedTriangleList triangles; + std::vector indices; + int count = mesh->getNumSubMeshes(); + int i, j; + int indexCount = 0; + int vertexCount = 0; + int sharedVertexOffset = 0; + for (i = 0; i < count; i++) { + Ogre::SubMesh *submesh = mesh->getSubMesh(i); + indexCount += submesh->indexData->indexCount; + if (submesh->useSharedVertices) + vertexCount += + mesh->sharedVertexData->vertexCount; + else + vertexCount += submesh->vertexData->vertexCount; + } + indices.reserve(indexCount); + vertices.reserve(vertexCount); + size_t currentVertexOffset = 0; + bool added_shared = false; + for (i = 0; i < count; i++) { + Ogre::SubMesh *submesh = mesh->getSubMesh(i); + Ogre::VertexData *vertex_data = + submesh->useSharedVertices ? + mesh->sharedVertexData : + submesh->vertexData; + bool add_vertices = + (submesh->useSharedVertices && !added_shared) || + !submesh->useSharedVertices; + if (add_vertices) { + if (submesh->useSharedVertices) + sharedVertexOffset = vertices.size(); + const Ogre::VertexDeclaration *decl = + vertex_data->vertexDeclaration; + const Ogre::VertexBufferBinding *bind = + vertex_data->vertexBufferBinding; + const Ogre::VertexElement *position_element = + decl->findElementBySemantic( + Ogre::VES_POSITION); + if (!position_element) + continue; + Ogre::HardwareVertexBufferSharedPtr vbuf = + bind->getBuffer( + position_element->getSource()); + unsigned char *vertex_buffer = static_cast< + unsigned char *>(vbuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + int vertexSize = vbuf->getVertexSize(); + for (j = 0; j < vertex_data->vertexCount; j++) { + float *position_data; + position_element + ->baseVertexPointerToElement( + vertex_buffer, + &position_data); + vertices.push_back( + { position_data[0], + position_data[1], + position_data[2] }); + vertex_buffer += vertexSize; + } + if (submesh->useSharedVertices) + added_shared = true; + vbuf->unlock(); + } + Ogre::HardwareIndexBufferSharedPtr ibuf = + submesh->indexData->indexBuffer; + size_t numIndices = submesh->indexData->indexCount; + size_t vertexOffset = submesh->useSharedVertices ? + sharedVertexOffset : + currentVertexOffset; + if (ibuf->getType() == + Ogre::HardwareIndexBuffer::IT_32BIT) { + unsigned int *pIndices = static_cast< + unsigned int *>(ibuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + for (j = 0; j < numIndices; j++) { + indices.push_back( + (uint32_t)pIndices[j] + + vertexOffset); + } + ibuf->unlock(); + } else { + unsigned short *pIndices = static_cast< + unsigned short *>(ibuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + for (j = 0; j < numIndices; j++) { + indices.push_back( + (uint32_t)pIndices[j] + + vertexOffset); + } + ibuf->unlock(); + } + currentVertexOffset = vertices.size(); + } + triangles.resize(indices.size() / 3); + for (j = 0; j < indices.size() / 3; j++) + triangles[j] = { indices[j * 3 + 0], indices[j * 3 + 1], + indices[j * 3 + 2] }; + JPH::MeshShapeSettings mesh_shape_settings(vertices, triangles); + JPH::ShapeSettings::ShapeResult result = + mesh_shape_settings.Create(); + OgreAssert(result.Get(), "Can not create mesh shape"); + return result.Get(); + } + JPH::ShapeRefC createMeshShape(Ogre::String meshName) + { + Ogre::DefaultHardwareBufferManager dmgr; + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton().getByName(meshName); + if (!mesh->isLoaded()) { + mesh->setHardwareBufferManager(&dmgr); + mesh->load(); + } + return createMeshShape(mesh); + } + JPH::ShapeRefC createConvexHullShape(Ogre::MeshPtr mesh) + { + std::vector vertices; + JPH::IndexedTriangleList triangles; + std::vector indices; + int count = mesh->getNumSubMeshes(); + int i, j; + int indexCount = 0; + int vertexCount = 0; + int sharedVertexOffset = 0; + for (i = 0; i < count; i++) { + Ogre::SubMesh *submesh = mesh->getSubMesh(i); + indexCount += submesh->indexData->indexCount; + if (submesh->useSharedVertices) + vertexCount += + mesh->sharedVertexData->vertexCount; + else + vertexCount += submesh->vertexData->vertexCount; + } + indices.reserve(indexCount); + vertices.reserve(vertexCount); + size_t currentVertexOffset = 0; + bool added_shared = false; + for (i = 0; i < count; i++) { + Ogre::SubMesh *submesh = mesh->getSubMesh(i); + Ogre::VertexData *vertex_data = + submesh->useSharedVertices ? + mesh->sharedVertexData : + submesh->vertexData; + bool add_vertices = + (submesh->useSharedVertices && !added_shared) || + !submesh->useSharedVertices; + if (add_vertices) { + if (submesh->useSharedVertices) + sharedVertexOffset = vertices.size(); + const Ogre::VertexDeclaration *decl = + vertex_data->vertexDeclaration; + const Ogre::VertexBufferBinding *bind = + vertex_data->vertexBufferBinding; + const Ogre::VertexElement *position_element = + decl->findElementBySemantic( + Ogre::VES_POSITION); + if (!position_element) + continue; + Ogre::HardwareVertexBufferSharedPtr vbuf = + bind->getBuffer( + position_element->getSource()); + unsigned char *vertex_buffer = static_cast< + unsigned char *>(vbuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + int vertexSize = vbuf->getVertexSize(); + for (j = 0; j < vertex_data->vertexCount; j++) { + float *position_data; + position_element + ->baseVertexPointerToElement( + vertex_buffer, + &position_data); + vertices.push_back( + { position_data[0], + position_data[1], + position_data[2] }); + vertex_buffer += vertexSize; + } + if (submesh->useSharedVertices) + added_shared = true; + vbuf->unlock(); + } + Ogre::HardwareIndexBufferSharedPtr ibuf = + submesh->indexData->indexBuffer; + size_t numIndices = submesh->indexData->indexCount; + size_t vertexOffset = submesh->useSharedVertices ? + sharedVertexOffset : + currentVertexOffset; + if (ibuf->getType() == + Ogre::HardwareIndexBuffer::IT_32BIT) { + unsigned int *pIndices = static_cast< + unsigned int *>(ibuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + for (j = 0; j < numIndices; j++) { + indices.push_back( + (uint32_t)pIndices[j] + + vertexOffset); + } + ibuf->unlock(); + } else { + unsigned short *pIndices = static_cast< + unsigned short *>(ibuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + for (j = 0; j < numIndices; j++) { + indices.push_back( + (uint32_t)pIndices[j] + + vertexOffset); + } + ibuf->unlock(); + } + currentVertexOffset = vertices.size(); + } + triangles.resize(indices.size() / 3); + for (j = 0; j < indices.size() / 3; j++) + triangles[j] = { indices[j * 3 + 0], indices[j * 3 + 1], + indices[j * 3 + 2] }; + JPH::ConvexHullShapeSettings mesh_shape_settings( + vertices.data(), vertices.size()); + JPH::ShapeSettings::ShapeResult result = + mesh_shape_settings.Create(); + OgreAssert(result.Get(), "Can not create mesh shape"); + return result.Get(); + } + JPH::ShapeRefC createConvexHullShape(Ogre::String meshName) + { + auto p = Ogre::DefaultHardwareBufferManager::getSingletonPtr(); + if (!p) { + new Ogre::DefaultHardwareBufferManager; + p = Ogre::DefaultHardwareBufferManager::getSingletonPtr(); + } + Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().load( + meshName, "General"); + if (mesh.get()) { + if (!mesh->isLoaded()) { + mesh->setHardwareBufferManager(p); + mesh->load(); + } + return createConvexHullShape(mesh); + } + OgreAssert(mesh.get(), "No file " + meshName); + return JPH::ShapeRefC(); + } + /// Create a height field shape of inSampleCount * inSampleCount vertices. + /// The height field is a surface defined by: inOffset + inScale * (x, inSamples[y * inSampleCount + x], y). + /// where x and y are integers in the range x and y e [0, inSampleCount - 1]. + /// inSampleCount: inSampleCount / mBlockSize must be minimally 2 and a power of 2 is the most efficient in terms of performance and storage. + /// inSamples: inSampleCount^2 vertices. + /// inMaterialIndices: (inSampleCount - 1)^2 indices that index into inMaterialList. + JPH::ShapeRefC createHeightfieldShape(const float *samples, + Ogre::Vector3 offset, + Ogre::Vector3 scale, + int sampleCount) + { + int i; + JPH::HeightFieldShapeSettings heightfieldSettings( + samples, JoltPhysics::convert(offset), + JoltPhysics::convert(scale), + (uint32_t)sampleCount); + for (i = 0; i < sampleCount; i++) { + memcpy(heightfieldSettings.mHeightSamples.data() + + sampleCount * i, + samples + sampleCount * (sampleCount - i - 1), + sizeof(float) * sampleCount); + } + JPH::ShapeSettings::ShapeResult result = + heightfieldSettings.Create(); + OgreAssert(result.Get(), "Can not create heightfield shape"); + return result.Get(); + } + JPH::ShapeRefC createMutableCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations) + { + int i; + OgreAssert(shapes.size() == positions.size() && + shapes.size() == rotations.size(), + "bad parameters"); + JPH::MutableCompoundShapeSettings settings; + for (i = 0; i < shapes.size(); i++) + settings.AddShape( + JoltPhysics::convert(positions[i]), + JoltPhysics::convert(rotations[i]), + shapes[i].GetPtr()); + JPH::ShapeSettings::ShapeResult result = settings.Create(); + OgreAssert(result.Get(), "Can not create compound shape"); + return result.Get(); + } + JPH::ShapeRefC createStaticCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations) + { + int i; + OgreAssert(shapes.size() == positions.size() && + shapes.size() == rotations.size(), + "bad parameters"); + JPH::StaticCompoundShapeSettings settings; + for (i = 0; i < shapes.size(); i++) + settings.AddShape( + JoltPhysics::convert(positions[i]), + JoltPhysics::convert(rotations[i]), + shapes[i].GetPtr()); + JPH::ShapeSettings::ShapeResult result = settings.Create(); + OgreAssert(result.Get(), "Can not create compound shape"); + return result.Get(); + } + JPH::ShapeRefC + createOffsetCenterOfMassShape(const Ogre::Vector3 &offset, + JPH::ShapeRefC shape) + { + JPH::OffsetCenterOfMassShapeSettings settings( + JoltPhysics::convert(offset), + shape.GetPtr()); + JPH::ShapeSettings::ShapeResult result = settings.Create(); + OgreAssert(result.Get(), "Can not create com offset shape"); + return result.Get(); + } + JPH::ShapeRefC + createRotatedTranslatedShape(const Ogre::Vector3 &offset, + const Ogre::Quaternion rotation, + JPH::ShapeRefC shape) + { + return JPH::RotatedTranslatedShapeSettings( + JoltPhysics::convert(offset), + JoltPhysics::convert(rotation), shape) + .Create() + .Get(); + } + void applyBuoyancyImpulse(JPH::BodyID id, + const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, + float buoyancy, float linearDrag, + float angularDrag, + const Ogre::Vector3 &fluidVelocity, + const Ogre::Vector3 &gravity, float dt) + { + JPH::BodyLockWrite lock(physics_system.GetBodyLockInterface(), + id); + JPH::Body &body = lock.GetBody(); + body.ApplyBuoyancyImpulse( + JoltPhysics::convert(surfacePosition), + JoltPhysics::convert(surfaceNormal), + buoyancy, linearDrag, angularDrag, + JoltPhysics::convert(fluidVelocity), + JoltPhysics::convert(gravity), dt); + } + void applyBuoyancyImpulse(JPH::BodyID id, + const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, + float buoyancy, float linearDrag, + float angularDrag, + const Ogre::Vector3 &fluidVelocity, float dt) + { + JPH::BodyLockWrite lock(physics_system.GetBodyLockInterface(), + id); + JPH::Body &body = lock.GetBody(); + body.ApplyBuoyancyImpulse( + JoltPhysics::convert(surfacePosition), + JoltPhysics::convert(surfaceNormal), + buoyancy, linearDrag, angularDrag, + JoltPhysics::convert(fluidVelocity), + physics_system.GetGravity(), dt); + } + bool isActive(JPH::BodyID id) + { + return physics_system.GetBodyInterface().IsActive(id); + } + void activate(JPH::BodyID id) + { + return physics_system.GetBodyInterface().ActivateBody(id); + } + Ogre::Vector3 getPosition(JPH::BodyID id) + { + return JoltPhysics::convert( + physics_system.GetBodyInterface().GetPosition(id)); + } + Ogre::Quaternion getRotation(JPH::BodyID id) + { + return JoltPhysics::convert( + physics_system.GetBodyInterface().GetRotation(id)); + } + void getPositionAndRotation(JPH::BodyID id, Ogre::Vector3 &position, + Ogre::Quaternion &rotation) + { + JPH::RVec3 _position; + JPH::Quat _rotation; + physics_system.GetBodyInterface().GetPositionAndRotation( + id, _position, _rotation); + position = JoltPhysics::convert(_position); + rotation = JoltPhysics::convert(_rotation); + } + void setPosition(JPH::BodyID id, const Ogre::Vector3 &position, + bool activate = true) + { + physics_system.GetBodyInterface().SetPosition( + id, JoltPhysics::convert(position), + activate ? JPH::EActivation::Activate : + JPH::EActivation::DontActivate); + } + void setRotation(JPH::BodyID id, const Ogre::Quaternion &rotation, + bool activate = true) + { + physics_system.GetBodyInterface().SetRotation( + id, JoltPhysics::convert(rotation), + activate ? JPH::EActivation::Activate : + JPH::EActivation::DontActivate); + } + void setPositionAndRotation(JPH::BodyID id, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + bool activate = true) + { + physics_system.GetBodyInterface().SetPositionAndRotation( + id, JoltPhysics::convert(position), + JoltPhysics::convert(rotation), + activate ? JPH::EActivation::Activate : + JPH::EActivation::DontActivate); + } + Ogre::Vector3 getLinearVelocity(JPH::BodyID id) + { + return JoltPhysics::convert( + physics_system.GetBodyInterface().GetLinearVelocity( + id)); + } + Ogre::Vector3 getAngularVelocity(JPH::BodyID id) + { + return JoltPhysics::convert( + physics_system.GetBodyInterface().GetAngularVelocity( + id)); + } + float getFriction(JPH::BodyID id) + { + return physics_system.GetBodyInterface().GetFriction(id); + } + void setFriction(JPH::BodyID id, float friction) + { + return physics_system.GetBodyInterface().SetFriction(id, + friction); + } + void broadphaseQuery(float dt, const Ogre::Vector3 &position, + std::set &inWater) + { + JPH::RVec3 surface_point = JoltPhysics::convert( + position + Ogre::Vector3(0, -0.1f, 0)); + + MyCollector collector(&physics_system, surface_point, + JPH::Vec3::sAxisY(), dt); + // Apply buoyancy to all bodies that intersect with the water + JPH::AABox water_box(-JPH::Vec3(1000, 1000, 1000), + JPH::Vec3(1000, 0.1f, 1000)); + water_box.Translate(JPH::Vec3(surface_point)); + physics_system.GetBroadPhaseQuery().CollideAABox( + water_box, collector, + JPH::SpecifiedBroadPhaseLayerFilter( + BroadPhaseLayers::MOVING), + JPH::SpecifiedObjectLayerFilter(Layers::MOVING)); + inWater.clear(); + for (JPH::BodyID inBodyID : collector.mInWater) { + inWater.insert(inBodyID); +#if 0 + std::cout << "addHit: " + << JoltPhysics::convert( + body.GetPosition()) + << std::endl; +#endif + } + } + bool raycastQuery(Ogre::Vector3 startPoint, Ogre::Vector3 endPoint, + Ogre::Vector3 &position, JPH::BodyID &id) + { + int i; + Ogre::Vector3 direction = endPoint - startPoint; + JPH::RRayCast ray{ JoltPhysics::convert(startPoint), + JoltPhysics::convert(direction) }; + JPH::RayCastResult hit; + bool hadHit = physics_system.GetNarrowPhaseQuery().CastRay( + ray, hit, {}, + JPH::SpecifiedObjectLayerFilter(Layers::NON_MOVING)); + if (hadHit) { + position = JoltPhysics::convert( + ray.GetPointOnRay(hit.mFraction)); + id = hit.mBodyID; + } + return hadHit; + } +}; + +void physics() +{ + // Physics physics; + // physics.update(1.0f / 60.0f); +} + +Physics *phys = nullptr; +JoltPhysicsWrapper::JoltPhysicsWrapper(Ogre::SceneManager *scnMgr, + Ogre::SceneNode *cameraNode) + : Ogre::Singleton() +{ + // Register allocation hook. In this example we'll just let Jolt use malloc / free but you can override these if you want (see Memory.h). + // This needs to be done before any other Jolt function is called. + JPH::RegisterDefaultAllocator(); + + // Install trace and assert callbacks + JPH::Trace = TraceImpl; + JPH_IF_ENABLE_ASSERTS(JPH::AssertFailed = AssertFailedImpl;) + + phys = new Physics(scnMgr, cameraNode, nullptr, &contacts); +} + +JoltPhysicsWrapper::~JoltPhysicsWrapper() +{ + if (phys) + delete phys; +} + +void JoltPhysicsWrapper::update(float dt) +{ + phys->update(dt); + contacts.update(); +} + +void JoltPhysicsWrapper::addBody(const JPH::BodyID &body, + JPH::EActivation activation) +{ + phys->addBody(body, activation); +} + +bool JoltPhysicsWrapper::isAdded(const JPH::BodyID &body) +{ + return phys->isAdded(body); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createBoxShape(const Ogre::Vector3 &extents) +{ + return phys->createBoxShape(extents); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createSphereShape(float radius) +{ + return phys->createSphereShape(radius); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createCylinderShape(float halfHeight, + float radius) +{ + return phys->createCylinderShape(halfHeight, radius); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createMeshShape(Ogre::MeshPtr mesh) +{ + return phys->createMeshShape(mesh); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createMeshShape(Ogre::String meshName) +{ + return phys->createMeshShape(meshName); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createConvexHullShape(Ogre::MeshPtr mesh) +{ + return phys->createConvexHullShape(mesh); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createConvexHullShape(Ogre::String meshName) +{ + return phys->createConvexHullShape(meshName); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createHeightfieldShape(const float *samples, + Ogre::Vector3 offset, + Ogre::Vector3 scale, + int sampleCount) +{ + return phys->createHeightfieldShape(samples, offset, scale, + sampleCount); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createMutableCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations) +{ + return phys->createMutableCompoundShape(shapes, positions, rotations); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createStaticCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations) +{ + return phys->createStaticCompoundShape(shapes, positions, rotations); +} + +JPH::ShapeRefC +JoltPhysicsWrapper::createOffsetCenterOfMassShape(const Ogre::Vector3 &offset, + JPH::ShapeRefC shape) +{ + return phys->createOffsetCenterOfMassShape(offset, shape); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createRotatedTranslatedShape( + const Ogre::Vector3 &offset, const Ogre::Quaternion rotation, + JPH::ShapeRefC shape) +{ + return phys->createRotatedTranslatedShape(offset, rotation, shape); +} + +JPH::BodyID +JoltPhysicsWrapper::createBody(const JPH::BodyCreationSettings &settings) +{ + return phys->createBody(settings); +} +JPH::BodyID JoltPhysicsWrapper::createBody(const JPH::Shape *shape, float mass, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, + JPH::ObjectLayer layer) +{ + return phys->createBody(shape, mass, position, rotation, motion, layer); +} +JPH::BodyID JoltPhysicsWrapper::createBody(const JPH::Shape *shape, float mass, + Ogre::SceneNode *node, + JPH::EMotionType motion, + JPH::ObjectLayer layer) +{ + return phys->createBody(shape, mass, node, motion, layer); +} +JPH::BodyID JoltPhysicsWrapper::createSensor(const JPH::Shape *shape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, + JPH::ObjectLayer layer) +{ + return phys->createSensor(shape, position, rotation, motion, layer); +} +JPH::BodyID JoltPhysicsWrapper::createSensor(const JPH::Shape *shape, + Ogre::SceneNode *node, + JPH::EMotionType motion, + JPH::ObjectLayer layer) +{ + return phys->createSensor(shape, node, motion, layer); +} +JPH::CharacterBase *JoltPhysicsWrapper::createCharacter(Ogre::SceneNode *node, + float characterHeight, + float characterRadius) +{ + return phys->createCharacter(node, characterHeight, characterRadius); +} +void JoltPhysicsWrapper::addShapeToCompound(JPH::Ref compoundShape, + JPH::ShapeRefC childShape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation) +{ + phys->addShapeToCompound(compoundShape, childShape, position, rotation); +} +void JoltPhysicsWrapper::removeBody(const JPH::BodyID &id) +{ + phys->removeBody(id); +} +void JoltPhysicsWrapper::destroyBody(const JPH::BodyID &id) +{ + phys->destroyBody(id); +} +void JoltPhysicsWrapper::setDebugDraw(bool enable) +{ + phys->setDebugDraw(enable); +} +void JoltPhysicsWrapper::broadphaseQuery(float dt, + const Ogre::Vector3 &position, + std::set &inWater) +{ + phys->broadphaseQuery(dt, position, inWater); +} +void JoltPhysicsWrapper::applyBuoyancyImpulse( + JPH::BodyID id, const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, float buoyancy, float linearDrag, + float angularDrag, const Ogre::Vector3 &fluidVelocity, + const Ogre::Vector3 &gravity, float dt) +{ + phys->applyBuoyancyImpulse(id, surfacePosition, surfaceNormal, buoyancy, + linearDrag, angularDrag, fluidVelocity, + gravity, dt); +} +void JoltPhysicsWrapper::applyBuoyancyImpulse( + JPH::BodyID id, const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, float buoyancy, float linearDrag, + float angularDrag, const Ogre::Vector3 &fluidVelocity, float dt) +{ + phys->applyBuoyancyImpulse(id, surfacePosition, surfaceNormal, buoyancy, + linearDrag, angularDrag, fluidVelocity, dt); +} +bool JoltPhysicsWrapper::isActive(JPH::BodyID id) +{ + return phys->isActive(id); +} +void JoltPhysicsWrapper::activate(JPH::BodyID id) +{ + phys->activate(id); +} +Ogre::Vector3 JoltPhysicsWrapper::getPosition(JPH::BodyID id) +{ + return phys->getPosition(id); +} +void JoltPhysicsWrapper::setPosition(JPH::BodyID id, + const Ogre::Vector3 &position, + bool activate) +{ + return phys->setPosition(id, position, activate); +} +Ogre::Quaternion JoltPhysicsWrapper::getRotation(JPH::BodyID id) +{ + return phys->getRotation(id); +} +void JoltPhysicsWrapper::setRotation(JPH::BodyID id, + const Ogre::Quaternion &rotation, + bool activate) +{ + phys->setRotation(id, rotation, activate); +} +void JoltPhysicsWrapper::getPositionAndRotation(JPH::BodyID id, + Ogre::Vector3 &position, + Ogre::Quaternion &rotation) +{ + phys->getPositionAndRotation(id, position, rotation); +} +void JoltPhysicsWrapper::setPositionAndRotation( + JPH::BodyID id, const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, bool activate) +{ + phys->setPositionAndRotation(id, position, rotation, activate); +} +Ogre::Vector3 JoltPhysicsWrapper::getLinearVelocity(JPH::BodyID id) +{ + return phys->getLinearVelocity(id); +} +Ogre::Vector3 JoltPhysicsWrapper::getAngularVelocity(JPH::BodyID id) +{ + return phys->getAngularVelocity(id); +} +float JoltPhysicsWrapper::getFriction(JPH::BodyID id) +{ + return phys->getFriction(id); +} +void JoltPhysicsWrapper::setFriction(JPH::BodyID id, float friction) +{ + phys->setFriction(id, friction); +} +void JoltPhysicsWrapper::addAngularImpulse(const JPH::BodyID &id, + const Ogre::Vector3 &impulse) +{ + return phys->addAngularImpulse(id, impulse); +} +void JoltPhysicsWrapper::setDispatch( + std::function< + void(const JoltPhysics::ContactListener::ContactReport &report)> + dispatcher) +{ + contacts.setDispatch(dispatcher); +} +void JoltPhysicsWrapper::addContactListener( + const JPH::BodyID &id, + std::function< + void(const JoltPhysics::ContactListener::ContactReport &report)> + listener) +{ + contacts.addListener(id, listener); +} +void JoltPhysicsWrapper::removeContactListener(const JPH::BodyID &id) +{ + contacts.removeListener(id); +} +bool JoltPhysicsWrapper::raycastQuery(Ogre::Vector3 startPoint, + Ogre::Vector3 endPoint, + Ogre::Vector3 &position, JPH::BodyID &id) +{ + return phys->raycastQuery(startPoint, endPoint, position, id); +} +template <> +JoltPhysicsWrapper *Ogre::Singleton::msSingleton = 0; diff --git a/physics.h b/physics.h new file mode 100644 index 0000000..48d59c2 --- /dev/null +++ b/physics.h @@ -0,0 +1,221 @@ +#ifndef __PHYSICS_H_ +#define __PHYSICS_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void physics(); +namespace JPH +{ +class CharacterBase; +class ContactManifold; +class ContactSettings; +class SubShapeIDPair; +} +// Layer that objects can be in, determines which other objects it can collide with +// Typically you at least want to have 1 layer for moving bodies and 1 layer for static bodies, but you can have more +// layers if you want. E.g. you could have a layer for high detail collision (which is not used by the physics simulation +// but only if you do collision testing). +namespace Layers +{ +static constexpr JPH::ObjectLayer NON_MOVING = 0; +static constexpr JPH::ObjectLayer MOVING = 1; +static constexpr JPH::ObjectLayer SENSORS = 2; +static constexpr JPH::ObjectLayer NUM_LAYERS = 3; +}; + +// Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have +// a layer for non-moving and moving objects to avoid having to update a tree full of static objects every frame. +// You can have a 1-on-1 mapping between object layers and broadphase layers (like in this case) but if you have +// many object layers you'll be creating many broad phase trees, which is not efficient. If you want to fine tune +// your broadphase layers define JPH_TRACK_BROADPHASE_STATS and look at the stats reported on the TTY. +namespace BroadPhaseLayers +{ +static constexpr JPH::BroadPhaseLayer NON_MOVING(0); +static constexpr JPH::BroadPhaseLayer MOVING(1); +static constexpr uint NUM_LAYERS(2); +}; + +namespace JoltPhysics +{ +template +Ogre::Vector3 convert(const T &vec) +{ + return {vec[0], vec[1], vec[2]}; +} +template T convert(const Ogre::Vector3 &vec) +{ + return { vec.x, vec.y, vec.z }; +} +Ogre::Quaternion convert(const JPH::QuatArg &rot); +JPH::Quat convert(const Ogre::Quaternion &rot); +struct ShapeData; +struct CompoundShapeBuilder { + JPH::StaticCompoundShapeSettings shapeSettings; + void addShape(JPH::ShapeRefC shape, const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation); + JPH::ShapeRefC build(); +}; +class ContactListener : public JPH::ContactListener { +public: + struct ContactReport { + bool entered; + JPH::BodyID id1, id2; + JPH::ContactManifold manifold; + JPH::ContactSettings settings; + }; + +private: + std::list reports; + std::function dispatch; + std::map > + listeners; + +public: + ContactListener(); + JPH::ValidateResult OnContactValidate( + const JPH::Body &inBody1, const JPH::Body &inBody2, + JPH::RVec3Arg inBaseOffset, + const JPH::CollideShapeResult &inCollisionResult) override; + void OnContactAdded(const JPH::Body &inBody1, const JPH::Body &inBody2, + const JPH::ContactManifold &inManifold, + JPH::ContactSettings &ioSettings) override; + void OnContactPersisted(const JPH::Body &inBody1, + const JPH::Body &inBody2, + const JPH::ContactManifold &inManifold, + JPH::ContactSettings &ioSettings) override; + void + OnContactRemoved(const JPH::SubShapeIDPair &inSubShapePair) override; + void setDispatch(const std::function + dispatcher) + { + dispatch = dispatcher; + } + void addListener( + const JPH::BodyID &id, + const std::function listener) + { + listeners[id] = listener; + } + void removeListener(const JPH::BodyID &id) + { + listeners.erase(id); + } + void update(); +}; +} + +class JoltPhysicsWrapper : public Ogre::Singleton { +public: + JoltPhysics::ContactListener contacts; + JoltPhysicsWrapper(Ogre::SceneManager *scnMgr, + Ogre::SceneNode *cameraNode); + ~JoltPhysicsWrapper(); + void update(float dt); + void addBody(const JPH::BodyID &body, JPH::EActivation activation); + bool isAdded(const JPH::BodyID &body); + JPH::ShapeRefC createBoxShape(const Ogre::Vector3 &extents); + JPH::ShapeRefC createSphereShape(float radius); + JPH::ShapeRefC createCylinderShape(float halfHeight, float radius); + JPH::ShapeRefC createMeshShape(Ogre::MeshPtr mesh); + JPH::ShapeRefC createMeshShape(Ogre::String meshName); + JPH::ShapeRefC createConvexHullShape(Ogre::MeshPtr mesh); + JPH::ShapeRefC createConvexHullShape(Ogre::String meshName); + JPH::ShapeRefC createHeightfieldShape(const float *samples, + Ogre::Vector3 offset, + Ogre::Vector3 scale, + int sampleCount); + JPH::ShapeRefC createMutableCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations); + JPH::ShapeRefC createStaticCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations); + JPH::ShapeRefC + createOffsetCenterOfMassShape(const Ogre::Vector3 &offset, + JPH::ShapeRefC shape); + JPH::ShapeRefC + createRotatedTranslatedShape(const Ogre::Vector3 &offset, const Ogre::Quaternion rotation, + JPH::ShapeRefC shape); + JPH::BodyID createBody(const JPH::BodyCreationSettings &settings); + JPH::BodyID createBody(const JPH::Shape *shape, float mass, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, JPH::ObjectLayer layer); + JPH::BodyID createBody(const JPH::Shape *shape, float mass, + Ogre::SceneNode *node, JPH::EMotionType motion, + JPH::ObjectLayer layer); + JPH::BodyID createSensor(const JPH::Shape *shape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, + JPH::ObjectLayer layer); + JPH::BodyID createSensor(const JPH::Shape *shape, Ogre::SceneNode *node, + JPH::EMotionType motion, + JPH::ObjectLayer layer); + JPH::CharacterBase *createCharacter(Ogre::SceneNode *node, + float characterHeight, + float characterRadius); + void addShapeToCompound(JPH::Ref compoundShape, + JPH::ShapeRefC childShape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation); + void removeBody(const JPH::BodyID &id); + void destroyBody(const JPH::BodyID &id); + void setDebugDraw(bool enable); + void broadphaseQuery(float dt, const Ogre::Vector3 &position, + std::set &inWater); + void applyBuoyancyImpulse(JPH::BodyID id, + const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, + float buoyancy, float linearDrag, + float angularDrag, + const Ogre::Vector3 &fluidVelocity, + const Ogre::Vector3 &gravity, float dt); + void applyBuoyancyImpulse(JPH::BodyID id, + const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, + float buoyancy, float linearDrag, + float angularDrag, + const Ogre::Vector3 &fluidVelocity, float dt); + bool isActive(JPH::BodyID id); + void activate(JPH::BodyID id); + Ogre::Vector3 getPosition(JPH::BodyID id); + void setPosition(JPH::BodyID id, const Ogre::Vector3 &position, + bool activate = true); + Ogre::Quaternion getRotation(JPH::BodyID id); + void setRotation(JPH::BodyID id, const Ogre::Quaternion &rotation, + bool activate = true); + void getPositionAndRotation(JPH::BodyID id, Ogre::Vector3 &position, + Ogre::Quaternion &rotation); + void setPositionAndRotation(JPH::BodyID id, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + bool activate = true); + Ogre::Vector3 getLinearVelocity(JPH::BodyID id); + Ogre::Vector3 getAngularVelocity(JPH::BodyID id); + float getFriction(JPH::BodyID id); + void setFriction(JPH::BodyID id, float friction); + void addAngularImpulse(const JPH::BodyID &id, + const Ogre::Vector3 &impulse); + void setDispatch(std::function + dispatcher); + void addContactListener( + const JPH::BodyID &id, + const std::function + listener); + void removeContactListener(const JPH::BodyID &id); + bool raycastQuery(Ogre::Vector3 startPoint, Ogre::Vector3 endPoint, + Ogre::Vector3 &position, JPH::BodyID &id); +}; +#endif diff --git a/resources.cfg b/resources.cfg new file mode 100644 index 0000000..3035737 --- /dev/null +++ b/resources.cfg @@ -0,0 +1,82 @@ +# Ogre Core Resources +[OgreInternal] +#FileSystem=./Media/Main +FileSystem=resources/main +FileSystem=resources/shaderlib +#FileSystem=./Media/RTShaderLib +FileSystem=resources/terrain + +# Resources required by OgreBites::Trays +[Essential] +#Zip=./Media/packs/SdkTrays.zip +#Zip=./Media/packs/profiler.zip + +## this line will end up in the [Essential] group +#FileSystem=./Media/thumbnails + +# Common sample resources needed by many of the samples. +# Rarely used resources should be separately loaded by the +# samples which require them. +[General] +FileSystem=skybox +FileSystem=resources/buildings +FileSystem=resources/vehicles +FileSystem=resources/debug +FileSystem=resources/fonts +FileSystem=resources/world +# PBR media must come before the scripts that reference it +#FileSystem=./Media/PBR +#FileSystem=./Media/PBR/filament + +#FileSystem=./Media/materials/programs/GLSL +#FileSystem=./Media/materials/programs/GLSL120 +#FileSystem=./Media/materials/programs/GLSL150 +#FileSystem=./Media/materials/programs/GLSL400 +#FileSystem=./Media/materials/programs/GLSLES +#FileSystem=./Media/materials/programs/SPIRV +#FileSystem=./Media/materials/programs/Cg +#FileSystem=./Media/materials/programs/HLSL +#FileSystem=./Media/materials/programs/HLSL_Cg +#FileSystem=./Media/materials/scripts +#FileSystem=./Media/materials/textures +#FileSystem=./Media/materials/textures/terrain +#FileSystem=./Media/models +#FileSystem=./Media/particle +#FileSystem=./Media/DeferredShadingMedia +#FileSystem=./Media/DeferredShadingMedia/DeferredShading/post +#FileSystem=./Media/PCZAppMedia +#FileSystem=./Media/materials/scripts/SSAO +#FileSystem=./Media/materials/textures/SSAO +#FileSystem=./Media/volumeTerrain +#FileSystem=./Media/CSMShadows + +#Zip=./Media/packs/cubemap.zip +#Zip=./Media/packs/cubemapsJS.zip +#Zip=./Media/packs/dragon.zip +#Zip=./Media/packs/fresneldemo.zip +#Zip=./Media/packs/ogredance.zip +#Zip=./Media/packs/Sinbad.zip +#Zip=./Media/packs/skybox.zip +#Zip=./Media/volumeTerrain/volumeTerrainBig.zip + +#Zip=./Media/packs/DamagedHelmet.zip +#Zip=./Media/packs/filament_shaders.zip + +#[BSPWorld] +#Zip=./Media/packs/oa_rpg3dm2.pk3 +#Zip=./Media/packs/ogretestmap.zip + +# Materials for visual tests +#[Tests] +#FileSystem=/media/slapin/library/ogre/ogre-sdk/Tests/Media +[LuaScripts] +FileSystem=lua-scripts + +[Characters] +FileSystem=./characters/male +FileSystem=./characters/female +[Audio] +FileSystem=./audio/gui + +[Water] +FileSystem=water diff --git a/resources/debug/debug.frag b/resources/debug/debug.frag new file mode 100644 index 0000000..c4d79f0 --- /dev/null +++ b/resources/debug/debug.frag @@ -0,0 +1,13 @@ +OGRE_NATIVE_GLSL_VERSION_DIRECTIVE +#include + +OGRE_UNIFORMS( +uniform vec4 ambient; +uniform vec4 diffuse; +) + +MAIN_PARAMETERS +MAIN_DECLARATION +{ + gl_FragColor = ambient * diffuse; +} diff --git a/resources/debug/debug.material b/resources/debug/debug.material new file mode 100644 index 0000000..0ac47e3 --- /dev/null +++ b/resources/debug/debug.material @@ -0,0 +1,71 @@ +material Skybox/Debug1 +{ + technique + { + pass + { + lighting off + depth_write off + ambient 1.0 0.0 0.0 1.0 + diffuse 1.0 0.0 0.0 1.0 + vertex_program_ref debug_vp + { + } + fragment_program_ref debug_fp + { + } + } + } +} + + + +material Debug/Red +{ + technique + { + pass + { + lighting off + depth_check on + depth_write on + depth_func always_pass + ambient 1.0 0.0 0.0 1.0 + diffuse vertexcolour + specular 0.0 0.0 0.0 1.0 + cull_software none + cull_hardware none +/* + rtshader_system + { + lighting_stage metal_roughness + } +*/ + } + } +} + +material Debug/Red2 +{ + technique + { + pass + { + lighting off + depth_check on + depth_write on + depth_func always_pass + ambient 1.0 0.0 0.0 1.0 + diffuse vertexcolour + specular 0.0 0.0 0.0 1.0 + cull_software none + cull_hardware none + + rtshader_system + { + lighting_stage metal_roughness + } + } + } +} + diff --git a/resources/debug/debug.program b/resources/debug/debug.program new file mode 100644 index 0000000..93c333e --- /dev/null +++ b/resources/debug/debug.program @@ -0,0 +1,19 @@ +fragment_program debug_fp glsl glsles glslang hlsl +{ + source debug.frag + default_params + { + param_named_auto ambient surface_ambient_colour + param_named_auto diffuse surface_diffuse_colour + } +} + +vertex_program debug_vp glsl glsles glslang hlsl +{ + source debug.vert + default_params + { + param_named_auto worldViewProj worldviewproj_matrix + } +} + diff --git a/resources/debug/debug.vert b/resources/debug/debug.vert new file mode 100644 index 0000000..62d2a95 --- /dev/null +++ b/resources/debug/debug.vert @@ -0,0 +1,25 @@ +OGRE_NATIVE_GLSL_VERSION_DIRECTIVE +#include + +OGRE_UNIFORMS( + uniform mat4 worldViewProj; +) + +MAIN_PARAMETERS +IN(vec4 vertex, POSITION) +IN(vec3 normal, NORMAL) +IN(vec3 tangent, TANGENT) +IN(vec3 uv0, TEXCOORD0) +// uniform mat4 worldViewProj; + +// attribute vec4 vertex; +// attribute vec3 normal; +// attribute vec4 tangent; +// attribute vec2 uv0; + +MAIN_DECLARATION +// void main() +{ + // gl_Position = mul(worldViewProj, position); + gl_Position = worldViewProj * vertex; +} diff --git a/resources/main/DefaultShaders.metal b/resources/main/DefaultShaders.metal new file mode 100644 index 0000000..84defe9 --- /dev/null +++ b/resources/main/DefaultShaders.metal @@ -0,0 +1,38 @@ +#include "OgreUnifiedShader.h" + +struct RasterizerData +{ + vec4 pos [[position]]; + vec2 uv; +}; + +struct Vertex +{ + IN(vec3 pos, POSITION); + IN(vec2 uv, TEXCOORD0); +}; + +struct Uniform +{ + mat4 mvpMtx; + mat4 texMtx; +}; + +// first 15 slots are reserved for the vertex attributes +#define UNIFORM_INDEX_START 16 + +vertex RasterizerData default_vp(Vertex in [[stage_in]], + constant Uniform& u [[buffer(UNIFORM_INDEX_START)]]) +{ + RasterizerData out; + out.pos = u.mvpMtx * vec4(in.pos, 1); + out.uv = (u.texMtx * vec4(in.uv,1,1)).xy; + return out; +} + +fragment half4 default_fp(RasterizerData in [[stage_in]], + metal::texture2d tex [[texture(0)]], + metal::sampler s [[sampler(0)]]) +{ + return tex.sample(s, in.uv); +} \ No newline at end of file diff --git a/resources/main/GLSL_GL3Support.glsl b/resources/main/GLSL_GL3Support.glsl new file mode 100644 index 0000000..a8bb979 --- /dev/null +++ b/resources/main/GLSL_GL3Support.glsl @@ -0,0 +1,97 @@ +// This file is part of the OGRE project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at https://www.ogre3d.org/licensing. + +// @public-api + +#if defined(OGRE_FRAGMENT_SHADER) && defined(OGRE_GLSLES) +// define default precisions for ES fragement shaders +precision mediump float; + +#if __VERSION__ > 100 +precision lowp sampler2DArray; +precision lowp sampler2DShadow; +precision lowp sampler3D; +#endif +#endif + +#if __VERSION__ == 100 +mat2 transpose(mat2 m) +{ + return mat2(m[0][0], m[1][0], + m[0][1], m[1][1]); +} + +mat3 transpose(mat3 m) +{ + return mat3(m[0][0], m[1][0], m[2][0], + m[0][1], m[1][1], m[2][1], + m[0][2], m[1][2], m[2][2]); +} + +mat4 transpose(mat4 m) +{ + return mat4(m[0][0], m[1][0], m[2][0], m[3][0], + m[0][1], m[1][1], m[2][1], m[3][1], + m[0][2], m[1][2], m[2][2], m[3][2], + m[0][3], m[1][3], m[2][3], m[3][3]); +} +#endif + +#if __VERSION__ > 120 || defined(OGRE_GLSLANG) +#define texture1D texture +#define texture2D texture +#define texture3D texture +#define texture2DArray texture +#define textureCube texture +#define shadow2D texture +#define shadow2DProj textureProj +#define texture2DProj textureProj +#define texture2DLod textureLod +#define textureCubeLod textureLod + +#if defined(OGRE_GLSLANG) || (__VERSION__ > 150 && defined(OGRE_VERTEX_SHADER)) || __VERSION__ >= 410 +#define IN(decl, loc) layout(location = loc) in decl; +#else +#define IN(decl, loc) in decl; +#endif + +#if defined(OGRE_GLSLANG) || (__VERSION__ > 150 && defined(OGRE_FRAGMENT_SHADER)) || __VERSION__ >= 410 +#define OUT(decl, loc) layout(location = loc) out decl; +#else +#define OUT(decl, loc) out decl; +#endif + +#else + +#ifdef OGRE_VERTEX_SHADER +#define IN(decl, loc) attribute decl; +#define OUT(decl, loc) varying decl; +#else +#define IN(decl, loc) varying decl; +#define OUT(decl, loc) out decl; +#endif + +#endif + +#if defined(OGRE_FRAGMENT_SHADER) && (defined(OGRE_GLSLANG) || (__VERSION__ > 130)) +#define gl_FragColor FragColor +OUT(vec4 FragColor, 0) +#endif + +#ifdef VULKAN + +#ifdef OGRE_VERTEX_SHADER +#define OGRE_UNIFORMS_BEGIN layout(binding = 0, row_major) uniform OgreUniforms { +#else +#define OGRE_UNIFORMS_BEGIN layout(binding = 1, row_major) uniform OgreUniforms { +#endif + +#define OGRE_UNIFORMS_END }; + +#else + +#define OGRE_UNIFORMS_BEGIN +#define OGRE_UNIFORMS_END + +#endif \ No newline at end of file diff --git a/resources/main/HLSL_SM4Support.hlsl b/resources/main/HLSL_SM4Support.hlsl new file mode 100644 index 0000000..2d2e3ea --- /dev/null +++ b/resources/main/HLSL_SM4Support.hlsl @@ -0,0 +1,98 @@ +// This file is part of the OGRE project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at https://www.ogre3d.org/licensing. + +// @public-api + +#if OGRE_HLSL >= 4 + +// SM4 separates sampler into Texture and SamplerState + +#define sampler1D Sampler1D +#define sampler2D Sampler2D +#define sampler3D Sampler3D +#define samplerCUBE SamplerCube + +struct Sampler1D +{ + Texture1D t; + SamplerState s; +}; +struct Sampler2D +{ + Texture2D t; + SamplerState s; +}; +struct Sampler3D +{ + Texture3D t; + SamplerState s; +}; +struct SamplerCube +{ + TextureCube t; + SamplerState s; +}; + +float4 tex1D(Sampler1D s, float v) { return s.t.Sample(s.s, v); } +float4 tex2D(Sampler2D s, float2 v) { return s.t.Sample(s.s, v); } +float4 tex3D(Sampler3D s, float3 v) { return s.t.Sample(s.s, v); } +float4 texCUBE(SamplerCube s, float3 v) { return s.t.Sample(s.s, v); } +float4 texCUBElod(SamplerCube s, float4 v) { return s.t.SampleLevel(s.s, v.xyz, v.w); } + +float4 tex2D(Sampler2D s, float2 v, float2 ddx, float2 ddy) { return s.t.SampleGrad(s.s, v, ddx, ddy); } +float4 tex2Dproj(Sampler2D s, float4 v) { return s.t.Sample(s.s, v.xy/v.w); } +float4 tex2Dlod(Sampler2D s, float4 v) { return s.t.SampleLevel(s.s, v.xy, v.w); } + +#define SAMPLER1D(name, reg) \ + Texture1D name ## Tex : register(t ## reg);\ + SamplerState name ## State : register(s ## reg);\ + static Sampler1D name = {name ## Tex, name ## State} + +#define SAMPLER2D(name, reg) \ + Texture2D name ## Tex : register(t ## reg);\ + SamplerState name ## State : register(s ## reg);\ + static Sampler2D name = {name ## Tex, name ## State} + +#define SAMPLER3D(name, reg) \ + Texture3D name ## Tex : register(t ## reg);\ + SamplerState name ## State : register(s ## reg);\ + static Sampler3D name = {name ## Tex, name ## State} + +#define SAMPLERCUBE(name, reg) \ + TextureCube name ## Tex : register(t ## reg);\ + SamplerState name ## State : register(s ## reg);\ + static SamplerCube name = {name ## Tex, name ## State} + +// the following are not available in D3D9, but provided for convenience +struct Sampler2DShadow +{ + Texture2D t; + SamplerComparisonState s; +}; +struct Sampler2DArray +{ + Texture2DArray t; + SamplerState s; +}; + +#define SAMPLER2DSHADOW(name, reg) \ + Texture2D name ## Tex : register(t ## reg);\ + SamplerComparisonState name ## State : register(s ## reg);\ + static Sampler2DShadow name = {name ## Tex, name ## State} + +#define SAMPLER2DARRAY(name, reg) \ + Texture2DArray name ## Tex : register(t ## reg);\ + SamplerState name ## State : register(s ## reg);\ + static Sampler2DArray name = {name ## Tex, name ## State} + +float tex2Dcmp(Sampler2DShadow s, float3 v) { return s.t.SampleCmpLevelZero(s.s, v.xy, v.z); } +float4 tex2DARRAY(Sampler2DArray s, float3 v) { return s.t.Sample(s.s, v); } +#else + +#define SAMPLER1D(name, reg) sampler1D name : register(s ## reg) +#define SAMPLER2D(name, reg) sampler2D name : register(s ## reg) +#define SAMPLER3D(name, reg) sampler3D name : register(s ## reg) +#define SAMPLERCUBE(name, reg) samplerCUBE name : register(s ## reg) + +#endif \ No newline at end of file diff --git a/resources/main/OgreUnifiedShader.h b/resources/main/OgreUnifiedShader.h new file mode 100644 index 0000000..538f533 --- /dev/null +++ b/resources/main/OgreUnifiedShader.h @@ -0,0 +1,185 @@ +// This file is part of the OGRE project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at https://www.ogre3d.org/licensing. + +// greatly inspired by +// - shiny: https://ogrecave.github.io/shiny/defining-materials-shaders.html +// - bgfx: https://github.com/bkaradzic/bgfx/blob/master/src/bgfx_shader.sh + +/// general usage: +// MAIN_PARAMETERS +// IN(vec4 vertex, POSITION) +// MAIN_DECLARATION +// { +// GLSL code here +// } + +/// configuration +// use macros that will be default with Ogre 15 +// #define USE_OGRE_FROM_FUTURE + +// @public-api + +#if defined(OGRE_HLSL) || defined(OGRE_CG) +// HLSL +#include "HLSL_SM4Support.hlsl" +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define mat3 float3x3 +#define mat4 float4x4 + +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 + +#define texture1D tex1D +#define texture2D tex2D +#define texture3D tex3D +#define texture2DArray tex2DARRAY +#define textureCube texCUBE +#define shadow2D tex2Dcmp +#define texture2DProj tex2Dproj +vec4 texture2DLod(sampler2D s, vec2 v, float lod) { return tex2Dlod(s, vec4(v.x, v.y, 0, lod)); } + +#define samplerCube samplerCUBE +vec4 textureCubeLod(samplerCube s, vec3 v, float lod) { return texCUBElod(s, vec4(v.x, v.y, v.z, lod)); } + +#define sampler2DShadow Sampler2DShadow + +#define mix lerp +#define fract frac +#define inversesqrt rsqrt +#define dFdx ddx +#define dFdy ddy + +float mod(float _a, float _b) { return _a - _b * floor(_a / _b); } +vec2 mod(vec2 _a, vec2 _b) { return _a - _b * floor(_a / _b); } +vec3 mod(vec3 _a, vec3 _b) { return _a - _b * floor(_a / _b); } +vec4 mod(vec4 _a, vec4 _b) { return _a - _b * floor(_a / _b); } + +vec2 vec2_splat(float x) { return vec2(x, x); } +vec3 vec3_splat(float x) { return vec3(x, x, x); } +vec4 vec4_splat(float x) { return vec4(x, x, x, x); } + +mat4 mtxFromRows(vec4 a, vec4 b, vec4 c, vec4 d) +{ + return mat4(a, b, c, d); +} + +mat3 mtxFromRows(vec3 a, vec3 b, vec3 c) +{ + return mat3(a, b, c); +} + +mat3 mtxFromCols(vec3 a, vec3 b, vec3 c) +{ + return transpose(mat3(a, b, c)); +} + +#define STATIC static + +#define OGRE_UNIFORMS_BEGIN +#define OGRE_UNIFORMS_END + +#define MAIN_PARAMETERS void main( + +#ifdef OGRE_VERTEX_SHADER +#define MAIN_DECLARATION out float4 gl_Position : POSITION) +#else +#define MAIN_DECLARATION in float4 gl_FragCoord : POSITION, out float4 gl_FragColor : COLOR) +#endif + +#define IN(decl, sem) in decl : sem, +#define OUT(decl, sem) out decl : sem, +#elif defined(OGRE_METAL) +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define mat3 metal::float3x3 +#define mat4 metal::float4x4 + +#define IN(decl, sem) decl [[ attribute(sem) ]]; +#else +// GLSL +#include "GLSL_GL3Support.glsl" + +#ifdef VULKAN +#define _UNIFORM_BINDING(b) layout(binding = b + 2) uniform +#elif __VERSION__ >= 420 +#define _UNIFORM_BINDING(b) layout(binding = b) uniform +#else +#define _UNIFORM_BINDING(b) uniform +#endif + +#define SAMPLER1D(name, reg) _UNIFORM_BINDING(reg) sampler1D name +#define SAMPLER2D(name, reg) _UNIFORM_BINDING(reg) sampler2D name +#define SAMPLER3D(name, reg) _UNIFORM_BINDING(reg) sampler3D name +#define SAMPLER2DARRAY(name, reg) _UNIFORM_BINDING(reg) sampler2DArray name +#define SAMPLERCUBE(name, reg) _UNIFORM_BINDING(reg) samplerCube name +#define SAMPLER2DSHADOW(name, reg) _UNIFORM_BINDING(reg) sampler2DShadow name + +#define saturate(x) clamp(x, 0.0, 1.0) +#define mul(a, b) ((a) * (b)) + +#define vec2_splat vec2 +#define vec3_splat vec3 +#define vec4_splat vec4 + +mat4 mtxFromRows(vec4 a, vec4 b, vec4 c, vec4 d) +{ + return transpose(mat4(a, b, c, d)); +} + +mat3 mtxFromRows(vec3 a, vec3 b, vec3 c) +{ + return transpose(mat3(a, b, c)); +} + +mat3 mtxFromCols(vec3 a, vec3 b, vec3 c) +{ + return mat3(a, b, c); +} + +#define STATIC + +#define MAIN_PARAMETERS +#define MAIN_DECLARATION void main() + +#endif + +#if !defined(OGRE_HLSL) && !defined(OGRE_CG) +// semantics as aliases for attribute locations +#define POSITION 0 +#define BLENDWEIGHT 1 +#define NORMAL 2 +#define COLOR0 3 +#define COLOR1 4 +#define COLOR COLOR0 +#define FOG 5 +#define BLENDINDICES 7 +#define TEXCOORD0 8 +#define TEXCOORD1 9 +#define TEXCOORD2 10 +#define TEXCOORD3 11 +#define TEXCOORD4 12 +#define TEXCOORD5 13 +#define TEXCOORD6 14 +#define TEXCOORD7 15 +#define TANGENT 14 +#endif + +#define OGRE_UNIFORMS(params) OGRE_UNIFORMS_BEGIN params OGRE_UNIFORMS_END + +// GL_EXT_shader_explicit_arithmetic_types polyfill +#ifdef OGRE_GLSLES +#define float32_t highp float +#define f32vec2 highp vec2 +#define f32vec3 highp vec3 +#define f32vec4 highp vec4 +#else +#define float32_t float +#define f32vec2 vec2 +#define f32vec3 vec3 +#define f32vec4 vec4 +#endif diff --git a/resources/main/Shadow.material b/resources/main/Shadow.material new file mode 100644 index 0000000..ef14b2d --- /dev/null +++ b/resources/main/Shadow.material @@ -0,0 +1,85 @@ +// This file is part of the OGRE project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at https://www.ogre3d.org/licensing. + +material Ogre/TextureShadowCaster +{ + receive_shadows false + technique + { + pass + { + // Lighting has to be on, because we need shadow coloured objects + // Note that because we can't predict vertex programs, we'll have to + // bind light values to those, and so we bind White to ambient + // reflectance, and we'll set the ambient colour to the shadow colour + ambient 1 1 1 + diffuse 0 0 0 + specular 0 0 0 1 + emissive 0 0 0 + fog_override true none + // set depth bias in case this is used with PF_DEPTH + depth_bias -1 -1 + } + } +} + +material Ogre/StencilShadowModulationPass +{ + technique + { + pass + { + lighting off + scene_blend modulate + depth_write off + depth_check off + cull_hardware none + + vertex_program_ref Ogre/ShadowBlendVP {} + fragment_program_ref Ogre/ShadowBlendFP {} + texture_unit {} + } + } +} + +material Ogre/StencilShadowVolumes +{ + technique + { + pass + { + // program will be set dynamically to match light type + vertex_program_ref Ogre/ShadowExtrudeDirLightFinite + { + // however, the parameters here are shared between all programs + param_named_auto worldviewproj_matrix worldviewproj_matrix + param_named_auto light_position_object_space light_position_object_space 0 + param_named_auto shadow_extrusion_distance shadow_extrusion_distance 0 + } + fragment_program_ref Ogre/ShadowBlendFP {} + } + } +} + +material Ogre/Debug/ShadowVolumes +{ + technique + { + pass + { + depth_write off + scene_blend add + cull_hardware none + + // program will be set dynamically to match light type + vertex_program_ref Ogre/ShadowExtrudeDirLight + { + // however, the parameters here are shared between all programs + param_named_auto worldviewproj_matrix worldviewproj_matrix + param_named_auto light_position_object_space light_position_object_space 0 + } + fragment_program_ref Ogre/ShadowBlendFP {} + } + } +} \ No newline at end of file diff --git a/resources/main/ShadowBlend.frag b/resources/main/ShadowBlend.frag new file mode 100644 index 0000000..4b6556e --- /dev/null +++ b/resources/main/ShadowBlend.frag @@ -0,0 +1,11 @@ +#include + +OGRE_UNIFORMS( + uniform vec4 shadowColor; +) + +MAIN_PARAMETERS +MAIN_DECLARATION +{ + gl_FragColor = shadowColor; +} diff --git a/resources/main/ShadowBlend.vert b/resources/main/ShadowBlend.vert new file mode 100644 index 0000000..d052617 --- /dev/null +++ b/resources/main/ShadowBlend.vert @@ -0,0 +1,12 @@ +#include + +OGRE_UNIFORMS( + uniform mat4 worldViewProj; +) + +MAIN_PARAMETERS +IN(vec4 vertex, POSITION) +MAIN_DECLARATION +{ + gl_Position = mul(worldViewProj, vertex); +} diff --git a/resources/main/ShadowExtrudeDirLight.vert b/resources/main/ShadowExtrudeDirLight.vert new file mode 100644 index 0000000..6080986 --- /dev/null +++ b/resources/main/ShadowExtrudeDirLight.vert @@ -0,0 +1,18 @@ +#include + +// Directional light extrude +uniform mat4 worldviewproj_matrix; +uniform vec4 light_position_object_space; // homogenous, object space + +MAIN_PARAMETERS +IN(vec4 uv0, TEXCOORD0) +IN(vec4 position, POSITION) +MAIN_DECLARATION +{ + // Extrusion in object space + // Vertex unmodified if w==1, extruded if w==0 + vec4 newpos = + (uv0.xxxx * (position + light_position_object_space)) - light_position_object_space; + + gl_Position = mul(worldviewproj_matrix, newpos); +} \ No newline at end of file diff --git a/resources/main/ShadowExtrudeDirLightFinite.vert b/resources/main/ShadowExtrudeDirLightFinite.vert new file mode 100644 index 0000000..16f758c --- /dev/null +++ b/resources/main/ShadowExtrudeDirLightFinite.vert @@ -0,0 +1,22 @@ +#include + +// Directional light extrude - FINITE +uniform mat4 worldviewproj_matrix; +uniform vec4 light_position_object_space; // homogenous, object space +uniform float shadow_extrusion_distance; // how far to extrude + +MAIN_PARAMETERS +IN(vec4 uv0, TEXCOORD0) +IN(vec4 position, POSITION) +MAIN_DECLARATION +{ + // Extrusion in object space + // Vertex unmodified if w==1, extruded if w==0 + vec3 extrusionDir = - light_position_object_space.xyz; + extrusionDir = normalize(extrusionDir); + + vec4 newpos = vec4(position.xyz + + ((1.0 - uv0.x) * shadow_extrusion_distance * extrusionDir), 1.0); + + gl_Position = mul(worldviewproj_matrix, newpos); +} \ No newline at end of file diff --git a/resources/main/ShadowExtrudePointLight.vert b/resources/main/ShadowExtrudePointLight.vert new file mode 100644 index 0000000..9f4ddf4 --- /dev/null +++ b/resources/main/ShadowExtrudePointLight.vert @@ -0,0 +1,19 @@ +#include + +// Point light shadow volume extrude +uniform mat4 worldviewproj_matrix; +uniform vec4 light_position_object_space; // homogenous, object space + +MAIN_PARAMETERS +IN(vec4 uv0, TEXCOORD0) +IN(vec4 position, POSITION) +MAIN_DECLARATION +{ + // Extrusion in object space + // Vertex unmodified if w==1, extruded if w==0 + vec4 newpos = + (uv0.xxxx * light_position_object_space) + + vec4(position.xyz - light_position_object_space.xyz, 0.0); + + gl_Position = mul(worldviewproj_matrix, newpos); +} \ No newline at end of file diff --git a/resources/main/ShadowExtrudePointLightFinite.vert b/resources/main/ShadowExtrudePointLightFinite.vert new file mode 100644 index 0000000..1e7134b --- /dev/null +++ b/resources/main/ShadowExtrudePointLightFinite.vert @@ -0,0 +1,22 @@ +#include + +// Point light shadow volume extrude - FINITE +uniform mat4 worldviewproj_matrix; +uniform vec4 light_position_object_space; // homogenous, object space +uniform float shadow_extrusion_distance; // how far to extrude + +MAIN_PARAMETERS +IN(vec4 uv0, TEXCOORD0) +IN(vec4 position, POSITION) +MAIN_DECLARATION +{ + // Extrusion in object space + // Vertex unmodified if w==1, extruded if w==0 + vec3 extrusionDir = position.xyz - light_position_object_space.xyz; + extrusionDir = normalize(extrusionDir); + + vec4 newpos = vec4(position.xyz + + ((1.0 - uv0.x) * shadow_extrusion_distance * extrusionDir), 1.0); + + gl_Position = mul(worldviewproj_matrix, newpos); +} \ No newline at end of file diff --git a/resources/main/ShadowVolumeExtude.program b/resources/main/ShadowVolumeExtude.program new file mode 100644 index 0000000..75c50e3 --- /dev/null +++ b/resources/main/ShadowVolumeExtude.program @@ -0,0 +1,41 @@ +// This file is part of the OGRE project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at https://www.ogre3d.org/licensing. + +vertex_program Ogre/ShadowBlendVP glsl glsles hlsl glslang +{ + source ShadowBlend.vert + default_params + { + param_named_auto worldViewProj worldviewproj_matrix + } +} + +fragment_program Ogre/ShadowBlendFP glsl glsles hlsl glslang +{ + source ShadowBlend.frag + default_params + { + param_named_auto shadowColor shadow_colour + } +} + +vertex_program Ogre/ShadowExtrudePointLight glsl glsles hlsl +{ + source ShadowExtrudePointLight.vert +} + +vertex_program Ogre/ShadowExtrudeDirLight glsl glsles hlsl +{ + source ShadowExtrudeDirLight.vert +} + +vertex_program Ogre/ShadowExtrudePointLightFinite glsl glsles hlsl +{ + source ShadowExtrudePointLightFinite.vert +} + +vertex_program Ogre/ShadowExtrudeDirLightFinite glsl glsles hlsl +{ + source ShadowExtrudeDirLightFinite.vert +} \ No newline at end of file diff --git a/resources/main/spot_shadow_fade.dds b/resources/main/spot_shadow_fade.dds new file mode 100644 index 0000000..f672065 Binary files /dev/null and b/resources/main/spot_shadow_fade.dds differ diff --git a/resources/shaderlib/FFPLib_AlphaTest.glsl b/resources/shaderlib/FFPLib_AlphaTest.glsl new file mode 100644 index 0000000..c28f059 --- /dev/null +++ b/resources/shaderlib/FFPLib_AlphaTest.glsl @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Program Name: FFPLib_AlphaTest +// Program Desc: Alpha test function. +// Program Type: Vertex/Pixel shader +// Language: GLSL +//----------------------------------------------------------------------------- + +#define CMPF_ALWAYS_FAIL 0 +#define CMPF_ALWAYS_PASS 1 +#define CMPF_LESS 2 +#define CMPF_LESS_EQUAL 3 +#define CMPF_EQUAL 4 +#define CMPF_NOT_EQUAL 5 +#define CMPF_GREATER_EQUAL 6 +#define CMPF_GREATER 7 + +bool Alpha_Func(in int func, in float alphaRef, in float alphaValue) +{ + // ES2 does not have switch + if(func == CMPF_ALWAYS_PASS) + return true; + else if(func == CMPF_LESS) + return alphaValue < alphaRef; + else if(func == CMPF_LESS_EQUAL) + return alphaValue <= alphaRef; + else if(func == CMPF_EQUAL) + return alphaValue == alphaRef; + else if(func == CMPF_NOT_EQUAL) + return alphaValue != alphaRef; + else if(func == CMPF_GREATER_EQUAL) + return alphaValue >= alphaRef; + else if(func == CMPF_GREATER) + return alphaValue > alphaRef; + + // CMPF_ALWAYS_FAIL and default + return false; +} + + +void FFP_Alpha_Test(in float func, in float alphaRef, in vec4 texel) +{ + bool pass_ = Alpha_Func(int(func), alphaRef, texel.a); + if (!pass_) + discard; +} diff --git a/resources/shaderlib/FFPLib_Fog.glsl b/resources/shaderlib/FFPLib_Fog.glsl new file mode 100644 index 0000000..b57c324 --- /dev/null +++ b/resources/shaderlib/FFPLib_Fog.glsl @@ -0,0 +1,92 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE +(Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org + +Copyright (c) 2000-2014 Torus Knot Software Ltd +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +//----------------------------------------------------------------------------- +// Program Name: FFPLib_Fog +// Program Desc: Fog functions of the FFP. +// Program Type: Vertex/Pixel shader +// Language: GLSL +// Notes: Implements core functions needed by FFPFog class. +// Based on fog engine. +// See http://msdn.microsoft.com/en-us/library/bb173398.aspx +// Vertex based fog: the w component of the out position is used +// as the distance parameter to fog formulas. This is basically the z coordinate +// in world space. See pixel fog under D3D docs. The fog factor is computed according +// to each formula, then clamped and output to the pixel shader. +// Pixel based fog: the w component of the out position is passed to pixel shader +// that computes the fog factor based on it. +// Both techniques use the fog factor in the end of the pixel shader to blend +// the output color with the fog color. +//----------------------------------------------------------------------------- + +#define FOG_EXP 1 +#define FOG_EXP2 2 +#define FOG_LINEAR 3 + +//----------------------------------------------------------------------------- +void FFP_FogFactor(in float depth, + in vec4 fogParams, + out float oFogFactor) +{ + float distance = abs(depth); + +#if FOG_TYPE == FOG_LINEAR + float fogFactor = (fogParams.z - distance) * fogParams.w; +#elif FOG_TYPE == FOG_EXP + float x = distance*fogParams.x; + float fogFactor = 1.0 / exp(x); +#elif FOG_TYPE == FOG_EXP2 + float x = (distance*fogParams.x*distance*fogParams.x); + float fogFactor = 1.0 / exp(x); +#endif + + oFogFactor = saturate(fogFactor); +} + +//----------------------------------------------------------------------------- +void FFP_PixelFog_PositionDepth(in mat4 mWorld, + in vec3 cameraPos, + in vec4 pos, + out vec3 oPosView, + out float oDepth) +{ + vec4 vOutPos = mul(mWorld, pos); + oPosView = vOutPos.xyz - cameraPos; + oDepth = length(oPosView); +} + +//----------------------------------------------------------------------------- +void FFP_PixelFog(in float depth, + in vec4 fogParams, + in vec4 fogColor, + in vec4 baseColor, + out vec4 oColor) +{ + float fogFactor = 0.0; + FFP_FogFactor(depth, fogParams, fogFactor); + oColor = mix(fogColor, baseColor, fogFactor); +} \ No newline at end of file diff --git a/resources/shaderlib/FFPLib_Texturing.glsl b/resources/shaderlib/FFPLib_Texturing.glsl new file mode 100644 index 0000000..a77a270 --- /dev/null +++ b/resources/shaderlib/FFPLib_Texturing.glsl @@ -0,0 +1,144 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE +(Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org + +Copyright (c) 2000-2014 Torus Knot Software Ltd +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +//----------------------------------------------------------------------------- +// Program Name: FFPLib_Texturing +// Program Desc: Texture functions of the FFP. +// Program Type: Vertex/Pixel shader +// Language: GLSL +// Notes: Implements core functions for FFPTexturing class. +// based on texturing operations needed by render system. +// Implements texture coordinate processing: +// see http://msdn.microsoft.com/en-us/library/bb206247.aspx +// Implements texture blending operation: +// see http://msdn.microsoft.com/en-us/library/bb206241.aspx +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +void FFP_TransformTexCoord(in mat4 m, in vec2 v, out vec2 vOut) +{ + vOut = mul(m, vec4(v, 0.0, 1.0)).xy; +} +//----------------------------------------------------------------------------- +void FFP_TransformTexCoord(in mat4 m, in vec4 v, out vec2 vOut) +{ + vOut = mul(m, v).xy; +} + +//----------------------------------------------------------------------------- +void FFP_TransformTexCoord(in mat4 m, in vec3 v, out vec3 vOut) +{ + vOut = mul(m, vec4(v, 1.0)).xyz; +} + +//----------------------------------------------------------------------------- +void FFP_GenerateTexCoord_EnvMap_Normal(in mat3 mWorldIT, + in vec3 vNormal, + out vec3 vOut) +{ + vOut = normalize(mul(mWorldIT, vNormal)); +} + +//----------------------------------------------------------------------------- +void FFP_GenerateTexCoord_EnvMap_Sphere(in mat4 mWorldView, + in mat3 mWorldIT, + in vec4 vPos, + in vec3 vNormal, + out vec2 vOut) +{ + vec3 normal = normalize( mul(mWorldIT, vNormal)); + vec3 eyedir = normalize(mul(mWorldView, vPos)).xyz; + vec3 r = reflect(eyedir, normal); + r.z += 1.0; + float two_p = 2.0 * length(r); + vOut = vec2(0.5 + r.x / two_p, 0.5 - r.y / two_p); +} + +//----------------------------------------------------------------------------- +void FFP_GenerateTexCoord_EnvMap_Reflect(in mat4 mWorld, + in mat4 mWorldIT, + in vec3 vCamPos, + in vec3 vNormal, + in vec4 vPos, + out vec3 vOut) +{ + vec3 vWorldNormal = normalize(mul(mWorldIT, vec4(vNormal, 0.0)).xyz); + vec3 vWorldPos = mul(mWorld, vPos).xyz; + vec3 vEyeDir = normalize(vWorldPos - vCamPos); + + vec3 vReflect = reflect(vEyeDir, vWorldNormal); + vReflect.z *= -1.0; + + vOut = vReflect; +} + +//----------------------------------------------------------------------------- +void FFP_AddSmooth(in float vIn0, in float vIn1, out float vOut) +{ + vOut = vIn0 + vIn1 - (vIn0 * vIn1); +} + +//----------------------------------------------------------------------------- +void FFP_AddSmooth(in vec2 vIn0, in vec2 vIn1, out vec2 vOut) +{ + vOut = vIn0 + vIn1 - (vIn0 * vIn1); +} + +//----------------------------------------------------------------------------- +void FFP_AddSmooth(in vec3 vIn0, in vec3 vIn1, out vec3 vOut) +{ + vOut = vIn0 + vIn1 - (vIn0 * vIn1); +} + +//----------------------------------------------------------------------------- +void FFP_AddSmooth(in vec4 vIn0, in vec4 vIn1, out vec4 vOut) +{ + vOut = vIn0 + vIn1 - (vIn0 * vIn1); +} +//----------------------------------------------------------------------------- +void FFP_DotProduct(in float vIn0, in float vIn1, out float vOut) +{ + vOut = dot(vIn0, vIn1); +} + +//----------------------------------------------------------------------------- +void FFP_DotProduct(in vec2 vIn0, in vec2 vIn1, out vec2 vOut) +{ + vOut = vec2_splat(dot(vIn0, vIn1)); +} + +//----------------------------------------------------------------------------- +void FFP_DotProduct(in vec3 vIn0, in vec3 vIn1, out vec3 vOut) +{ + vOut = vec3_splat(dot(vIn0, vIn1)); +} + +//----------------------------------------------------------------------------- +void FFP_DotProduct(in vec4 vIn0, in vec4 vIn1, out vec4 vOut) +{ + vOut = vec4_splat(dot(vIn0, vIn1)); +} \ No newline at end of file diff --git a/resources/shaderlib/FFPLib_Transform.glsl b/resources/shaderlib/FFPLib_Transform.glsl new file mode 100644 index 0000000..936257c --- /dev/null +++ b/resources/shaderlib/FFPLib_Transform.glsl @@ -0,0 +1,100 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE +(Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org + +Copyright (c) 2000-2014 Torus Knot Software Ltd +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +//----------------------------------------------------------------------------- +// Program Name: FFPLib_Transform +// Program Desc: Transform functions of the FFP. +// Program Type: Vertex shader +// Language: GLSL +// Notes: Implements core functions for FFPTransform class. +// based on transform engine. +// See http://msdn.microsoft.com/en-us/library/bb206269.aspx +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +void FFP_Transform(in mat4 m, + in vec4 v, + out vec3 vOut) +{ + vOut = mul(m, v).xyz; +} + +#ifdef OGRE_HLSL +void FFP_Transform(in float3x4 m, + in float4 v, + out float3 vOut) +{ + vOut = mul(m, v); +} + +//----------------------------------------------------------------------------- +void FFP_Transform(in float3x4 m, + in float3 v, + out float3 vOut) +{ + vOut = mul((float3x3)m, v); +} +#elif !defined(OGRE_GLSLES) || OGRE_GLSLES > 100 +//----------------------------------------------------------------------------- +void FFP_Transform(in mat3x4 m, + in vec4 v, + out vec3 vOut) +{ +/* transpose non-square uniform matrix for correct row-major > column-major mapping + * to keep the indexing inside the shader so mat[0] returns the same data in both GLSL and HLSL + * although it will be the first row in HLSL and the first column in GLSL + */ + vOut = v * m; +} + +void FFP_Transform(in mat3x4 m, + in vec3 v, + out vec3 vOut) +{ + vOut = v * mat3(m); +} +#endif + +//----------------------------------------------------------------------------- +void FFP_Transform(in mat4 m, + in vec3 v, + out vec3 vOut) +{ +#ifdef OGRE_HLSL + vOut = mul((float3x3)m, v); +#else + vOut = mat3(m) * v; +#endif +} + +//----------------------------------------------------------------------------- +void FFP_DerivePointSize(in vec4 params, + in float d, + out float sz) +{ + sz = params.x/sqrt(params.y + params.z*d + params.w*d*d); +} diff --git a/resources/shaderlib/RTSLib_IBL.glsl b/resources/shaderlib/RTSLib_IBL.glsl new file mode 100644 index 0000000..f55372d --- /dev/null +++ b/resources/shaderlib/RTSLib_IBL.glsl @@ -0,0 +1,88 @@ +// This file is part of the OGRE project. +// code adapted from Google Filament +// SPDX-License-Identifier: Apache-2.0 + +vec3 specularDFG(const PixelParams pixel) { + return mix(pixel.dfg.xxx, pixel.dfg.yyy, pixel.f0); +} + +vec3 decodeDataForIBL(const vec4 data) { + return data.rgb; +} + +vec3 Irradiance_RoughnessOne(samplerCube light_iblSpecular, const vec3 n, float iblRoughnessOneLevel) { + // note: lod used is always integer, hopefully the hardware skips tri-linear filtering + return decodeDataForIBL(textureCubeLod(light_iblSpecular, n, iblRoughnessOneLevel)); +} + +vec3 PrefilteredDFG_LUT(sampler2D light_iblDFG, float lod, float NoV) { + // coord = sqrt(linear_roughness), which is the mapping used by cmgen. + // OGRE Specific: y is flipped compared to Filament code + return texture2DLod(light_iblDFG, vec2(NoV, 1.0 - lod), 0.0).rgb; +} + +float perceptualRoughnessToLod(float iblRoughnessOneLevel, float perceptualRoughness) { + // The mapping below is a quadratic fit for log2(perceptualRoughness)+iblRoughnessOneLevel when + // iblRoughnessOneLevel is 4. We found empirically that this mapping works very well for + // a 256 cubemap with 5 levels used. But also scales well for other iblRoughnessOneLevel values. + return iblRoughnessOneLevel * perceptualRoughness * (2.0 - perceptualRoughness); +} + +vec3 prefilteredRadiance(samplerCube light_iblSpecular, const vec3 r, float perceptualRoughness, float iblRoughnessOneLevel) { + float lod = perceptualRoughnessToLod(iblRoughnessOneLevel, perceptualRoughness); + return decodeDataForIBL(textureCubeLod(light_iblSpecular, r, lod)); +} + +vec3 getSpecularDominantDirection(const vec3 n, const vec3 r, float roughness) { + return mix(r, n, roughness * roughness); +} + +void evaluateIBL(inout PixelParams pixel, + in vec3 vNormal, + in vec3 viewPos, + in mat4 invViewMat, + in sampler2D dfgTex, + in samplerCube iblEnvTex, + in float iblRoughnessOneLevel, + in float iblLuminance, + inout vec3 color) +{ + vec3 shading_normal = normalize(vNormal); + vec3 shading_view = -normalize(viewPos); + float shading_NoV = clampNoV(abs(dot(shading_normal, shading_view))); + + // the above is currently duplicated with CookTorrance + + vec3 shading_reflected = reflect(-shading_view, shading_normal); + + // Pre-filtered DFG term used for image-based lighting + pixel.dfg = PrefilteredDFG_LUT(dfgTex, pixel.perceptualRoughness, shading_NoV); + + vec3 E = specularDFG(pixel); + vec3 r = getSpecularDominantDirection(shading_normal, shading_reflected, pixel.roughness); + + // OGRE specific: convert r and n back to world space for texture sampling + r = normalize(mul(invViewMat, vec4(r, 0.0)).xyz); + r.z *= -1.0; + shading_normal = normalize(mul(invViewMat, vec4(shading_normal, 0.0)).xyz); + + // specular layer + vec3 Fr = E * prefilteredRadiance(iblEnvTex, r, pixel.perceptualRoughness, iblRoughnessOneLevel); + + vec3 diffuseIrradiance = Irradiance_RoughnessOne(iblEnvTex, shading_normal, iblRoughnessOneLevel); + vec3 Fd = pixel.diffuseColor * diffuseIrradiance * (1.0 - E); + + Fr *= iblLuminance; + Fd *= iblLuminance; + + // Combine all terms + // Note: iblLuminance is already premultiplied by the exposure + + color = pow(color, vec3_splat(2.2)); // gamma to linear + + color += Fr + Fd; + + // linear to gamma + color = pow(color, vec3_splat(1.0/2.2)); + color = saturate(color); +} \ No newline at end of file diff --git a/resources/shaderlib/RTSLib_LTC.glsl b/resources/shaderlib/RTSLib_LTC.glsl new file mode 100644 index 0000000..90d5139 --- /dev/null +++ b/resources/shaderlib/RTSLib_LTC.glsl @@ -0,0 +1,114 @@ +// Real-Time Polygonal-Light Shading with Linearly Transformed Cosines +// by Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt +// code: https://github.com/selfshadow/ltc_code/ +// also: https://github.com/mrdoob/three.js/blob/master/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +// adapted for Ogre by Pavel Rojtberg + +#define LUT_SIZE 64.0 +#define LUT_SCALE ((LUT_SIZE - 1.0)/LUT_SIZE) +#define LUT_BIAS (0.5/LUT_SIZE) + +vec3 IntegrateEdgeVec(vec3 v1, vec3 v2) +{ + float x = dot(v1, v2); + float y = abs(x); + + float a = 0.8543985 + (0.4965155 + 0.0145206*y)*y; + float b = 3.4175940 + (4.1616724 + y)*y; + float v = a / b; + + float theta_sintheta = (x > 0.0) ? v : 0.5*inversesqrt(max(1.0 - x*x, 1e-7)) - v; + + return cross(v1, v2)*theta_sintheta; +} + +float LTC_Evaluate(vec3 N, vec3 V, vec3 P, mat3 Minv, vec3 points[4], sampler2D ltc_2) +{ + vec3 dir = points[0] - P; + vec3 lightDir = cross(points[1] - points[0], points[3] - points[0]); + if(dot(dir, lightDir) < 0.0) + return 0.0; + + // construct orthonormal basis around N + vec3 T1, T2; + T1 = normalize(V - N*dot(V, N)); + T2 = cross(N, T1); + + // rotate area light in (T1, T2, N) basis + Minv = mul(Minv, mtxFromRows(T1, T2, N)); + + // polygon + vec3 L[4]; + L[0] = mul(Minv, points[0] - P); + L[1] = mul(Minv, points[1] - P); + L[2] = mul(Minv, points[2] - P); + L[3] = mul(Minv, points[3] - P); + + // project rect onto sphere + L[0] = normalize(L[0]); + L[1] = normalize(L[1]); + L[2] = normalize(L[2]); + L[3] = normalize(L[3]); + + vec3 vsum = vec3_splat(0.0); + + vsum += IntegrateEdgeVec(L[0], L[1]); + vsum += IntegrateEdgeVec(L[1], L[2]); + vsum += IntegrateEdgeVec(L[2], L[3]); + vsum += IntegrateEdgeVec(L[3], L[0]); + + float len = length(vsum); + float z = vsum.z/len; + + // clipless approximation: tabulated horizon-clipped sphere + // visually better than alternatives, but produces artifacts at low roughness values + vec2 uv = vec2(z*0.5 + 0.5, len); + uv = uv*LUT_SCALE + LUT_BIAS; + + float scale = texture2D(ltc_2, uv).w; + return len*scale; +} + +void InitRectPoints(vec3 center, vec3 ex, vec3 ey, out vec3 points[4]) +{ + points[0] = center - ex - ey; + points[1] = center + ex - ey; + points[2] = center + ex + ey; + points[3] = center - ex + ey; +} + +void evaluateRectLight(sampler2D ltc_1, sampler2D ltc_2, float roughness, vec3 N, vec3 pos, vec3 lpos, vec3 halfwidth, vec3 halfheight, + inout vec3 scol, inout vec3 dcol) +{ + vec3 points[4]; + InitRectPoints(lpos, halfwidth, halfheight, points); + + vec3 V = -normalize(pos); + + float ndotv = saturate(dot(N, V)); + vec2 uv = vec2(roughness, sqrt(1.0 - ndotv)); + uv = uv*LUT_SCALE + LUT_BIAS; + + vec4 t1 = texture2D(ltc_1, uv); + + mat3 Minv = mtxFromCols( + vec3(t1.x, 0.0, t1.y), + vec3( 0.0, 1.0, 0.0), + vec3(t1.z, 0.0, t1.w) + ); + + float spec = LTC_Evaluate(N, V, pos, Minv, points, ltc_2); + + // LTC Fresnel Approximation by Stephen Hill + // http://blog.selfshadow.com/publications/s2016-advances/s2016_ltc_fresnel.pdf + vec4 t2 = texture2D(ltc_2, uv); + scol = (scol*t2.x + (1.0 - scol)*t2.y)*spec; + + mat3 Meye = mat3( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) + ); + + dcol *= LTC_Evaluate(N, V, pos, Meye, points, ltc_2); +} \ No newline at end of file diff --git a/resources/shaderlib/RTSLib_Lighting.glsl b/resources/shaderlib/RTSLib_Lighting.glsl new file mode 100644 index 0000000..ab5b9e3 --- /dev/null +++ b/resources/shaderlib/RTSLib_Lighting.glsl @@ -0,0 +1,18 @@ +// This file is part of the OGRE project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at https://www.ogre3d.org/licensing. +// SPDX-License-Identifier: MIT + +#define M_PI 3.14159265359 + +float getDistanceAttenuation(const vec3 params, float distance) +{ + return 1.0 / (params.x + params.y * distance + params.z * distance * distance); +} + +float getAngleAttenuation(const vec3 params, const vec3 lightDir, const vec3 toLight) +{ + float rho = dot(-lightDir, toLight); + float fSpotE = saturate((rho - params.y) / (params.x - params.y)); + return pow(fSpotE, params.z); +} \ No newline at end of file diff --git a/resources/shaderlib/RTSSamplers.material b/resources/shaderlib/RTSSamplers.material new file mode 100644 index 0000000..fa4ef32 --- /dev/null +++ b/resources/shaderlib/RTSSamplers.material @@ -0,0 +1,17 @@ +sampler Ogre/ShadowSampler +{ + tex_address_mode border + tex_border_colour 1 1 1 1 +} + +sampler Ogre/DepthShadowSampler : Ogre/ShadowSampler +{ + compare_test on + comp_func less_equal +} + +sampler Ogre/LtcLUTSampler +{ + filtering point linear point + tex_address_mode clamp +} \ No newline at end of file diff --git a/resources/shaderlib/SGXLib_CookTorrance.glsl b/resources/shaderlib/SGXLib_CookTorrance.glsl new file mode 100644 index 0000000..acbbe82 --- /dev/null +++ b/resources/shaderlib/SGXLib_CookTorrance.glsl @@ -0,0 +1,246 @@ +// This file is part of the OGRE project. +// code adapted from Google Filament +// SPDX-License-Identifier: Apache-2.0 + +#include "RTSLib_Lighting.glsl" + +#ifdef HAVE_AREA_LIGHTS +#include "RTSLib_LTC.glsl" +#endif + +#ifdef OGRE_GLSLES + // min roughness such that (MIN_PERCEPTUAL_ROUGHNESS^4) > 0 in fp16 (i.e. 2^(-14/4), rounded up) + #define MIN_PERCEPTUAL_ROUGHNESS 0.089 +#else + #define MIN_PERCEPTUAL_ROUGHNESS 0.045 +#endif + +#define MEDIUMP_FLT_MAX 65504.0 +#define saturateMediump(x) min(x, MEDIUMP_FLT_MAX) + +#define MIN_N_DOT_V 1e-4 + +struct PixelParams +{ + vec3 baseColor; + vec3 diffuseColor; + float perceptualRoughness; + float roughness; + vec3 f0; + vec3 dfg; + vec3 energyCompensation; +}; + +float clampNoV(float NoV) { + // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" + return max(NoV, MIN_N_DOT_V); +} + +// Computes x^5 using only multiply operations. +float pow5(float x) { + float x2 = x * x; + return x2 * x2 * x; +} + +// https://google.github.io/filament/Filament.md.html#materialsystem/diffusebrdf +float Fd_Lambert() { + return 1.0 / M_PI; +} + +// https://google.github.io/filament/Filament.md.html#materialsystem/specularbrdf/fresnel(specularf) +vec3 F_Schlick(const vec3 f0, float f90, float VoH) { + // Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering" + return f0 + (f90 - f0) * pow5(1.0 - VoH); +} + +vec3 computeDiffuseColor(const vec3 baseColor, float metallic) { + return baseColor.rgb * (1.0 - metallic); +} + +vec3 computeF0(const vec3 baseColor, float metallic, float reflectance) { + return baseColor.rgb * metallic + (reflectance * (1.0 - metallic)); +} + +float perceptualRoughnessToRoughness(float perceptualRoughness) { + return perceptualRoughness * perceptualRoughness; +} + +// https://google.github.io/filament/Filament.md.html#materialsystem/specularbrdf/geometricshadowing(specularg) +float V_SmithGGXCorrelated(float roughness, float NoV, float NoL) { + // Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs" + float a2 = roughness * roughness; + // TODO: lambdaV can be pre-computed for all the lights, it should be moved out of this function + float lambdaV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2); + float lambdaL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2); + float v = 0.5 / (lambdaV + lambdaL); + // a2=0 => v = 1 / 4*NoL*NoV => min=1/4, max=+inf + // a2=1 => v = 1 / 2*(NoL+NoV) => min=1/4, max=+inf + // clamp to the maximum value representable in mediump + return saturateMediump(v); +} + +// https://google.github.io/filament/Filament.md.html#materialsystem/specularbrdf/normaldistributionfunction(speculard) +float D_GGX(float roughness, float NoH, const vec3 h, const vec3 n) { + // Walter et al. 2007, "Microfacet Models for Refraction through Rough Surfaces" + + // In mediump, there are two problems computing 1.0 - NoH^2 + // 1) 1.0 - NoH^2 suffers floating point cancellation when NoH^2 is close to 1 (highlights) + // 2) NoH doesn't have enough precision around 1.0 + // Both problem can be fixed by computing 1-NoH^2 in highp and providing NoH in highp as well + + // However, we can do better using Lagrange's identity: + // ||a x b||^2 = ||a||^2 ||b||^2 - (a . b)^2 + // since N and H are unit vectors: ||N x H||^2 = 1.0 - NoH^2 + // This computes 1.0 - NoH^2 directly (which is close to zero in the highlights and has + // enough precision). + // Overall this yields better performance, keeping all computations in mediump +#ifdef OGRE_GLSLES + vec3 NxH = cross(n, h); + float oneMinusNoHSquared = dot(NxH, NxH); +#else + float oneMinusNoHSquared = 1.0 - NoH * NoH; +#endif + + float a = NoH * roughness; + float k = roughness / (oneMinusNoHSquared + a * a); + float d = k * k * (1.0 / M_PI); + return saturateMediump(d); +} + +vec3 evaluateLight( + in vec3 vNormal, + in vec3 viewPos, + in vec4 lightPos, + in vec3 lightColor, + in vec4 pointParams, + in vec4 vLightDirView, + in vec4 spotParams, + in PixelParams pixel) +{ + vec3 vLightView = lightPos.xyz; + float fLightD = 0.0; + + if (lightPos.w != 0.0) + { + vLightView -= viewPos; // to light + fLightD = length(vLightView); + + if(fLightD > pointParams.x) + return vec3_splat(0.0); + } + + vLightView = normalize(vLightView); + + vec3 vNormalView = normalize(vNormal); + float NoL = saturate(dot(vNormalView, vLightView)); + + if(NoL <= 0.0) + return vec3_splat(0.0); // not lit by this light + + // https://google.github.io/filament/Filament.md.html#toc5.6.2 + float f90 = saturate(dot(pixel.f0, vec3_splat(50.0 * 0.33))); + + vec3 vView = -normalize(viewPos); + + // https://google.github.io/filament/Filament.md.html#materialsystem/standardmodelsummary + vec3 h = normalize(vView + vLightView); + float NoH = saturate(dot(vNormalView, h)); + float NoV = clampNoV(abs(dot(vNormalView, vView))); + + float V = V_SmithGGXCorrelated(pixel.roughness, NoV, NoL); + vec3 F = F_Schlick(pixel.f0, f90, NoH); + float D = D_GGX(pixel.roughness, NoH, h, vNormalView); + + vec3 Fr = (D * V) * F; + vec3 Fd = pixel.diffuseColor * Fd_Lambert(); + + // https://google.github.io/filament/Filament.md.html#materialsystem/improvingthebrdfs/energylossinspecularreflectance + vec3 color = NoL * lightColor * (Fr * pixel.energyCompensation + Fd); + + color *= getDistanceAttenuation(pointParams.yzw, fLightD); + + if(spotParams.w != 0.0) + { + color *= getAngleAttenuation(spotParams.xyz, vLightDirView.xyz, vLightView); + } + + return color; +} + +void PBR_MakeParams(in vec3 baseColor, in vec2 mrParam, inout PixelParams pixel) +{ + baseColor = pow(baseColor, vec3_splat(2.2)); + pixel.baseColor = baseColor; + + float perceptualRoughness = mrParam.x; + // Clamp the roughness to a minimum value to avoid divisions by 0 during lighting + pixel.perceptualRoughness = clamp(perceptualRoughness, MIN_PERCEPTUAL_ROUGHNESS, 1.0); + // Remaps the roughness to a perceptually linear roughness (roughness^2) + pixel.roughness = perceptualRoughnessToRoughness(pixel.perceptualRoughness); + + float metallic = saturate(mrParam.y); + pixel.f0 = computeF0(baseColor, metallic, 0.04); + pixel.diffuseColor = computeDiffuseColor(baseColor, metallic); + + pixel.dfg = vec3_splat(0.5); // use full f0 for energy compensation + pixel.energyCompensation = vec3_splat(0.0); // will be set later +} + +#if LIGHT_COUNT > 0 +void PBR_Lights( +#ifdef SHADOWLIGHT_COUNT + in float shadowFactor[SHADOWLIGHT_COUNT], +#endif +#ifdef HAVE_AREA_LIGHTS + in sampler2D ltcLUT1, + in sampler2D ltcLUT2, +#endif + in vec3 vNormal, + in vec3 viewPos, + in vec4 ambient, + in vec4 lightPos[LIGHT_COUNT], + in vec4 lightColor[LIGHT_COUNT], + in vec4 pointParams[LIGHT_COUNT], + in vec4 vLightDirView[LIGHT_COUNT], + in vec4 spotParams[LIGHT_COUNT], + in PixelParams pixel, + inout vec3 vOutColour) +{ + vOutColour = pow(vOutColour, vec3_splat(2.2)); // gamma to linear + + // Energy compensation for multiple scattering in a microfacet model + // See "Multiple-Scattering Microfacet BSDFs with the Smith Model" + pixel.energyCompensation = 1.0 + pixel.f0 * (1.0 / pixel.dfg.y - 1.0); + + for(int i = 0; i < LIGHT_COUNT; i++) + { +#ifdef HAVE_AREA_LIGHTS + if(spotParams[i].w == 2.0) + { + // rect area light + vec3 dcol = pixel.diffuseColor; + vec3 scol = pixel.f0; + evaluateRectLight(ltcLUT1, ltcLUT2, pixel.roughness, normalize(vNormal), viewPos, + lightPos[i].xyz, spotParams[i].xyz, pointParams[i].xyz, scol, dcol); + vOutColour += lightColor[i].xyz * (scol + dcol) * 4.0; + continue; + } +#endif + vec3 lightVal = evaluateLight(vNormal, viewPos, lightPos[i], lightColor[i].xyz, pointParams[i], vLightDirView[i], spotParams[i], + pixel); + +#ifdef SHADOWLIGHT_COUNT + if(i < SHADOWLIGHT_COUNT) + lightVal *= shadowFactor[i]; +#endif + vOutColour += lightVal; + } + + vOutColour += pixel.baseColor * pow(ambient.rgb, vec3_splat(2.2)); + + // linear to gamma + vOutColour = pow(vOutColour, vec3_splat(1.0/2.2)); + + vOutColour = saturate(vOutColour); +} +#endif \ No newline at end of file diff --git a/resources/shaderlib/SGXLib_DualQuaternion.glsl b/resources/shaderlib/SGXLib_DualQuaternion.glsl new file mode 100644 index 0000000..e9a64a3 --- /dev/null +++ b/resources/shaderlib/SGXLib_DualQuaternion.glsl @@ -0,0 +1,172 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE +(Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org + +Copyright (c) 2000-2014 Torus Knot Software Ltd +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +//These functions are based on dqs.cg from http://isg.cs.tcd.ie/kavanl/dq/ +/* dqs.cg + + Dual quaternion skinning vertex shaders (no shading computations) + + Version 1.0.3, November 1st, 2007 + + Copyright (C) 2006-2007 University of Dublin, Trinity College, All Rights + Reserved + + This software is provided 'as-is', without any express or implied + warranty. In no event will the author(s) be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Author: Ladislav Kavan, kavanl@cs.tcd.ie + +*/ + +//----------------------------------------------------------------------------- +// Program Name: SGXLib_DualQuaternion +// Program Desc: Dual quaternion skinning functions. +// Program Type: Vertex shader +// Language: GLSL +//----------------------------------------------------------------------------- + +#if defined(OGRE_HLSL) || defined(OGRE_CG) +// this is technically wrong, thats why we dont put it into OgreUnifiedShader.h +#define mat2x4 float2x4 +#define mat3x4 float3x4 +#endif + +//----------------------------------------------------------------------------- +void SGX_BlendWeight(in float blendWgt, in mat2x4 dualQuaternion, out mat2x4 vOut) +{ + vOut = blendWgt*dualQuaternion; +} + +//----------------------------------------------------------------------------- +void SGX_BlendWeight(in float blendWgt, in mat3x4 scaleShearMatrix, out mat3x4 vOut) +{ + vOut = blendWgt*scaleShearMatrix; +} + +//----------------------------------------------------------------------------- +// Adjusts the sign of a dual quaternion depending on its orientation to the root dual quaternion +void SGX_AntipodalityAdjustment(in mat2x4 dq0, in mat2x4 dq1,out mat2x4 dq2) +{ + //Accurate antipodality handling. For speed increase, remove the following line, + //though, the results will only be valid for rotations less than 180 degrees. + dq2 = (dot(dq0[0], dq1[0]) < 0.0) ? dq1 * -1.0 : dq1; +} + +//----------------------------------------------------------------------------- +void SGX_CalculateBlendPosition(in vec3 position, in mat2x4 blendDQ, out vec4 vOut) +{ + vec3 blendPosition = position + 2.0*cross(blendDQ[0].yzw, cross(blendDQ[0].yzw, position) + blendDQ[0].x*position); + vec3 trans = 2.0*(blendDQ[0].x*blendDQ[1].yzw - blendDQ[1].x*blendDQ[0].yzw + cross(blendDQ[0].yzw, blendDQ[1].yzw)); + blendPosition += trans; + + vOut = vec4(blendPosition, 1.0); +} + +//----------------------------------------------------------------------------- +void SGX_CalculateBlendNormal(in vec3 normal, in mat2x4 blendDQ, out vec3 vOut) +{ + vOut = normal + 2.0*cross(blendDQ[0].yzw, cross(blendDQ[0].yzw, normal) + blendDQ[0].x*normal); +} + +//----------------------------------------------------------------------------- +void SGX_AdjointTransposeMatrix(in mat3x4 M, out mat3 vOut) +{ + mat3 atM; + atM[0][0] = M[2][2] * M[1][1] - M[1][2] * M[2][1]; + atM[0][1] = M[1][2] * M[2][0] - M[1][0] * M[2][2]; + atM[0][2] = M[1][0] * M[2][1] - M[2][0] * M[1][1]; + + atM[1][0] = M[0][2] * M[2][1] - M[2][2] * M[0][1]; + atM[1][1] = M[2][2] * M[0][0] - M[0][2] * M[2][0]; + atM[1][2] = M[2][0] * M[0][1] - M[0][0] * M[2][1]; + + atM[2][0] = M[1][2] * M[0][1] - M[0][2] * M[1][1]; + atM[2][1] = M[1][0] * M[0][2] - M[1][2] * M[0][0]; + atM[2][2] = M[0][0] * M[1][1] - M[1][0] * M[0][1]; + + vOut = atM; +} + +//----------------------------------------------------------------------------- +void blendBonesDQ(mat2x4 bones_dq[BONE_COUNT], vec4 indices, vec4 weights, out mat2x4 blendDQ) +{ + blendDQ = bones_dq[int(indices.x)] * weights.x; + mat2x4 dqi; +#ifdef CORRECT_ANTIPODALITY + mat2x4 dq0 = blendDQ; +#endif +#if WEIGHT_COUNT > 1 + dqi = bones_dq[int(indices.y)] * weights.y; +# ifdef CORRECT_ANTIPODALITY + SGX_AntipodalityAdjustment(dq0, dqi, dqi); +# endif + blendDQ += dqi; +#endif +#if WEIGHT_COUNT > 2 + dqi = bones_dq[int(indices.z)] * weights.z; +# ifdef CORRECT_ANTIPODALITY + SGX_AntipodalityAdjustment(dq0, dqi, dqi); +# endif + blendDQ += dqi; +#endif +#if WEIGHT_COUNT > 3 + dqi = bones_dq[int(indices.w)] * weights.w; +# ifdef CORRECT_ANTIPODALITY + SGX_AntipodalityAdjustment(dq0, dqi, dqi); +# endif + blendDQ += dqi; +#endif + + blendDQ /= length(blendDQ[0]); // normalise dual quaternion +} + +void blendBonesMat3x4(mat3x4 bones_mat[BONE_COUNT], vec4 indices, vec4 weights, out mat3x4 blendMat) +{ + blendMat = bones_mat[int(indices.x)] * weights.x; +#if WEIGHT_COUNT > 1 + blendMat += bones_mat[int(indices.y)] * weights.y; +#endif +#if WEIGHT_COUNT > 2 + blendMat += bones_mat[int(indices.z)] * weights.z; +#endif +#if WEIGHT_COUNT > 3 + blendMat += bones_mat[int(indices.w)] * weights.w; +#endif +} \ No newline at end of file diff --git a/resources/shaderlib/SGXLib_IntegratedPSSM.glsl b/resources/shaderlib/SGXLib_IntegratedPSSM.glsl new file mode 100644 index 0000000..ecd7612 --- /dev/null +++ b/resources/shaderlib/SGXLib_IntegratedPSSM.glsl @@ -0,0 +1,183 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE +(Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org + +Copyright (c) 2000-2014 Torus Knot Software Ltd +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ +//----------------------------------------------------------------------------- +// Program Name: SGXLib_IntegratedPSSM +// Program Desc: Integrated PSSM functions. +// Program Type: Vertex/Pixel shader +// Language: GLSL +//----------------------------------------------------------------------------- + +#ifdef PSSM_SAMPLE_CMP +#define SAMPLER_TYPE sampler2DShadow +#else +#define SAMPLER_TYPE sampler2D +#endif + +// default to 2x2 PCF +#ifndef PCF_XSAMPLES +#define PCF_XSAMPLES 2.0 +#endif + +//----------------------------------------------------------------------------- +#ifdef SHADOWLIGHT_COUNT +void SGX_ApplyShadowFactor_Modulative(in vec4 ambient, + in float fShadowFactor[SHADOWLIGHT_COUNT], + inout vec4 diffCol +#ifdef USE_SPECULAR + , inout vec4 specCol +#endif + ) +{ + float shadowFactor = fShadowFactor[0]; + for(int i = 1; i < SHADOWLIGHT_COUNT; ++i) + shadowFactor *= fShadowFactor[i]; + + diffCol.rgb = mix(ambient.rgb, diffCol.rgb, shadowFactor); + +#ifdef USE_SPECULAR + specCol.rgb *= shadowFactor; +#endif +} +#endif + +float sampleDepth(in SAMPLER_TYPE shadowMap, vec2 uv, float depth) +{ +#ifdef PSSM_SAMPLE_CMP +# if defined(OGRE_GLSL) && OGRE_GLSL < 130 + return shadow2D(shadowMap, vec3(uv, depth)).r; +# else + return shadow2D(shadowMap, vec3(uv, depth)); +# endif +#else + return (depth <= texture2D(shadowMap, uv).r) ? 1.0 : 0.0; +#endif +} + +//----------------------------------------------------------------------------- +#ifdef PSSM_SAMPLE_COLOUR +void SGX_ShadowPCF4(in sampler2D shadowMap, in vec4 shadowMapPos, in vec2 invTexSize, out float c) +{ + c = texture2DProj(shadowMap, shadowMapPos).x; +} +#else +void SGX_ShadowPCF4(in SAMPLER_TYPE shadowMap, in vec4 shadowMapPos, in vec2 invTexSize, out float c) +{ + shadowMapPos = shadowMapPos / shadowMapPos.w; +#if !defined(OGRE_REVERSED_Z) && !defined(OGRE_HLSL) && !defined(VULKAN) + shadowMapPos.z = shadowMapPos.z * 0.5 + 0.5; // convert -1..1 to 0..1 +#endif + vec2 uv = shadowMapPos.xy; + + // depth must be clamped to support floating-point depth formats. This is to avoid comparing a + // value from the depth texture (which is never greater than 1.0) with a greater-than-one + // comparison value (which is possible with floating-point formats). + float depth = clamp(shadowMapPos.z, 0.0, 1.0); + + c = 0.0; + float scale = 1.0; + float offset = (PCF_XSAMPLES / 2.0 - 0.5) * scale; + for (float y = -offset; y <= offset; y += scale) + for (float x = -offset; x <= offset; x += scale) + c += sampleDepth(shadowMap, uv + invTexSize * vec2(x, y), depth); + + c /= PCF_XSAMPLES * PCF_XSAMPLES; +} +#endif + +//----------------------------------------------------------------------------- +void SGX_ComputeShadowFactor_PSSM3(in float fDepth, + in vec4 vSplitPoints, + in vec4 lightPosition0, + in SAMPLER_TYPE shadowMap0, + in vec2 invShadowMapSize0, + #if PSSM_NUM_SPLITS > 2 + in vec4 lightPosition1, + in SAMPLER_TYPE shadowMap1, + in vec2 invShadowMapSize1, + #endif + #if PSSM_NUM_SPLITS > 3 + in vec4 lightPosition2, + in SAMPLER_TYPE shadowMap2, + in vec2 invShadowMapSize2, + #endif + in vec4 lightPosition3, + in SAMPLER_TYPE shadowMap3, + in vec2 invShadowMapSize3, + out float oShadowFactor + #ifdef DEBUG_PSSM + , out vec4 oDiffuse + #endif + ) +{ +#if !defined(OGRE_REVERSED_Z) && !defined(OGRE_HLSL) && !defined(VULKAN) + vSplitPoints = vSplitPoints * 0.5 + 0.5; // convert -1..1 to 0..1 +#endif + +#ifdef OGRE_REVERSED_Z + vSplitPoints = vec4_splat(1.0) - vSplitPoints; + fDepth = 1.0 - fDepth; +#endif + + if (fDepth <= vSplitPoints.x) + { + SGX_ShadowPCF4(shadowMap0, lightPosition0, invShadowMapSize0, oShadowFactor); +#ifdef DEBUG_PSSM + oDiffuse.r += 1.0; +#endif + } +#if PSSM_NUM_SPLITS > 2 + else if (fDepth <= vSplitPoints.y) + { + SGX_ShadowPCF4(shadowMap1, lightPosition1, invShadowMapSize1, oShadowFactor); +#ifdef DEBUG_PSSM + oDiffuse.g += 1.0; +#endif + } +#endif +#if PSSM_NUM_SPLITS > 3 + else if (fDepth <= vSplitPoints.z) + { + SGX_ShadowPCF4(shadowMap2, lightPosition2, invShadowMapSize2, oShadowFactor); +#ifdef DEBUG_PSSM + oDiffuse.r += 1.0; + oDiffuse.g += 1.0; +#endif + } +#endif + else if (fDepth <= vSplitPoints.w) + { + SGX_ShadowPCF4(shadowMap3, lightPosition3, invShadowMapSize3, oShadowFactor); +#ifdef DEBUG_PSSM + oDiffuse.b += 1.0; +#endif + } + else + { + // behind far distance + oShadowFactor = 1.0; + } +} diff --git a/resources/shaderlib/SGXLib_LayeredBlending.glsl b/resources/shaderlib/SGXLib_LayeredBlending.glsl new file mode 100644 index 0000000..8af709a --- /dev/null +++ b/resources/shaderlib/SGXLib_LayeredBlending.glsl @@ -0,0 +1,825 @@ +/* +** layered blending & misc math +** Blending modes, RGB/HSL/Contrast/Desaturate, levels control +** +** The shaders below are base on the shaders created by: +** Romain Dura | Romz +** Blog: http://blog.mouaif.org +** Post: http://blog.mouaif.org/?p=94 +*/ + + +/* +** Desaturation +*/ + +vec4 Desaturate(in vec3 color, in float Desaturation) +{ + vec3 grayXfer = vec3(0.3, 0.59, 0.11); + float grayf = dot(grayXfer, color); + vec3 gray = vec3(grayf, grayf, grayf); + return vec4(mix(color, gray, Desaturation), 1.0); +} + + +/* +** Hue, saturation, luminance +*/ + +vec3 RGBToHSL(in vec3 color) +{ + vec3 hsl; // init to 0 to avoid warnings ? (and reverse if + remove first part) + + float fmin = min(min(color.r, color.g), color.b); //Min. value of RGB + float fmax = max(max(color.r, color.g), color.b); //Max. value of RGB + float delta = fmax - fmin; //Delta RGB value + + hsl.z = (fmax + fmin) / 2.0; // Luminance + + if (delta == 0.0) //This is a gray, no chroma... + { + hsl.x = 0.0; // Hue + hsl.y = 0.0; // Saturation + } + else //Chromatic data... + { + if (hsl.z < 0.5) + hsl.y = delta / (fmax + fmin); // Saturation + else + hsl.y = delta / (2.0 - fmax - fmin); // Saturation + + float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta; + float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta; + float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta; + + if (color.r == fmax ) + hsl.x = deltaB - deltaG; // Hue + else if (color.g == fmax) + hsl.x = (1.0 / 3.0) + deltaR - deltaB; // Hue + else if (color.b == fmax) + hsl.x = (2.0 / 3.0) + deltaG - deltaR; // Hue + + if (hsl.x < 0.0) + hsl.x += 1.0; // Hue + else if (hsl.x > 1.0) + hsl.x -= 1.0; // Hue + } + + return hsl; +} + +float HueToRGB(in float f1, in float f2, in float hue) +{ + if (hue < 0.0) + hue += 1.0; + else if (hue > 1.0) + hue -= 1.0; + float res; + if ((6.0 * hue) < 1.0) + res = f1 + (f2 - f1) * 6.0 * hue; + else if ((2.0 * hue) < 1.0) + res = f2; + else if ((3.0 * hue) < 2.0) + res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0; + else + res = f1; + return res; +} + +vec3 HSLToRGB(in vec3 hsl) +{ + vec3 rgb; + + if (hsl.y == 0.0) + rgb = hsl.zzz; // Luminance + else + { + float f2; + + if (hsl.z < 0.5) + f2 = hsl.z * (1.0 + hsl.y); + else + f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z); + + float f1 = 2.0 * hsl.z - f2; + + rgb.r = HueToRGB(f1, f2, hsl.x + (1.0/3.0)); + rgb.g = HueToRGB(f1, f2, hsl.x); + rgb.b = HueToRGB(f1, f2, hsl.x - (1.0/3.0)); + } + + return rgb; +} + + +/* +** Contrast, saturation, brightness +** Code of this function is from TGM's shader pack +** http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=21057 +*/ + +// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150% +vec3 ContrastSaturationBrightness(in vec3 color, in float brt, in float sat, in float con) +{ + // Increase or decrease these values to adjust r, g and b color channels separately + const float AvgLumR = 0.5; + const float AvgLumG = 0.5; + const float AvgLumB = 0.5; + + const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721); + + vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB); + vec3 brtColor = color * brt; + float intensityf = dot(brtColor, LumCoeff); + vec3 intensity = vec3(intensityf, intensityf, intensityf); + vec3 satColor = mix(intensity, brtColor, sat); + vec3 conColor = mix(AvgLumin, satColor, con); + return conColor; +} + +/* +** Float blending modes +** Adapted from here: http://www.nathanm.com/photoshop-blending-math/ +** But I modified the HardMix (wrong condition), Overlay, SoftLight, ColorDodge, ColorBurn, VividLight, PinLight (inverted layers) ones to have correct results +*/ + +#define BlendLinearDodgef BlendAddf +#define BlendLinearBurnf BlendSubtractf +#define BlendAddf(base, blend) min(base + blend, 1.0) +#define BlendSubtractf(base, blend) max(base + blend - 1.0, 0.0) +#define BlendLightenf(base, blend) max(blend, base) +#define BlendDarkenf(base, blend) min(blend, base) +#define BlendScreenf(base, blend) (1.0 - ((1.0 - base) * (1.0 - blend))) +#define BlendOverlayf(base, blend) (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))) +#define BlendSoftLightf(base, blend) ((blend < 0.5) ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend))) +#define BlendColorDodgef(base, blend) ((blend == 1.0) ? blend : min(base / (1.0 - blend), 1.0)) +#define BlendColorBurnf(base, blend) ((blend == 0.0) ? blend : max((1.0 - ((1.0 - base) / blend)), 0.0)) +#define BlendHardMixf(base, blend) ((BlendVividLightf(base, blend) < 0.5) ? 0.0 : 1.0) + + + +/* +** Vector3 blending modes +*/ + +// Component wise blending +#define Blend1(base, blend, funcf) funcf(base, blend) +#define Blend3(base, blend, funcf) vec3(funcf(base.r, blend.r), funcf(base.g, blend.g), funcf(base.b, blend.b)) +#define Blend4(base, blend, funcf) vec4(funcf(base.r, blend.r), funcf(base.g, blend.g), funcf(base.b, blend.b), funcf(base.a, blend.a)) + +#define BlendNormal(base, blend) (base) +#define BlendMultiply(base, blend) (base * blend) +#define BlendAverage(base, blend) ((base + blend) / 2.0) +#define BlendAdd(base, blend) min(base + blend, 1.0) +#define BlendSubtract(base, blend) max(base + blend - 1.0, 0.0) +#define BlendDifference(base, blend) abs(base - blend) +#define BlendNegation(base, blend) (1.0 - abs(1.0 - base - blend)) +#define BlendExclusion(base, blend) (base + blend - 2.0 * base * blend) +#define BlendPhoenix(base, blend) (min(base, blend) - max(base, blend) + 1.0) +#define BlendOpacity(base, blend, F, O) (F(base, blend) * O + blend * (1.0 - O)) + +// Hue Blend mode creates the result color by combining the luminance and saturation of the base color with the hue of the blend color. +float BlendHue1(in float base, in float blend) +{ + return base; +} + +vec3 BlendHue3(in vec3 base, in vec3 blend) +{ + vec3 baseHSL = RGBToHSL(base); + return HSLToRGB(vec3(RGBToHSL(blend).r, baseHSL.g, baseHSL.b)); +} + +vec4 BlendHue4(in vec4 base, in vec4 blend) +{ + vec3 hue = BlendHue3(base.xyz, blend.xyz); + return vec4(hue.x, hue.y, hue.z, BlendHue1(base.w, blend.w)); +} + +// Saturation Blend mode creates the result color by combining the luminance and hue of the base color with the saturation of the blend color. +float BlendSaturation1(in float base, in float blend) +{ + return base; +} + +vec3 BlendSaturation3(in vec3 base, in vec3 blend) +{ + vec3 baseHSL = RGBToHSL(base); + return HSLToRGB(vec3(baseHSL.r, RGBToHSL(blend).g, baseHSL.b)); +} + +vec4 BlendSaturation4(in vec4 base, in vec4 blend) +{ + vec3 hue = BlendSaturation3(base.xyz, blend.xyz); + return vec4(hue.x, hue.y, hue.z, BlendSaturation1(base.w, blend.w)); +} + +// Color Mode keeps the brightness of the base color and applies both the hue and saturation of the blend color. +float BlendColor1(in float base, in float blend) +{ + return base; +} + +vec3 BlendColor3(in vec3 base, in vec3 blend) +{ + vec3 blendHSL = RGBToHSL(blend); + return HSLToRGB(vec3(blendHSL.r, blendHSL.g, RGBToHSL(base).b)); +} + +vec4 BlendColor4(in vec4 base, in vec4 blend) +{ + vec3 hue = BlendColor3(base.xyz, blend.xyz); + return vec4(hue.x, hue.y, hue.z, BlendColor1(base.w, blend.w)); +} + + +// Luminosity Blend mode creates the result color by combining the hue and saturation of the base color with the luminance of the blend color. +float BlendLuminosity1(in float base, in float blend) +{ + return base; +} + +vec3 BlendLuminosity3(in vec3 base, in vec3 blend) +{ + vec3 baseHSL = RGBToHSL(base); + return HSLToRGB(vec3(baseHSL.r, baseHSL.g, RGBToHSL(blend).b)); +} + +vec4 BlendLuminosity4(in vec4 base, in vec4 blend) +{ + vec3 hue = BlendLuminosity3(base.xyz, blend.xyz); + return vec4(hue.x, hue.y, hue.z, BlendLuminosity1(base.w, blend.w)); +} + +float BlendLinearLightf(in float s1, in float s2) +{ + float oColor; + + if (s2 < 0.5) + { + float s2x = (2.0 * s2); + oColor = BlendSubtractf(s1, s2x); + } + else + { + float s2x = (2.0 * (s2 - 0.5)); + oColor = BlendAddf(s1, s2x); + } + + return oColor; +} + +float BlendVividLightf(in float s1, in float s2) +{ + float oColor; + + if (s2 < 0.5) + { + float s2x = (2.0 * s2); + oColor = BlendColorBurnf(s1, s2x); + } + else + { + float s2x = (2.0 * (s2 - 0.5)); + oColor = BlendColorDodgef(s1, s2x); + } + + return oColor; +} + +float BlendPinLightf(in float s1, in float s2) +{ + float oColor; + + if (s2 < 0.5) + { + float s2x = (2.0 * s2); + oColor = BlendDarkenf(s1, s2x); + } + else + { + float s2x = (2.0 * (s2 - 0.5)); + oColor = BlendLightenf(s1, s2x); + } + + return oColor; +} + +float BlendReflectf(in float s1, in float s2) +{ + float oColor; + + if (s2 == 1.0) + { + oColor = s2; + } + else + { + float s1x = (s1 * s1) / (1.0 - s2); + + oColor = min(s1x, 1.0); + } + + return oColor; +} + +//------------------------------------ +// Interface for RTShader +//------------------------------------ + + +void SGX_blend_normal(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendNormal(basePixel, blendPixel); +} + +void SGX_blend_normal(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendNormal(basePixel, blendPixel); +} + +void SGX_blend_normal(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendNormal(basePixel, blendPixel); +} + + +void SGX_blend_lighten(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendLightenf(basePixel, blendPixel); +} + +void SGX_blend_lighten(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendLightenf(basePixel, blendPixel); +} + +void SGX_blend_lighten(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendLightenf(basePixel, blendPixel); +} + + +void SGX_blend_darken(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendDarkenf(basePixel, blendPixel); +} + +void SGX_blend_darken(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendDarkenf(basePixel, blendPixel); +} + +void SGX_blend_darken(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendDarkenf(basePixel, blendPixel); +} + + +void SGX_blend_multiply(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendMultiply(basePixel, blendPixel); +} + +void SGX_blend_multiply(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendMultiply(basePixel, blendPixel); +} + +void SGX_blend_multiply(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendMultiply(basePixel, blendPixel); +} + + +void SGX_blend_average(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendAverage(basePixel, blendPixel); +} + +void SGX_blend_average(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendAverage(basePixel, blendPixel); +} + +void SGX_blend_average(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendAverage(basePixel, blendPixel); +} + + +void SGX_blend_add(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendAdd(basePixel, blendPixel); +} + +void SGX_blend_add(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendAdd(basePixel, blendPixel); +} + +void SGX_blend_add(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendAdd(basePixel, blendPixel); +} + + +void SGX_blend_subtract(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendSubtract(basePixel, blendPixel); +} + +void SGX_blend_subtract(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendSubtract(basePixel, blendPixel); +} + +void SGX_blend_subtract(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendSubtract(basePixel, blendPixel); +} + + +void SGX_blend_difference(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendDifference(basePixel, blendPixel); +} +void SGX_blend_difference(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendDifference(basePixel, blendPixel); +} +void SGX_blend_difference(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendDifference(basePixel, blendPixel); +} + + +void SGX_blend_negation(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendNegation(basePixel, blendPixel); +} +void SGX_blend_negation(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendNegation(basePixel, blendPixel); +} +void SGX_blend_negation(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendNegation(basePixel, blendPixel); +} + + +void SGX_blend_exclusion(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendExclusion(basePixel, blendPixel); +} +void SGX_blend_exclusion(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendExclusion(basePixel, blendPixel); +} +void SGX_blend_exclusion(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendExclusion(basePixel, blendPixel); +} + + +void SGX_blend_screen(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendScreenf(s1.r, s2.r), + BlendScreenf(s1.g, s2.g), + BlendScreenf(s1.b, s2.b), + BlendScreenf(s1.a, s2.a)); +} +void SGX_blend_screen(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendScreenf(s1.r, s2.r), + BlendScreenf(s1.g, s2.g), + BlendScreenf(s1.b, s2.b)); +} +void SGX_blend_screen(in float s1, in float s2, out float oColor) +{ + oColor = BlendScreenf(s1, s2); +} + + +void SGX_blend_overlay(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendOverlayf(s1.r, s2.r), + BlendOverlayf(s1.g, s2.g), + BlendOverlayf(s1.b, s2.b), + BlendOverlayf(s1.a, s2.a)); +} +void SGX_blend_overlay(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendOverlayf(s1.r, s2.r), + BlendOverlayf(s1.g, s2.g), + BlendOverlayf(s1.b, s2.b)); +} +void SGX_blend_overlay(in float s1, in float s2, out float oColor) +{ + oColor = BlendOverlayf(s1, s2); +} + + +void SGX_blend_softLight(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendSoftLightf(s1.r, s2.r), + BlendSoftLightf(s1.g, s2.g), + BlendSoftLightf(s1.b, s2.b), + BlendSoftLightf(s1.a, s2.a)); +} +void SGX_blend_softLight(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendSoftLightf(s1.r, s2.r), + BlendSoftLightf(s1.g, s2.g), + BlendSoftLightf(s1.b, s2.b)); +} +void SGX_blend_softLight(in float s1, in float s2, out float oColor) +{ + oColor = BlendSoftLightf(s1, s2); +} + + +void SGX_blend_hardLight(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendOverlayf(s1.r, s2.r), + BlendOverlayf(s1.g, s2.g), + BlendOverlayf(s1.b, s2.b), + BlendOverlayf(s1.a, s2.a)); +} +void SGX_blend_hardLight(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendOverlayf(s1.r, s2.r), + BlendOverlayf(s1.g, s2.g), + BlendOverlayf(s1.b, s2.b)); +} +void SGX_blend_hardLight(in float s1, in float s2, out float oColor) +{ + oColor = BlendOverlayf(s1, s2); +} + + +void SGX_blend_colorDodge(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendColorDodgef(s1.r, s2.r), + BlendColorDodgef(s1.g, s2.g), + BlendColorDodgef(s1.b, s2.b), + BlendColorDodgef(s1.a, s2.a)); +} +void SGX_blend_colorDodge(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendColorDodgef(s1.r, s2.r), + BlendColorDodgef(s1.g, s2.g), + BlendColorDodgef(s1.b, s2.b)); +} +void SGX_blend_colorDodge(in float s1, in float s2, out float oColor) +{ + oColor = BlendColorDodgef(s1, s2); +} + + +void SGX_blend_colorBurn(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendColorBurnf(s1.r, s2.r), + BlendColorBurnf(s1.g, s2.g), + BlendColorBurnf(s1.b, s2.b), + BlendColorBurnf(s1.a, s2.a)); +} +void SGX_blend_colorBurn(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendColorBurnf(s1.r, s2.r), + BlendColorBurnf(s1.g, s2.g), + BlendColorBurnf(s1.b, s2.b)); +} +void SGX_blend_colorBurn(in float s1, in float s2, out float oColor) +{ + oColor = BlendColorBurnf(s1, s2); +} + + +void SGX_blend_linearDodge(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendAddf(basePixel, blendPixel); +} +void SGX_blend_linearDodge(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendAddf(basePixel, blendPixel); +} +void SGX_blend_linearDodge(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendAddf(basePixel, blendPixel); +} + + +void SGX_blend_linearBurn(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendSubtractf(basePixel, blendPixel); +} +void SGX_blend_linearBurn(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendSubtractf(basePixel, blendPixel); +} +void SGX_blend_linearBurn(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendSubtractf(basePixel, blendPixel); +} + + +void SGX_blend_linearLight(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendLinearLightf(s1.r, s2.r), + BlendLinearLightf(s1.g, s2.g), + BlendLinearLightf(s1.b, s2.b), + BlendLinearLightf(s1.a, s2.a)); +} +void SGX_blend_linearLight(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendLinearLightf(s1.r, s2.r), + BlendLinearLightf(s1.g, s2.g), + BlendLinearLightf(s1.b, s2.b)); +} +void SGX_blend_linearLight(in float s1, in float s2, out float oColor) +{ + oColor = BlendLinearLightf(s1, s2); +} + + +void SGX_blend_vividLight(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendVividLightf(s1.r, s2.r), + BlendVividLightf(s1.g, s2.g), + BlendVividLightf(s1.b, s2.b), + BlendVividLightf(s1.a, s2.a)); +} +void SGX_blend_vividLight(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendVividLightf(s1.r, s2.r), + BlendVividLightf(s1.g, s2.g), + BlendVividLightf(s1.b, s2.b)); +} +void SGX_blend_vividLight(in float s1, in float s2, out float oColor) +{ + oColor = BlendVividLightf(s1, s2); +} + + +void SGX_blend_pinLight(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendPinLightf(s1.r, s2.r), + BlendPinLightf(s1.g, s2.g), + BlendPinLightf(s1.b, s2.b), + BlendPinLightf(s1.a, s2.a)); +} +void SGX_blend_pinLight(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendPinLightf(s1.r, s2.r), + BlendPinLightf(s1.g, s2.g), + BlendPinLightf(s1.b, s2.b)); +} +void SGX_blend_pinLight(in float s1, in float s2, out float oColor) +{ + oColor = BlendPinLightf(s1, s2); +} + + +void SGX_blend_hardMix(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendHardMixf(s1.r, s2.r), + BlendHardMixf(s1.g, s2.g), + BlendHardMixf(s1.b, s2.b), + BlendHardMixf(s1.a, s2.a)); +} +void SGX_blend_hardMix(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendHardMixf(s1.r, s2.r), + BlendHardMixf(s1.g, s2.g), + BlendHardMixf(s1.b, s2.b)); +} +void SGX_blend_hardMix(in float s1, in float s2, out float oColor) +{ + oColor = BlendHardMixf(s1, s2); +} + +void SGX_blend_reflect(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendReflectf(s1.r, s2.r), + BlendReflectf(s1.g, s2.g), + BlendReflectf(s1.b, s2.b), + BlendReflectf(s1.a, s2.a)); +} +void SGX_blend_reflect(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendReflectf(s1.r, s2.r), + BlendReflectf(s1.g, s2.g), + BlendReflectf(s1.b, s2.b)); +} +void SGX_blend_reflect(in float s1, in float s2, out float oColor) +{ + oColor = BlendReflectf(s1, s2); +} + + +void SGX_blend_glow(in vec4 s1, in vec4 s2, out vec4 oColor) +{ + oColor = vec4(BlendReflectf(s1.r, s2.r), + BlendReflectf(s1.g, s2.g), + BlendReflectf(s1.b, s2.b), + BlendReflectf(s1.a, s2.a)); +} +void SGX_blend_glow(in vec3 s1, in vec3 s2, out vec3 oColor) +{ + oColor = vec3(BlendReflectf(s1.r, s2.r), + BlendReflectf(s1.g, s2.g), + BlendReflectf(s1.b, s2.b)); +} +void SGX_blend_glow(in float s1, in float s2, out float oColor) +{ + oColor = BlendReflectf(s1, s2); +} + + +void SGX_blend_phoenix(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendPhoenix(basePixel, blendPixel); +} +void SGX_blend_phoenix(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendPhoenix(basePixel, blendPixel); +} +void SGX_blend_phoenix(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendPhoenix(basePixel, blendPixel); +} + + +void SGX_blend_saturation(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendSaturation4(basePixel, blendPixel); +} +void SGX_blend_saturation(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendSaturation3(basePixel, blendPixel); +} +void SGX_blend_saturation(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendSaturation1(basePixel, blendPixel); +} + + +void SGX_blend_color(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendColor4(basePixel, blendPixel); +} +void SGX_blend_color(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendColor3(basePixel, blendPixel); +} +void SGX_blend_color(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendColor1(basePixel, blendPixel); +} + + +void SGX_blend_luminosity(in vec4 basePixel, in vec4 blendPixel, out vec4 oColor) +{ + oColor = BlendLuminosity4(basePixel, blendPixel); +} +void SGX_blend_luminosity(in vec3 basePixel, in vec3 blendPixel, out vec3 oColor) +{ + oColor = BlendLuminosity3(basePixel, blendPixel); +} +void SGX_blend_luminosity(in float basePixel, in float blendPixel, out float oColor) +{ + oColor = BlendLuminosity1(basePixel, blendPixel); +} + + +//////////////////////////////////////////////////////////////////////////////////// +/// Source modification functions +//////////////////////////////////////////////////////////////////////////////////// + + +void SGX_src_mod_modulate(in vec4 iColor, in vec4 controlVal, out vec4 oColor) +{ + oColor = iColor * controlVal; +} +void SGX_src_mod_modulate(in vec3 iColor, in vec3 controlVal, out vec3 oColor) +{ + oColor = iColor * controlVal; +} +void SGX_src_mod_modulate(in float iColor, in float controlVal, out float oColor) +{ + oColor = iColor * controlVal; +} + +void SGX_src_mod_inv_modulate(in vec4 iColor, in vec4 controlVal, out vec4 oColor) +{ + oColor = mix(iColor, vec4(1.0,1.0,1.0,1.0), controlVal); +} +void SGX_src_mod_inv_modulate(in vec3 iColor, in vec3 controlVal, out vec3 oColor) +{ + oColor = mix(iColor, vec3(1.0,1.0,1.0), controlVal); +} +void SGX_src_mod_inv_modulate(in float iColor, in float controlVal, out float oColor) +{ + oColor = mix(iColor, 1.0, controlVal); +} diff --git a/resources/shaderlib/SGXLib_NormalMap.glsl b/resources/shaderlib/SGXLib_NormalMap.glsl new file mode 100644 index 0000000..fb7555a --- /dev/null +++ b/resources/shaderlib/SGXLib_NormalMap.glsl @@ -0,0 +1,125 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE +(Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org + +Copyright (c) 2000-2014 Torus Knot Software Ltd +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +//----------------------------------------------------------------------------- +// Program Name: SGXLib_NormalMapLighting +// Program Desc: Normal map lighting functions. +// Program Type: Vertex/Pixel shader +// Language: GLSL +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +void SGX_FetchNormal(in sampler2D s, + in vec2 uv, + out vec3 vOut) +{ + vOut = 2.0 * texture2D(s, uv).xyz - 1.0; +} + +//----------------------------------------------------------------------------- +void SGX_CalculateTBN(in vec3 vNormal, + in vec4 vTangent, + out mat3 TBN) +{ + vec3 vBinormal = cross(vNormal, vTangent.xyz) * vTangent.w; + + // direction: from tangent space to world + TBN = mtxFromCols(vTangent.xyz, vBinormal, vNormal); +} + +//----------------------------------------------------------------------------- +void SGX_Generate_Parallax_Texcoord(in sampler2D normalHeightMap, + in vec2 texCoord, + in vec3 viewPos, + in float heightScale, + in mat3 TBN, + out vec2 newTexCoord) +{ + //Calculate eye direction + vec3 eyeVec = mul(-viewPos, TBN); + eyeVec = normalize(eyeVec); +#ifndef TERRAIN_PARALLAX_MAPPING + eyeVec.y = -eyeVec.y; //Inverse y +#endif + + newTexCoord = texCoord; + +#ifndef POM_LAYER_COUNT + //Simple parallax mapping + float height = 1.0f - texture2D(normalHeightMap, newTexCoord).a; + + #ifndef TERRAIN_PARALLAX_MAPPING + vec2 p = eyeVec.xy / eyeVec.z * (height * heightScale); + #else + vec2 p = eyeVec.xy * (height * heightScale); + #endif + + newTexCoord = newTexCoord - p; +#else + // parallax occlusion mapping +#ifdef POM_MAX_DISTANCE + if (abs(viewPos.z) > POM_MAX_DISTANCE) + return; +#endif + + //Configure steep mapping layering. + float layerDepth = 1.0 / float(POM_LAYER_COUNT); + float currentLayerDepth = 0.0; + vec2 parallaxShift = (eyeVec.xy) * heightScale; + vec2 deltaTexCoords = parallaxShift / float(POM_LAYER_COUNT); + + float currentDepthMapValue = 1.0f - texture2D(normalHeightMap, newTexCoord).a; + + //Loop through layers and break early if match found. + for (int currentLayerId = 0; currentLayerId < POM_LAYER_COUNT; currentLayerId++) + { + // shift texture coordinates along direction of P + newTexCoord -= deltaTexCoords; + + // get depthmap value at current texture coordinates + currentDepthMapValue = 1.0f - texture2D(normalHeightMap, newTexCoord).a; + + //Break if layer height matched + if (currentLayerDepth > currentDepthMapValue) + break; + + // get depth of next layer + currentLayerDepth += layerDepth; + } + + // get texture coordinates before collision (reverse operations) + vec2 prevTexCoords = newTexCoord + deltaTexCoords; + + // get depth after and before collision for linear interpolation + float afterDepth = currentDepthMapValue - currentLayerDepth; + float beforeDepth = (1.0f - texture2D(normalHeightMap, prevTexCoords).a) - currentLayerDepth + layerDepth; + + // interpolation of texture coordinates + float weight = afterDepth / (afterDepth - beforeDepth); + newTexCoord = mix(newTexCoord, prevTexCoords, weight); +#endif +} \ No newline at end of file diff --git a/resources/shaderlib/SGXLib_PerPixelLighting.glsl b/resources/shaderlib/SGXLib_PerPixelLighting.glsl new file mode 100644 index 0000000..d6fc0a4 --- /dev/null +++ b/resources/shaderlib/SGXLib_PerPixelLighting.glsl @@ -0,0 +1,172 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE +(Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org + +Copyright (c) 2000-2014 Torus Knot Software Ltd +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +//----------------------------------------------------------------------------- +// Program Name: SGXLib_Lighting +// Program Desc: Per pixel lighting functions. +// Program Type: Vertex/Pixel shader +// Language: GLSL +// Notes: Implements core functions for FFPLighting class. +// based on lighting engine. +// See http://msdn.microsoft.com/en-us/library/bb147178.aspx +//----------------------------------------------------------------------------- + +#include "RTSLib_Lighting.glsl" +#ifdef HAVE_AREA_LIGHTS +#include "RTSLib_LTC.glsl" +#endif + +#ifdef OGRE_HLSL +void SGX_Flip_Backface_Normal(in float triArea, in float targetFlipped, inout vec3 normal) +{ +#if OGRE_HLSL == 3 + triArea *= -1.0; + triArea *= targetFlipped; +#endif + if(triArea < 0.0) + normal *= -1.0; +} +#else +void SGX_Flip_Backface_Normal(in bool frontFacing, in float targetFlipped, inout vec3 normal) +{ + if(!frontFacing) + normal *= -1.0; +} +#endif + +void evaluateLight( + in vec3 vNormal, + in vec3 vViewPos, + in vec4 vLightPos, + in vec4 vAttParams, + in vec4 vLightDirView, + in vec4 spotParams, + in vec4 vDiffuseColour, + inout vec3 vOutDiffuse +#if defined(TVC_DIFFUSE) || defined(TVC_SPECULAR) + , in vec4 vInVertexColour +#endif +#ifdef USE_SPECULAR + , in vec4 vSpecularColour, + in float fSpecularPower, + inout vec3 vOutSpecular +#endif +#ifdef SHADOWLIGHT_COUNT + , in float shadowFactor +#endif +#ifdef HAVE_AREA_LIGHTS + , in sampler2D ltcLUT1, + in sampler2D ltcLUT2 +#endif + ) +{ + + vec3 vLightView = vLightPos.xyz; + float fLightD = 0.0; + +#ifdef TVC_DIFFUSE + vDiffuseColour *= vInVertexColour; +#endif + +#ifdef HAVE_AREA_LIGHTS + if(spotParams.w == 2.0) + { + // rect area light + vec3 dcol = vDiffuseColour.rgb; +#ifdef USE_SPECULAR +#ifdef TVC_SPECULAR + vSpecularColour *= vInVertexColour; +#endif + vec3 scol = vSpecularColour.rgb; +#else + vec3 scol = vec3_splat(0.0); + float fSpecularPower = 0.0; +#endif + float roughness = saturate(1.0 - fSpecularPower/128.0); // convert specular to roughness + roughness *= roughness; // perceptual to physical roughness + evaluateRectLight(ltcLUT1, ltcLUT2, roughness, normalize(vNormal), vViewPos, vLightPos.xyz, spotParams.xyz, vAttParams.xyz, scol, dcol); + +#ifdef SHADOWLIGHT_COUNT + dcol *= shadowFactor; + scol *= shadowFactor; +#endif + + // linear to gamma + dcol = pow(dcol, vec3_splat(1.0/2.2)); + vOutDiffuse.rgb = saturate(vOutDiffuse.rgb + dcol); +#ifdef USE_SPECULAR + scol = pow(scol, vec3_splat(1.0/2.2)); + vOutSpecular.rgb = saturate(vOutSpecular.rgb + scol); +#endif + return; + } +#endif + + if (vLightPos.w != 0.0) + { + vLightView -= vViewPos; // to light + fLightD = length(vLightView); + + if(fLightD > vAttParams.x) + return; + } + + vLightView = normalize(vLightView); + vec3 vNormalView = normalize(vNormal); + float nDotL = saturate(dot(vNormalView, vLightView)); + + if (nDotL <= 0.0) + return; + + float fAtten = getDistanceAttenuation(vAttParams.yzw, fLightD); + +#ifdef SHADOWLIGHT_COUNT + fAtten *= shadowFactor; +#endif + + if(spotParams.w != 0.0) + { + fAtten *= getAngleAttenuation(spotParams.xyz, vLightDirView.xyz, vLightView); + } + + vOutDiffuse += vDiffuseColour.rgb * nDotL * fAtten; + vOutDiffuse = saturate(vOutDiffuse); + +#ifdef USE_SPECULAR + vec3 vView = -normalize(vViewPos); + vec3 vHalfWay = normalize(vView + vLightView); + float nDotH = saturate(dot(vNormalView, vHalfWay)); +#ifdef TVC_SPECULAR + vSpecularColour *= vInVertexColour; +#endif +#ifdef NORMALISED + vSpecularColour *= (fSpecularPower + 8.0)/(8.0 * M_PI); +#endif + vOutSpecular += vSpecularColour.rgb * pow(nDotH, fSpecularPower) * fAtten; + vOutSpecular = saturate(vOutSpecular); +#endif +} \ No newline at end of file diff --git a/resources/shaderlib/SGXLib_TriplanarTexturing.glsl b/resources/shaderlib/SGXLib_TriplanarTexturing.glsl new file mode 100644 index 0000000..179c4d7 --- /dev/null +++ b/resources/shaderlib/SGXLib_TriplanarTexturing.glsl @@ -0,0 +1,47 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE +(Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org + +Copyright (c) 2000-2014 Torus Knot Software Ltd +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +void SGX_TriplanarTexturing(in vec4 diffuse, in vec3 normal, in vec4 position, in sampler2D texFromX, in sampler2D texFromY, in sampler2D texFromZ, in vec3 parameters, out vec4 cOut) +{ + vec3 blendWeights = abs(normalize(normal)); + blendWeights = blendWeights - parameters.y; + blendWeights = pow(max(blendWeights, vec3(0.0, 0.0, 0.0)), parameters.zzz); + float tot = (blendWeights.x + blendWeights.y + blendWeights.z); + blendWeights /= vec3(tot, tot, tot); + // Move the planar mapping a bit according to the normal length to avoid bad looking skirts. + float nLength = length(normal - 1.0); + vec2 coord1 = (position.yz + nLength) * parameters.x; + vec2 coord2 = (position.zx + nLength) * parameters.x; + vec2 coord3 = (position.xy + nLength) * parameters.x; + + vec4 col1 = texture2D(texFromX, coord1); + vec4 col2 = texture2D(texFromY, coord2); + vec4 col3 = texture2D(texFromZ, coord3); + cOut = diffuse * vec4(col1.xyz * blendWeights.x + + col2.xyz * blendWeights.y + + col3.xyz * blendWeights.z, 1); +} diff --git a/resources/shaderlib/SGXLib_WBOIT.glsl b/resources/shaderlib/SGXLib_WBOIT.glsl new file mode 100644 index 0000000..961cc3d --- /dev/null +++ b/resources/shaderlib/SGXLib_WBOIT.glsl @@ -0,0 +1,19 @@ +// This file is part of the OGRE project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at https://www.ogre3d.org/licensing. +// SPDX-License-Identifier: MIT + +float weight(float z, float a) +{ + // from https://casual-effects.blogspot.com/2015/03/implemented-weighted-blended-order.html + return clamp(pow(min(1.0, a * 10.0) + 0.01, 3.0) * 1e8 * pow(1.0 - z * 0.9, 3.0), 1e-2, 3e3); +} + +void SGX_WBOIT(float depth, inout vec4 accum, out vec4 revealage) +{ + vec4 colour = accum; + // Weighted Blended Order-Independent Transparency, Listing 4 + float w = weight(depth, colour.a); + accum = vec4(colour.rgb * w * colour.a, colour.a); + revealage = vec4_splat(colour.a * w); +} \ No newline at end of file diff --git a/resources/shaderlib/dfgLUTmultiscatter.dds b/resources/shaderlib/dfgLUTmultiscatter.dds new file mode 100644 index 0000000..51e397b Binary files /dev/null and b/resources/shaderlib/dfgLUTmultiscatter.dds differ diff --git a/resources/shaderlib/ltc_1.dds b/resources/shaderlib/ltc_1.dds new file mode 100644 index 0000000..e4465d2 Binary files /dev/null and b/resources/shaderlib/ltc_1.dds differ diff --git a/resources/shaderlib/ltc_2.dds b/resources/shaderlib/ltc_2.dds new file mode 100644 index 0000000..64e6b53 Binary files /dev/null and b/resources/shaderlib/ltc_2.dds differ diff --git a/resources/terrain/Bricks076C_diffspec.dds b/resources/terrain/Bricks076C_diffspec.dds new file mode 100644 index 0000000..52bd347 Binary files /dev/null and b/resources/terrain/Bricks076C_diffspec.dds differ diff --git a/resources/terrain/Bricks076C_normheight.dds b/resources/terrain/Bricks076C_normheight.dds new file mode 100644 index 0000000..4ac5423 Binary files /dev/null and b/resources/terrain/Bricks076C_normheight.dds differ diff --git a/resources/terrain/Ground23_col.jpg b/resources/terrain/Ground23_col.jpg new file mode 100644 index 0000000..31a9cbf Binary files /dev/null and b/resources/terrain/Ground23_col.jpg differ diff --git a/resources/terrain/Ground23_normheight.dds b/resources/terrain/Ground23_normheight.dds new file mode 100644 index 0000000..97cf457 Binary files /dev/null and b/resources/terrain/Ground23_normheight.dds differ diff --git a/resources/terrain/Ground23_spec.png b/resources/terrain/Ground23_spec.png new file mode 100644 index 0000000..a6a31c6 Binary files /dev/null and b/resources/terrain/Ground23_spec.png differ diff --git a/resources/terrain/Ground37_diffspec.dds b/resources/terrain/Ground37_diffspec.dds new file mode 100644 index 0000000..235e16d Binary files /dev/null and b/resources/terrain/Ground37_diffspec.dds differ diff --git a/resources/terrain/Ground37_normheight.dds b/resources/terrain/Ground37_normheight.dds new file mode 100644 index 0000000..66e5cba Binary files /dev/null and b/resources/terrain/Ground37_normheight.dds differ diff --git a/resources/terrain/README.md b/resources/terrain/README.md new file mode 100644 index 0000000..7f546af --- /dev/null +++ b/resources/terrain/README.md @@ -0,0 +1 @@ +Textures are CC0 from https://cc0textures.com/ adapted for Ogre \ No newline at end of file diff --git a/resources/terrain/Rock20_diffspec.dds b/resources/terrain/Rock20_diffspec.dds new file mode 100644 index 0000000..66888bb Binary files /dev/null and b/resources/terrain/Rock20_diffspec.dds differ diff --git a/resources/terrain/Rock20_normheight.dds b/resources/terrain/Rock20_normheight.dds new file mode 100644 index 0000000..3e1142b Binary files /dev/null and b/resources/terrain/Rock20_normheight.dds differ diff --git a/resources/terrain/TerrainSurface.glsl b/resources/terrain/TerrainSurface.glsl new file mode 100644 index 0000000..02df32e --- /dev/null +++ b/resources/terrain/TerrainSurface.glsl @@ -0,0 +1,83 @@ +void transformToTS(in vec3 TSnormal, in mat3 normalMatrix, inout vec3 normal) +{ + // derive the tangent space basis + // we do this in the pixel shader because we don't have per-vertex normals + // because of the LOD, we use a normal map + // tangent is always +x or -z in object space depending on alignment +#ifdef TERRAIN_ALIGN_Y_Z + vec3 tangent = vec3(0, 0, -1); +#else + vec3 tangent = vec3(1, 0, 0); +#endif + normal = normalize(normal); + tangent = normalize(mul(normalMatrix, tangent)); + vec3 binormal = cross(tangent, normal); + // note, now we need to re-cross to derive tangent again because it wasn't orthonormal + tangent = cross(normal, binormal); + // derive final matrix + mat3 TBN = mtxFromCols(tangent, binormal, normal); + + normal = mul(TBN, TSnormal); +} + +void getShadowFactor(in sampler2D lightmap, in vec2 uv, inout float shadowFactor) +{ + float lmShadow = texture2D(lightmap, uv).x; + shadowFactor = min(shadowFactor, lmShadow); +} + +#define MIN_BLEND_WEIGHT 0.0039 // 1/255 + +void blendTerrainLayer(in float blendWeight, in f32vec2 uv0, in float uvMul, +#ifdef TERRAIN_PARALLAX_MAPPING + in vec3 viewPos, in float scaleBias, in mat3 TBN, +#endif +#ifdef TERRAIN_NORMAL_MAPPING + in sampler2D normtex, inout vec3 normal, +#endif + in sampler2D difftex, inout vec4 diffuseSpec) +{ + if(blendWeight < MIN_BLEND_WEIGHT) + return; + + // generate UV + vec2 uv = mod(uv0 * uvMul, 1.0); + +#ifdef TERRAIN_PARALLAX_MAPPING + SGX_Generate_Parallax_Texcoord(normtex, uv, viewPos, scaleBias, TBN, uv); +#endif + + // sample diffuse texture + vec4 diffuseSpecTex = texture2D(difftex, uv); + // apply to common + diffuseSpec = mix(diffuseSpec, diffuseSpecTex, blendWeight); + +#ifdef TERRAIN_NORMAL_MAPPING + vec3 TSnormal; + // access TS normal map + SGX_FetchNormal(normtex, uv, TSnormal); + // Partial Derivative Blending https://blog.selfshadow.com/publications/blending-in-detail/ + normal = normalize(vec3(mix(normal.xy*TSnormal.z, TSnormal.xy*normal.z, blendWeight), TSnormal.z*normal.z)); +#endif +} + +//----------------------------------------------------------------------------- +void SGX_CalculateTerrainTBN(in vec3 normal, in mat3 normalMatrix, out mat3 TBN) +{ + // derive the tangent space basis + // we do this in the pixel shader because we don't have per-vertex normals + // because of the LOD, we use a normal map + // tangent is always +x or -z in object space depending on alignment + #ifdef TERRAIN_ALIGN_Y_Z + vec3 tangent = vec3(0, 0, -1); + #else + vec3 tangent = vec3(1, 0, 0); + #endif + normal = normalize(normal); + tangent = normalize(mul(normalMatrix, tangent)); + vec3 binormal = cross(tangent, normal); + // note, now we need to re-cross to derive tangent again because it wasn't orthonormal + tangent = cross(normal, binormal); + // derive final matrix + TBN = mtxFromCols(tangent, binormal, normal); +} \ No newline at end of file diff --git a/resources/terrain/TerrainTransforms.glsl b/resources/terrain/TerrainTransforms.glsl new file mode 100644 index 0000000..1e59964 --- /dev/null +++ b/resources/terrain/TerrainTransforms.glsl @@ -0,0 +1,36 @@ +/** + * @param delta: lodDelta, lodThreshold (vertex attribute) + * @param lodMorph: morph amount, morph targetLOD (uniform) + */ +void applyLODMorph(vec2 delta, vec2 lodMorph, inout float height +#ifdef TERRAIN_DEBUG +, out vec2 lodInfo +#endif +) +{ + // determine whether to apply the LOD morph to this vertex + // we store the deltas against all vertices so we only want to apply + // the morph to the ones which would disappear. + // If we subtract + // the lodThreshold from the targetLOD, and arrange to only morph if the + // result is negative (it will only be -1 in fact, since after that + // the vertex will never be indexed), we will achieve our aim. + // sign(lodThreshold - targetLOD) == -1 is to morph + + // this will either be 1 (morph) or 0 (don't morph) + float toMorph = -min(0.0, sign(delta.y - lodMorph.y)); + height += delta.x * toMorph * lodMorph.x; + +#ifdef TERRAIN_DEBUG + // LOD level (-1 since value is target level, we want to display actual) + lodInfo.x = (lodMorph.y - 1) / NUM_LODS; + // LOD morph + lodInfo.y = toMorph * lodMorph.x; +#endif +} + +void expandVertex(mat4 idxToObjectSpace, float baseUVScale, vec2 idx, float height, out vec4 position, out vec2 uv) +{ + position = mul(idxToObjectSpace, vec4(idx, height, 1)); + uv = vec2(idx.x * baseUVScale, 1.0 - idx.y * baseUVScale); +} \ No newline at end of file diff --git a/resources/terrain/terr_dirt-grass.jpg b/resources/terrain/terr_dirt-grass.jpg new file mode 100644 index 0000000..001bd3e Binary files /dev/null and b/resources/terrain/terr_dirt-grass.jpg differ diff --git a/resources/terrain/terr_rock-dirt.jpg b/resources/terrain/terr_rock-dirt.jpg new file mode 100644 index 0000000..6fda5c7 Binary files /dev/null and b/resources/terrain/terr_rock-dirt.jpg differ diff --git a/resources/terrain/terr_rock6.jpg b/resources/terrain/terr_rock6.jpg new file mode 100644 index 0000000..3100ed7 Binary files /dev/null and b/resources/terrain/terr_rock6.jpg differ diff --git a/resources/terrain/terrain.png b/resources/terrain/terrain.png new file mode 100644 index 0000000..16e853f Binary files /dev/null and b/resources/terrain/terrain.png differ diff --git a/resources/terrain/terrain_detail.jpg b/resources/terrain/terrain_detail.jpg new file mode 100644 index 0000000..981e37e Binary files /dev/null and b/resources/terrain/terrain_detail.jpg differ diff --git a/resources/terrain/terrain_texture.jpg b/resources/terrain/terrain_texture.jpg new file mode 100644 index 0000000..8d4fda5 Binary files /dev/null and b/resources/terrain/terrain_texture.jpg differ diff --git a/skybox/early_morning_bk.jpg b/skybox/early_morning_bk.jpg new file mode 100644 index 0000000..15b8042 Binary files /dev/null and b/skybox/early_morning_bk.jpg differ diff --git a/skybox/early_morning_dn.jpg b/skybox/early_morning_dn.jpg new file mode 100644 index 0000000..9263126 Binary files /dev/null and b/skybox/early_morning_dn.jpg differ diff --git a/skybox/early_morning_fr.jpg b/skybox/early_morning_fr.jpg new file mode 100644 index 0000000..811bb14 Binary files /dev/null and b/skybox/early_morning_fr.jpg differ diff --git a/skybox/early_morning_lf.jpg b/skybox/early_morning_lf.jpg new file mode 100644 index 0000000..bfef097 Binary files /dev/null and b/skybox/early_morning_lf.jpg differ diff --git a/skybox/early_morning_rt.jpg b/skybox/early_morning_rt.jpg new file mode 100644 index 0000000..d31cd8d Binary files /dev/null and b/skybox/early_morning_rt.jpg differ diff --git a/skybox/early_morning_up.jpg b/skybox/early_morning_up.jpg new file mode 100644 index 0000000..aa2a004 Binary files /dev/null and b/skybox/early_morning_up.jpg differ diff --git a/skybox/skybox.material b/skybox/skybox.material new file mode 100644 index 0000000..271f098 --- /dev/null +++ b/skybox/skybox.material @@ -0,0 +1,37 @@ +material Skybox +{ + technique + { + pass + { + lighting off + depth_write off + + texture_unit + { + texture early_morning.jpg cubic + tex_address_mode clamp + } + } + } +} +material Skybox/Dynamic +{ + technique + { + pass + { + lighting off + depth_write off + ambient 1.0 1.0 1.0 1.0 + diffuse 0.0 0.0 1.0 1.0 + specular 0.0 0.0 0.0 1.0 + vertex_program_ref debug_vp + { + } + fragment_program_ref debug_fp + { + } + } + } +}