From f58cb9ad9c95786dc806d3af1884c6d6094d63e2 Mon Sep 17 00:00:00 2001 From: AVinitzca Date: Sun, 31 Aug 2025 22:40:33 -0300 Subject: [PATCH 01/10] Base Sample. Still missing docs --- .../3D/sponza/Sponza.fbm/background.tga | 3 + .../3D/sponza/Sponza.fbm/chain_texture.tga | 3 + .../Content/3D/sponza/Sponza.fbm/lion.tga | 3 + .../sponza/Sponza.fbm/spnza_bricks_a_diff.tga | 3 + .../3D/sponza/Sponza.fbm/sponza_arch_diff.tga | 3 + .../Sponza.fbm/sponza_ceiling_a_diff.tga | 3 + .../Sponza.fbm/sponza_column_a_diff.tga | 3 + .../Sponza.fbm/sponza_column_b_diff.tga | 3 + .../Sponza.fbm/sponza_column_c_diff.tga | 3 + .../Sponza.fbm/sponza_curtain_blue_diff.tga | 3 + .../sponza/Sponza.fbm/sponza_curtain_diff.tga | 3 + .../Sponza.fbm/sponza_curtain_green_diff.tga | 3 + .../sponza/Sponza.fbm/sponza_details_diff.tga | 3 + .../Sponza.fbm/sponza_fabric_blue_diff.tga | 3 + .../sponza/Sponza.fbm/sponza_fabric_diff.tga | 3 + .../Sponza.fbm/sponza_fabric_green_diff.tga | 3 + .../Sponza.fbm/sponza_flagpole_diff.tga | 3 + .../sponza/Sponza.fbm/sponza_floor_a_diff.tga | 3 + .../3D/sponza/Sponza.fbm/sponza_roof_diff.tga | 3 + .../sponza/Sponza.fbm/sponza_thorn_diff.tga | 3 + .../Content/3D/sponza/Sponza.fbm/vase_dif.tga | 3 + .../3D/sponza/Sponza.fbm/vase_hanging.tga | 3 + .../3D/sponza/Sponza.fbm/vase_plant.tga | 3 + .../3D/sponza/Sponza.fbm/vase_round.tga | 3 + .../Content/3D/sponza/Sponza.fbx | 3 + TGC.MonoGame.Samples/Content/Content.mgcb | 63 +- .../Content/Effects/BasicTexture.fx | 65 ++ TGC.MonoGame.Samples/Models/Geometry.cs | 81 ++ TGC.MonoGame.Samples/Models/GeometryData.cs | 13 + .../Models/ModelExtensions.cs | 755 ++++++++++++++++++ TGC.MonoGame.Samples/Models/ModelInfo.cs | 13 + TGC.MonoGame.Samples/Samples/Models/Sponza.cs | 122 +++ .../TGC.MonoGame.Samples.csproj | 3 + 33 files changed, 1174 insertions(+), 16 deletions(-) create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/background.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/chain_texture.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/lion.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/spnza_bricks_a_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_arch_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_ceiling_a_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_a_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_b_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_c_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_blue_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_green_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_details_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_blue_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_green_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_flagpole_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_floor_a_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_roof_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_thorn_diff.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_dif.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_hanging.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_plant.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_round.tga create mode 100644 TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbx create mode 100644 TGC.MonoGame.Samples/Content/Effects/BasicTexture.fx create mode 100644 TGC.MonoGame.Samples/Models/Geometry.cs create mode 100644 TGC.MonoGame.Samples/Models/GeometryData.cs create mode 100644 TGC.MonoGame.Samples/Models/ModelExtensions.cs create mode 100644 TGC.MonoGame.Samples/Models/ModelInfo.cs create mode 100644 TGC.MonoGame.Samples/Samples/Models/Sponza.cs diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/background.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/background.tga new file mode 100644 index 0000000..e9a0e0c --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/background.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e62f5e6c69430bdefbd1e5f61a0f27aa95cd9e564aeaf2c64d73df09f8f0e83 +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/chain_texture.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/chain_texture.tga new file mode 100644 index 0000000..899320e --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/chain_texture.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e04fd407ae53adcc7d63396b6eae30e9e54f1b5ab002a5b517c06193971a1f56 +size 1048620 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/lion.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/lion.tga new file mode 100644 index 0000000..5cae91c --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/lion.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47d156110176e3dc23017a8afb04fd919fa4a82eb7ac88b51840a090a88e2024 +size 4194348 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/spnza_bricks_a_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/spnza_bricks_a_diff.tga new file mode 100644 index 0000000..144e487 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/spnza_bricks_a_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ecb29bfc09df89efd8acd5243067e2421c08843066d75f08be340f365f8dc7f +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_arch_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_arch_diff.tga new file mode 100644 index 0000000..fed674e --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_arch_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:878105dd0acdd0fdfadae0b394e5d245692c25ed62ebcd4a129768901ab09546 +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_ceiling_a_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_ceiling_a_diff.tga new file mode 100644 index 0000000..e0b99eb --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_ceiling_a_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1b4105e8c4d8f4f90f380a77cd3235a764b8a8d8017e6aa4c94d7acb65cf4dd +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_a_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_a_diff.tga new file mode 100644 index 0000000..c0eb58a --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_a_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bed2aa34a8600c0b33d40dbf68e497acb5cc85943b914f2cb32d33594910db86 +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_b_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_b_diff.tga new file mode 100644 index 0000000..2213949 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_b_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96754107a435960e6402c93d186a4db1da00f06d8f5b63b5049f7b6693ac0ba3 +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_c_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_c_diff.tga new file mode 100644 index 0000000..0ba195d --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_column_c_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25057287f03ec7af9e03deccbb578a7c6fce7ec861625b1006a1a5279e64e4be +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_blue_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_blue_diff.tga new file mode 100644 index 0000000..361d4f0 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_blue_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8ec042a8c3d83a9da8170fdfc1f9d6566a474859c6c88f289a6daec30524112 +size 12582956 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_diff.tga new file mode 100644 index 0000000..0ccfa34 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd979e212ec84ae8df8b3e3b77dfeb4d12c07a469ee19b3b628cbce502524ede +size 12582956 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_green_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_green_diff.tga new file mode 100644 index 0000000..f944440 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_curtain_green_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9ef35f06ac675bf0f85e8fcf516b22c90e5218d032c4a7947ecfeec4b6becdb +size 12582956 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_details_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_details_diff.tga new file mode 100644 index 0000000..fcbe2de --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_details_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ed79e9c6360cd72c7a5f6048fe84983b6aee0c0966e0a63a3765905bc46fda2 +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_blue_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_blue_diff.tga new file mode 100644 index 0000000..4d3d962 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_blue_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30c22a697dd2bb0ce25f18dc3ff82a0cf7b36ac2458914efa746474924fbe370 +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_diff.tga new file mode 100644 index 0000000..880f552 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af9be9d8f09f2399fc25b42eb65877f4329439cbf88643e10175997de278a831 +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_green_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_green_diff.tga new file mode 100644 index 0000000..52967af --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_fabric_green_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3664389c001f79809a9da36dbd64162564987d88f5744ae05b91c056b8a0eb7 +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_flagpole_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_flagpole_diff.tga new file mode 100644 index 0000000..3cb77b7 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_flagpole_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0852fd9b176d4ca5e8afe20901de44acd9180552648d81406b93e217f37562bb +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_floor_a_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_floor_a_diff.tga new file mode 100644 index 0000000..78f9102 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_floor_a_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08a953cd9083577f284911e5450cc81ac3abcc25fd2ca7485e28f6ec65e83797 +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_roof_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_roof_diff.tga new file mode 100644 index 0000000..8fe1a93 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_roof_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d42bd726aa8d4a04272eace08beea677c45bb010b08923c341564e1dc96eb521 +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_thorn_diff.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_thorn_diff.tga new file mode 100644 index 0000000..adb5481 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/sponza_thorn_diff.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22b2188de0ec4ca7495ffaac9a47ecc339365686ff6b1722ecffea145c25d45b +size 1048620 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_dif.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_dif.tga new file mode 100644 index 0000000..09e2291 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_dif.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8c8a66cf47379ab6bf14373c17302116fbc81d52fcf479e2e911b90086eaaab +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_hanging.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_hanging.tga new file mode 100644 index 0000000..5ec9766 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_hanging.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbd7094291c824f9907652f463878abd14090da424ae7b2b99bb896275528a8c +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_plant.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_plant.tga new file mode 100644 index 0000000..929ef98 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_plant.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d64638a1a213510a2d8acc0bb67cbf7036a2f27755d660682ab0da2817fe827a +size 4194348 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_round.tga b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_round.tga new file mode 100644 index 0000000..345fb89 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbm/vase_round.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:becf2130c4ec5fc071b743626b064ae7346c3c9654e8ca9233c332d26fb2d32a +size 3145772 diff --git a/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbx b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbx new file mode 100644 index 0000000..884c9a7 --- /dev/null +++ b/TGC.MonoGame.Samples/Content/3D/sponza/Sponza.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acc694212c42af5ac985492accb186cf713f268fdeba8db25d53b2caae994377 +size 8621004 diff --git a/TGC.MonoGame.Samples/Content/Content.mgcb b/TGC.MonoGame.Samples/Content/Content.mgcb index c4d8d85..1d372cd 100644 --- a/TGC.MonoGame.Samples/Content/Content.mgcb +++ b/TGC.MonoGame.Samples/Content/Content.mgcb @@ -108,6 +108,25 @@ /processorParam:TextureFormat=Compressed /build:3D/skybox/cube.fbx +#begin 3D/sponza/Sponza.fbx +/importer:FbxImporter +/processor:ModelProcessor +/processorParam:ColorKeyColor=0,0,0,0 +/processorParam:ColorKeyEnabled=True +/processorParam:DefaultEffect=BasicEffect +/processorParam:GenerateMipmaps=True +/processorParam:GenerateTangentFrames=False +/processorParam:PremultiplyTextureAlpha=True +/processorParam:PremultiplyVertexColors=True +/processorParam:ResizeTexturesToPowerOfTwo=False +/processorParam:RotationX=0 +/processorParam:RotationY=0 +/processorParam:RotationZ=0 +/processorParam:Scale=1 +/processorParam:SwapWindingOrder=False +/processorParam:TextureFormat=Compressed +/build:3D/sponza/Sponza.fbx + #begin 3D/tank/tank.fbx /importer:FbxImporter /processor:ModelProcessor @@ -127,7 +146,7 @@ /processorParam:TextureFormat=Compressed /build:3D/tank/tank.fbx -#begin 3D/tgcito-classic/tgcito-classic.obj +#begin 3D/tgc-logo/tgc-logo.fbx /importer:FbxImporter /processor:ModelProcessor /processorParam:ColorKeyColor=0,0,0,0 @@ -144,10 +163,10 @@ /processorParam:Scale=1 /processorParam:SwapWindingOrder=False /processorParam:TextureFormat=Compressed -/build:3D/tgcito-classic/tgcito-classic.obj +/build:3D/tgc-logo/tgc-logo.fbx -#begin 3D/tgcito-mega/tgcito-mega.obj -/importer:OpenAssetImporter +#begin 3D/tgcito-classic/tgcito-classic.obj +/importer:FbxImporter /processor:ModelProcessor /processorParam:ColorKeyColor=0,0,0,0 /processorParam:ColorKeyEnabled=True @@ -163,10 +182,10 @@ /processorParam:Scale=1 /processorParam:SwapWindingOrder=False /processorParam:TextureFormat=Compressed -/build:3D/tgcito-mega/tgcito-mega.obj +/build:3D/tgcito-classic/tgcito-classic.obj -#begin 3D/tgc-logo/tgc-logo.fbx -/importer:FbxImporter +#begin 3D/tgcito-mega/tgcito-mega.obj +/importer:OpenAssetImporter /processor:ModelProcessor /processorParam:ColorKeyColor=0,0,0,0 /processorParam:ColorKeyEnabled=True @@ -182,7 +201,7 @@ /processorParam:Scale=1 /processorParam:SwapWindingOrder=False /processorParam:TextureFormat=Compressed -/build:3D/tgc-logo/tgc-logo.fbx +/build:3D/tgcito-mega/tgcito-mega.obj #begin Effects/AlphaBlending.fx /importer:EffectImporter @@ -202,6 +221,18 @@ /processorParam:DebugMode=Auto /build:Effects/BasicShader.fx +#begin Effects/BasicTexture.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:Effects/BasicTexture.fx + +#begin Effects/BasicTexture.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:Effects/BasicTexture.fx + #begin Effects/BlinnPhong.fx /importer:EffectImporter /processor:EffectProcessor @@ -354,7 +385,7 @@ #begin SpriteFonts/CascadiaCode/CascadiaMonoPL.ttf /copy:SpriteFonts/CascadiaCode/CascadiaMonoPL.ttf -#begin Textures/floor/adoquin.jpg +#begin Textures/floor/adoquin-2.jpg /importer:TextureImporter /processor:TextureProcessor /processorParam:ColorKeyColor=255,0,255,255 @@ -364,9 +395,9 @@ /processorParam:ResizeToPowerOfTwo=False /processorParam:MakeSquare=False /processorParam:TextureFormat=Color -/build:Textures/floor/adoquin.jpg +/build:Textures/floor/adoquin-2.jpg -#begin Textures/floor/adoquin-2.jpg +#begin Textures/floor/adoquin.jpg /importer:TextureImporter /processor:TextureProcessor /processorParam:ColorKeyColor=255,0,255,255 @@ -376,7 +407,7 @@ /processorParam:ResizeToPowerOfTwo=False /processorParam:MakeSquare=False /processorParam:TextureFormat=Color -/build:Textures/floor/adoquin-2.jpg +/build:Textures/floor/adoquin.jpg #begin Textures/floor/tierra.jpg /importer:TextureImporter @@ -474,7 +505,7 @@ /processorParam:TextureFormat=Color /build:Textures/heightmaps/colormap.jpg -#begin Textures/heightmaps/heightmap.jpg +#begin Textures/heightmaps/heightmap-3.jpg /importer:TextureImporter /processor:TextureProcessor /processorParam:ColorKeyColor=255,0,255,255 @@ -484,9 +515,9 @@ /processorParam:ResizeToPowerOfTwo=False /processorParam:MakeSquare=False /processorParam:TextureFormat=Color -/build:Textures/heightmaps/heightmap.jpg +/build:Textures/heightmaps/heightmap-3.jpg -#begin Textures/heightmaps/heightmap-3.jpg +#begin Textures/heightmaps/heightmap.jpg /importer:TextureImporter /processor:TextureProcessor /processorParam:ColorKeyColor=255,0,255,255 @@ -496,7 +527,7 @@ /processorParam:ResizeToPowerOfTwo=False /processorParam:MakeSquare=False /processorParam:TextureFormat=Color -/build:Textures/heightmaps/heightmap-3.jpg +/build:Textures/heightmaps/heightmap.jpg #begin Textures/heightmaps/terrain-texture-3.jpg /importer:TextureImporter diff --git a/TGC.MonoGame.Samples/Content/Effects/BasicTexture.fx b/TGC.MonoGame.Samples/Content/Effects/BasicTexture.fx new file mode 100644 index 0000000..048cb0e --- /dev/null +++ b/TGC.MonoGame.Samples/Content/Effects/BasicTexture.fx @@ -0,0 +1,65 @@ +#if OPENGL + #define SV_POSITION POSITION + #define VS_SHADERMODEL vs_3_0 + #define PS_SHADERMODEL ps_3_0 +#else + #define VS_SHADERMODEL vs_4_0_level_9_1 + #define PS_SHADERMODEL ps_4_0_level_9_1 +#endif + +float4x4 World; +float4x4 View; +float4x4 Projection; + +struct VertexShaderInput +{ + float4 Position : POSITION0; + float2 TextureCoordinate : TEXCOORD0; +}; + +struct VertexShaderOutput +{ + float4 Position : SV_POSITION; + float2 TextureCoordinate : TEXCOORD1; +}; + +texture ModelTexture; +sampler2D textureSampler = sampler_state +{ + Texture = (ModelTexture); + MagFilter = Linear; + MinFilter = Linear; + AddressU = Wrap; + AddressV = Wrap; +}; + +VertexShaderOutput MainVS(in VertexShaderInput input) +{ + VertexShaderOutput output = (VertexShaderOutput)0; + + float4 worldPosition = mul(input.Position, World); + float4 viewPosition = mul(worldPosition, View); + + // Project position + output.Position = mul(viewPosition, Projection); + + // Propagate texture coordinates + output.TextureCoordinate = input.TextureCoordinate; + + return output; +} + +float4 MainPS(VertexShaderOutput input) : COLOR +{ + // Get the texture texel textureSampler is the sampler, Texcoord is the interpolated coordinates + return tex2D(textureSampler, input.TextureCoordinate); +} + +technique BasicDrawing +{ + pass P0 + { + VertexShader = compile VS_SHADERMODEL MainVS(); + PixelShader = compile PS_SHADERMODEL MainPS(); + } +}; diff --git a/TGC.MonoGame.Samples/Models/Geometry.cs b/TGC.MonoGame.Samples/Models/Geometry.cs new file mode 100644 index 0000000..f96e417 --- /dev/null +++ b/TGC.MonoGame.Samples/Models/Geometry.cs @@ -0,0 +1,81 @@ +using System; +using Microsoft.Xna.Framework.Graphics; + +namespace TGC.MonoGame.Samples.Models; + +public class Geometry : IDisposable +{ + internal VertexBuffer VertexBuffer; + + internal IndexBuffer IndexBuffer; + + internal int VertexOffset; + + internal int StartIndex; + + internal int PrimitiveCount; + + public int VertexCount; + + internal bool OwnsVertexBuffer = false; + + internal bool OwnsIndexBuffer = false; + + + internal static Geometry FromMeshPart(ModelMeshPart part) + { + return new Geometry(part); + } + + internal Geometry() + { } + + internal Geometry(VertexBuffer vertexBuffer, IndexBuffer indexBuffer) + { + VertexBuffer = vertexBuffer; + IndexBuffer = indexBuffer; + VertexOffset = 0; + StartIndex = 0; + PrimitiveCount = indexBuffer.IndexCount / 3; + VertexCount = vertexBuffer.VertexCount; + OwnsVertexBuffer = true; + OwnsIndexBuffer = true; + } + + private Geometry(ModelMeshPart part) + { + VertexCount = part.NumVertices; + VertexBuffer = part.VertexBuffer; + IndexBuffer = part.IndexBuffer; + VertexOffset = part.VertexOffset; + StartIndex = part.StartIndex; + PrimitiveCount = part.PrimitiveCount; + OwnsVertexBuffer = false; + OwnsIndexBuffer = false; + } + + public void Draw(Effect effect) + { + var graphicsDevice = effect.GraphicsDevice; + + graphicsDevice.SetVertexBuffer(VertexBuffer); + graphicsDevice.Indices = IndexBuffer; + + foreach (var pass in effect.CurrentTechnique.Passes) + { + pass.Apply(); + graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, + VertexOffset, StartIndex, PrimitiveCount); + } + } + + + public void Dispose() + { + if(OwnsVertexBuffer) + VertexBuffer.Dispose(); + + if (OwnsIndexBuffer) + IndexBuffer.Dispose(); + } +} \ No newline at end of file diff --git a/TGC.MonoGame.Samples/Models/GeometryData.cs b/TGC.MonoGame.Samples/Models/GeometryData.cs new file mode 100644 index 0000000..c892c89 --- /dev/null +++ b/TGC.MonoGame.Samples/Models/GeometryData.cs @@ -0,0 +1,13 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace TGC.MonoGame.Samples.Models; + +public struct GeometryData(Geometry geometry, Matrix relativeMatrix, Texture[] textures) +{ + public Geometry Geometry { get; private set; } = geometry; + + public Matrix RelativeMatrix { get; private set; } = relativeMatrix; + + public Texture[] Textures { get; private set; } = textures; +} \ No newline at end of file diff --git a/TGC.MonoGame.Samples/Models/ModelExtensions.cs b/TGC.MonoGame.Samples/Models/ModelExtensions.cs new file mode 100644 index 0000000..8634e07 --- /dev/null +++ b/TGC.MonoGame.Samples/Models/ModelExtensions.cs @@ -0,0 +1,755 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace TGC.MonoGame.Samples.Models; + +public static class ModelExtensions +{ + public static ModelInfo Get(Model model) + { + int geometryCount = 0; + foreach (var mesh in model.Meshes) + { + geometryCount += mesh.MeshParts.Count; + } + + var geometryData = new GeometryData[geometryCount]; + + int geometryIndex = 0; + var absoluteMatrices = new Matrix[model.Bones.Count]; + model.CopyAbsoluteBoneTransformsTo(absoluteMatrices); + + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + var mainTexture = ((BasicEffect)part.Effect).Texture; + Texture[] textures = mainTexture != null ? [mainTexture] : []; + + geometryData[geometryIndex] = + new GeometryData(Geometry.FromMeshPart(part), absoluteMatrices[mesh.ParentBone.Index], + textures); + + geometryIndex++; + } + } + + return new ModelInfo(geometryData); + } + + public static ModelInfo GetMerged(Microsoft.Xna.Framework.Graphics.Model model) + { + var absoluteMatrices = new Matrix[model.Bones.Count]; + model.CopyAbsoluteBoneTransformsTo(absoluteMatrices); + + int vertexCount = 0; + int indexCount = 0; + + var device = model.Meshes.First().Effects.First().GraphicsDevice; + + var textures = new List(); + + // Extract textures, vertices and indices + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + vertexCount += part.NumVertices; + indexCount += part.PrimitiveCount * 3; + + var mainTexture = ((BasicEffect)part.Effect).Texture; + + if (mainTexture != null) + textures.Add(mainTexture); + } + } + + VertexBuffer vertexBuffer; + { + // Extract vertices + var vertices = new VertexPositionColorNormalTexture[vertexCount]; + + Dictionary vertexData = new(); + + foreach (var mesh in model.Meshes) + { + var transform = absoluteMatrices[mesh.ParentBone.Index]; + + foreach (var part in mesh.MeshParts) + { + var partVertexBuffer = part.VertexBuffer; + + if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) + { + var declaration = partVertexBuffer.VertexDeclaration; + var vertexSize = declaration.VertexStride; + bufferData = new byte[vertexSize * partVertexBuffer.VertexCount]; + partVertexBuffer.GetData(bufferData); + vertexData.Add(partVertexBuffer, bufferData); + } + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; + + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + CopyTo(bufferData.AsSpan().Slice(offsetByStride, numVerticesByStride), + vertices.AsSpan().Slice(part.VertexOffset, part.NumVertices), + transform, partVertexBuffer.VertexDeclaration); + } + } + + vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), vertices.Length, BufferUsage.None); + vertexBuffer.SetData(vertices); + } + + + IndexBuffer indexBuffer = new IndexBuffer(device, + vertexCount > ushort.MaxValue ? IndexElementSize.ThirtyTwoBits : IndexElementSize.SixteenBits, + indexCount, BufferUsage.None); + + { + // Extract indices + + Dictionary indexData = new(); + int currentIndexCount = 0; + + if (vertexCount > ushort.MaxValue) + { + var indices = new uint[indexCount]; + + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + var partIndexBuffer = part.IndexBuffer; + + int stride = partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; + + if (!indexData.TryGetValue(partIndexBuffer, out var bufferData)) + { + var data = new byte[partIndexBuffer.IndexCount * stride]; + partIndexBuffer.GetData(data); + bufferData = data; + indexData.Add(partIndexBuffer, data); + } + + if (partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) + { + var shortIndexData = MemoryMarshal.Cast(bufferData.AsSpan() + .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); + + for (int i = 0; i < shortIndexData.Length; i++) + { + indices[i + currentIndexCount] = shortIndexData[i]; + } + + currentIndexCount += shortIndexData.Length; + } + else + { + var integerIndexData = MemoryMarshal.Cast(bufferData.AsSpan() + .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); + + integerIndexData.CopyTo(indices.AsSpan().Slice(currentIndexCount)); + currentIndexCount += integerIndexData.Length; + } + } + } + + indexBuffer.SetData(indices); + } + else + { + var indices = new ushort[indexCount]; + + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + var partIndexBuffer = part.IndexBuffer; + + int stride = partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; + + if (!indexData.TryGetValue(partIndexBuffer, out var bufferData)) + { + var data = new byte[partIndexBuffer.IndexCount * stride]; + partIndexBuffer.GetData(data); + bufferData = data; + indexData.Add(partIndexBuffer, data); + } + + if (partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) + { + var shortIndexData = MemoryMarshal.Cast(bufferData.AsSpan() + .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); + + shortIndexData.CopyTo(indices.AsSpan().Slice(currentIndexCount)); + + currentIndexCount += shortIndexData.Length; + } + else + { + var integerIndexData = MemoryMarshal.Cast(bufferData.AsSpan() + .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); + + for (int i = 0; i < integerIndexData.Length; i++) + { + indices[i + currentIndexCount] = (ushort)integerIndexData[i]; + } + + currentIndexCount += integerIndexData.Length; + } + } + } + + indexBuffer.SetData(indices); + } + } + + return new ModelInfo( + [ + new GeometryData(new Geometry(vertexBuffer, indexBuffer), Matrix.Identity, textures.ToArray()) + ]); + } + + public static ModelInfo GetCentered(Model model) + { + int geometryCount = 0; + foreach (var mesh in model.Meshes) + { + geometryCount += mesh.MeshParts.Count; + } + + var geometryData = new GeometryData[geometryCount]; + + var absoluteMatrices = new Matrix[model.Bones.Count]; + model.CopyAbsoluteBoneTransformsTo(absoluteMatrices); + + int vertexCount = 0; + + var device = model.Meshes.First().Effects.First().GraphicsDevice; + + List vertexBuffers = new(); + + Dictionary vertexData = new(); + + { + Vector3 sum = Vector3.Zero; + + // Extract vertices + + int vertexBufferIndex = 0; + + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + vertexCount += part.NumVertices; + + var partVertexBuffer = part.VertexBuffer; + + if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) + { + var declaration = partVertexBuffer.VertexDeclaration; + var vertexSize = declaration.VertexStride; + var data = new byte[vertexSize * partVertexBuffer.VertexCount]; + partVertexBuffer.GetData(data); + + bufferData.Index = vertexBufferIndex; + bufferData.Data = data; + bufferData.Vertices = new VertexPositionColorNormalTexture[partVertexBuffer.VertexCount]; + vertexData.Add(partVertexBuffer, bufferData); + + vertexBufferIndex++; + } + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; + + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + Sum(bufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), partVertexBuffer.VertexDeclaration, ref sum); + } + } + + sum /= vertexCount; + + var transform = Matrix.CreateTranslation(-sum); + + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + var partVertexBuffer = part.VertexBuffer; + + var vertexBufferData = vertexData[partVertexBuffer]; + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; + + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + CopyTo(vertexBufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), + vertexBufferData.Vertices.AsSpan().Slice(part.VertexOffset, part.NumVertices), + transform, partVertexBuffer.VertexDeclaration); + } + } + + foreach (var data in vertexData.Values) + { + var vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), data.Vertices.Length, BufferUsage.None); + vertexBuffer.SetData(data.Vertices); + vertexBuffers.Add(vertexBuffer); + } + } + + HashSet assignedVertexBuffers = new(); + + int geometryIndex = 0; + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + var mainTexture = ((BasicEffect)part.Effect).Texture; + + Texture[] textures; + + if (mainTexture != null) + textures = [mainTexture]; + else + textures = []; + + geometryData[geometryIndex] = new GeometryData( + new Geometry() + { + VertexBuffer = vertexBuffers[vertexData[part.VertexBuffer].Index], + PrimitiveCount = part.PrimitiveCount, + VertexCount = part.NumVertices, + IndexBuffer = part.IndexBuffer, + StartIndex = part.StartIndex, + VertexOffset = part.VertexOffset, + OwnsVertexBuffer = assignedVertexBuffers.Add(part.VertexBuffer), + OwnsIndexBuffer = false + }, + absoluteMatrices[mesh.ParentBone.Index], + textures + ); + + geometryIndex++; + } + } + + return new ModelInfo(geometryData); + } + + public static ModelInfo GetCenteredXZ(Model model) + { + int geometryCount = 0; + foreach (var mesh in model.Meshes) + { + geometryCount += mesh.MeshParts.Count; + } + + var geometryData = new GeometryData[geometryCount]; + + var absoluteMatrices = new Matrix[model.Bones.Count]; + model.CopyAbsoluteBoneTransformsTo(absoluteMatrices); + + int vertexCount = 0; + + var device = model.Meshes.First().Effects.First().GraphicsDevice; + + List vertexBuffers = new(); + + Dictionary vertexData = new(); + + { + Vector3 sum = Vector3.Zero; + + // Extract vertices + + int vertexBufferIndex = 0; + + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + vertexCount += part.NumVertices; + + var partVertexBuffer = part.VertexBuffer; + + if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) + { + var declaration = partVertexBuffer.VertexDeclaration; + var vertexSize = declaration.VertexStride; + var data = new byte[vertexSize * partVertexBuffer.VertexCount]; + partVertexBuffer.GetData(data); + + bufferData.Index = vertexBufferIndex; + bufferData.Data = data; + bufferData.Vertices = new VertexPositionColorNormalTexture[partVertexBuffer.VertexCount]; + vertexData.Add(partVertexBuffer, bufferData); + + vertexBufferIndex++; + } + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; + + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + Sum(bufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), partVertexBuffer.VertexDeclaration, ref sum); + } + } + + sum /= vertexCount; + + var transform = Matrix.CreateTranslation(-sum.X, 0f, -sum.Z); + + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + var partVertexBuffer = part.VertexBuffer; + + var vertexBufferData = vertexData[partVertexBuffer]; + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; + + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + CopyTo(vertexBufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), + vertexBufferData.Vertices.AsSpan().Slice(part.VertexOffset, part.NumVertices), + transform, partVertexBuffer.VertexDeclaration); + } + } + + foreach (var data in vertexData.Values) + { + var vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), data.Vertices.Length, BufferUsage.None); + vertexBuffer.SetData(data.Vertices); + vertexBuffers.Add(vertexBuffer); + } + } + + HashSet assignedVertexBuffers = new(); + + int geometryIndex = 0; + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + var mainTexture = ((BasicEffect)part.Effect).Texture; + + Texture[] textures; + + if (mainTexture != null) + textures = [mainTexture]; + else + textures = []; + + geometryData[geometryIndex] = new GeometryData( + new Geometry() + { + VertexBuffer = vertexBuffers[vertexData[part.VertexBuffer].Index], + PrimitiveCount = part.PrimitiveCount, + VertexCount = part.NumVertices, + IndexBuffer = part.IndexBuffer, + StartIndex = part.StartIndex, + VertexOffset = part.VertexOffset, + OwnsVertexBuffer = assignedVertexBuffers.Add(part.VertexBuffer), + OwnsIndexBuffer = false + }, + absoluteMatrices[mesh.ParentBone.Index], + textures + ); + + geometryIndex++; + } + } + + return new ModelInfo(geometryData); + } + + public static ModelInfo GetMergedCentered(Microsoft.Xna.Framework.Graphics.Model model) + { + var absoluteMatrices = new Matrix[model.Bones.Count]; + model.CopyAbsoluteBoneTransformsTo(absoluteMatrices); + + int vertexCount = 0; + int indexCount = 0; + + var device = model.Meshes.First().Effects.First().GraphicsDevice; + + var textures = new List(); + + // Extract textures, vertices and indices + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + vertexCount += part.NumVertices; + indexCount += part.PrimitiveCount * 3; + + var mainTexture = ((BasicEffect)part.Effect).Texture; + + if (mainTexture != null) + textures.Add(mainTexture); + } + } + + Vector3 sum = Vector3.Zero; + + Dictionary vertexData = new(); + + // Extract vertices + + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + vertexCount += part.NumVertices; + + var partVertexBuffer = part.VertexBuffer; + + if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) + { + var declaration = partVertexBuffer.VertexDeclaration; + var vertexSize = declaration.VertexStride; + bufferData = new byte[vertexSize * partVertexBuffer.VertexCount]; + partVertexBuffer.GetData(bufferData); + vertexData.Add(partVertexBuffer, bufferData); + } + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; + + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + Sum(bufferData.AsSpan().Slice(offsetByStride, numVerticesByStride), partVertexBuffer.VertexDeclaration, ref sum); + } + } + + sum /= vertexCount; + + var centeringTransform = Matrix.CreateTranslation(-sum); + + VertexBuffer vertexBuffer; + { + // Extract vertices + var vertices = new VertexPositionColorNormalTexture[vertexCount]; + + + foreach (var mesh in model.Meshes) + { + var transform = absoluteMatrices[mesh.ParentBone.Index] * centeringTransform; + + foreach (var part in mesh.MeshParts) + { + var partVertexBuffer = part.VertexBuffer; + + var data = vertexData[partVertexBuffer]; + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; + + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + CopyTo(data.AsSpan().Slice(offsetByStride, numVerticesByStride), + vertices.AsSpan().Slice(part.VertexOffset, part.NumVertices), + transform, partVertexBuffer.VertexDeclaration); + } + } + + vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), vertices.Length, BufferUsage.None); + vertexBuffer.SetData(vertices); + } + + IndexBuffer indexBuffer = new IndexBuffer(device, + vertexCount > ushort.MaxValue ? IndexElementSize.ThirtyTwoBits : IndexElementSize.SixteenBits, + indexCount, BufferUsage.None); + + { + // Extract indices + + Dictionary indexData = new(); + int currentIndexCount = 0; + + if (vertexCount > ushort.MaxValue) + { + var indices = new uint[indexCount]; + + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + var partIndexBuffer = part.IndexBuffer; + + int stride = partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; + + if (!indexData.TryGetValue(partIndexBuffer, out var bufferData)) + { + var data = new byte[partIndexBuffer.IndexCount * stride]; + partIndexBuffer.GetData(data); + bufferData = data; + indexData.Add(partIndexBuffer, data); + } + + if (partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) + { + var shortIndexData = MemoryMarshal.Cast(bufferData.AsSpan() + .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); + + for (int i = 0; i < shortIndexData.Length; i++) + { + indices[i + currentIndexCount] = shortIndexData[i]; + } + + currentIndexCount += shortIndexData.Length; + } + else + { + var integerIndexData = MemoryMarshal.Cast(bufferData.AsSpan() + .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); + + integerIndexData.CopyTo(indices.AsSpan().Slice(currentIndexCount)); + currentIndexCount += integerIndexData.Length; + } + } + } + + indexBuffer.SetData(indices); + } + else + { + var indices = new ushort[indexCount]; + + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + var partIndexBuffer = part.IndexBuffer; + + int stride = partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; + + if (!indexData.TryGetValue(partIndexBuffer, out var bufferData)) + { + var data = new byte[partIndexBuffer.IndexCount * stride]; + partIndexBuffer.GetData(data); + bufferData = data; + indexData.Add(partIndexBuffer, data); + } + + if (partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) + { + var shortIndexData = MemoryMarshal.Cast(bufferData.AsSpan() + .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); + + shortIndexData.CopyTo(indices.AsSpan().Slice(currentIndexCount)); + + currentIndexCount += shortIndexData.Length; + } + else + { + var integerIndexData = MemoryMarshal.Cast(bufferData.AsSpan() + .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); + + for (int i = 0; i < integerIndexData.Length; i++) + { + indices[i + currentIndexCount] = (ushort)integerIndexData[i]; + } + + currentIndexCount += integerIndexData.Length; + } + } + } + + indexBuffer.SetData(indices); + } + } + + return new ModelInfo( + [ + new GeometryData(new Geometry(vertexBuffer, indexBuffer), Matrix.Identity, textures.ToArray()) + ]); + } + + private static uint GetMask(VertexElement[] elements) + { + uint mask = 0; + + for (int i = 0; i < elements.Length; i++) + { + mask |= (uint)((1 << (int)elements[i].VertexElementUsage)); + } + + return mask; + } + + private static void Sum(Span data, VertexDeclaration declaration, ref Vector3 sum) + { + var positionElement = + declaration.GetVertexElements().First(e => e.VertexElementUsage == VertexElementUsage.Position); + + int dataOffset = 0; + for (int i = 0; i < data.Length; i += declaration.VertexStride) + { + sum += MemoryMarshal.AsRef(data.Slice(dataOffset + positionElement.Offset)); + } + } + + private static void CopyTo(Span from, Span destination, in Matrix matrix, VertexDeclaration declaration) + { + var elements = declaration.GetVertexElements().AsSpan(); + elements.Sort((a, b) + => a.VertexElementUsage.CompareTo(b.VertexElementUsage)); + + uint mask = GetMask(declaration.GetVertexElements()); + + int dataOffset = 0; + int elementIndex = 0; + for (int i = 0; i < destination.Length; i++) + { + if ((mask & (1 << (int)VertexElementUsage.Position)) != 0) + { + destination[i].Position = Vector3.Transform( + MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)), matrix); + elementIndex++; + } + + if ((mask & (1 << (int)VertexElementUsage.Color)) != 0) + { + destination[i].Color = MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)); + elementIndex++; + } + + if ((mask & (1 << (int)VertexElementUsage.TextureCoordinate)) != 0) + { + destination[i].TextureCoordinate = MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)); + elementIndex++; + } + + if ((mask & (1 << (int)VertexElementUsage.Normal)) != 0) + { + destination[i].Normal = Vector3.TransformNormal( + MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)), matrix); + } + + dataOffset += declaration.VertexStride; + elementIndex = 0; + } + } +} \ No newline at end of file diff --git a/TGC.MonoGame.Samples/Models/ModelInfo.cs b/TGC.MonoGame.Samples/Models/ModelInfo.cs new file mode 100644 index 0000000..5d78faa --- /dev/null +++ b/TGC.MonoGame.Samples/Models/ModelInfo.cs @@ -0,0 +1,13 @@ + + +namespace TGC.MonoGame.Samples.Models; + +public class ModelInfo +{ + public GeometryData[] GeometryData { get; private set; } + + internal ModelInfo(GeometryData[] geometryData) + { + GeometryData = geometryData; + } +} diff --git a/TGC.MonoGame.Samples/Samples/Models/Sponza.cs b/TGC.MonoGame.Samples/Samples/Models/Sponza.cs new file mode 100644 index 0000000..43ee3c0 --- /dev/null +++ b/TGC.MonoGame.Samples/Samples/Models/Sponza.cs @@ -0,0 +1,122 @@ +using System; +using System.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using TGC.MonoGame.Samples.Cameras; +using TGC.MonoGame.Samples.Viewer; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Graphics; +using TGC.MonoGame.Samples.Models; + +namespace TGC.MonoGame.Samples.Samples.Models; + + +public class Sponza : TGCSample +{ + /// + /// A camera to draw geometry + /// + private Camera _camera; + + /// + /// The model to draw using both quaternions and euler rotations + /// + private Model _model; + + /// + /// An effect to draw the meshes. + /// + private Effect _effect; + + /// + /// Model info containing data + /// + private ModelInfo _info; + + public Sponza(TGCViewer game) : base(game) + { + Category = TGCSampleCategory.Models; + Name = "Sponza Model"; + Description = + "Shows how to draw a complex model using the new ModelExtensions interface"; + } + + public override void Initialize() + { + Game.Background = Color.Black; + + var size = GraphicsDevice.Viewport.Bounds.Size; + size.X /= 2; + size.Y /= 2; + _camera = new FreeCamera(GraphicsDevice.Viewport.AspectRatio, new Vector3(0, 5, 0), size); + _camera.BuildProjection( + GraphicsDevice.Viewport.AspectRatio, 0.1f, 100000f, + MathF.PI / 3f); + + base.Initialize(); + } + + protected override void LoadContent() + { + // Load the chair model + _model = Game.Content.Load(ContentFolder3D + "sponza/Sponza"); + + _effect = Game.Content.Load(ContentFolderEffects + "BasicTexture"); + + // This copies the Vertex/Index Buffers into new Geometries. + // They must be disposed later + _info = ModelExtensions.GetCenteredXZ(_model); + + // Set the depth state to default + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + + base.LoadContent(); + } + + public override void Update(GameTime gameTime) + { + // Update Camera and Gizmos + _camera.Update(gameTime); + + Game.Gizmos.UpdateViewProjection(_camera.View, _camera.Projection); + + base.Update(gameTime); + } + + public override void Draw(GameTime gameTime) + { + Game.Background = Color.CornflowerBlue; + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + GraphicsDevice.BlendState = BlendState.Opaque; + + var scaleMatrix = Matrix.CreateScale(0.01f); + _effect.Parameters["View"].SetValue(_camera.View); + _effect.Parameters["Projection"].SetValue(_camera.Projection); + + for (int index = 0; index < _info.GeometryData.Length; index++) + { + ref var geometryData = ref _info.GeometryData[index]; + + if (geometryData.Textures.Length > 0) + { + _effect.Parameters["ModelTexture"].SetValue(geometryData.Textures[0]); + } + + _effect.Parameters["World"].SetValue(geometryData.RelativeMatrix * scaleMatrix); + + geometryData.Geometry.Draw(_effect); + } + + base.Draw(gameTime); + } + + protected override void UnloadContent() + { + // Need to dispose the GeometryData because GetCenteredXZ copies the buffers + foreach (var geometryData in _info.GeometryData) + { + geometryData.Geometry.Dispose(); + } + } +} + diff --git a/TGC.MonoGame.Samples/TGC.MonoGame.Samples.csproj b/TGC.MonoGame.Samples/TGC.MonoGame.Samples.csproj index 3a89c59..a4159f5 100644 --- a/TGC.MonoGame.Samples/TGC.MonoGame.Samples.csproj +++ b/TGC.MonoGame.Samples/TGC.MonoGame.Samples.csproj @@ -32,6 +32,9 @@ Always + + + From 20b3daaaf54959cf3c8f514f56f0d8c9a5e73d2e Mon Sep 17 00:00:00 2001 From: AVinitzca Date: Tue, 9 Sep 2025 18:35:56 -0300 Subject: [PATCH 02/10] Added docs and made ModelInfo disposable --- .../Models/ModelExtensions.cs | 40 ++++++++++++++++++- TGC.MonoGame.Samples/Models/ModelInfo.cs | 10 ++++- TGC.MonoGame.Samples/Samples/Models/Sponza.cs | 16 +++----- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/TGC.MonoGame.Samples/Models/ModelExtensions.cs b/TGC.MonoGame.Samples/Models/ModelExtensions.cs index 8634e07..c375e5e 100644 --- a/TGC.MonoGame.Samples/Models/ModelExtensions.cs +++ b/TGC.MonoGame.Samples/Models/ModelExtensions.cs @@ -9,6 +9,12 @@ namespace TGC.MonoGame.Samples.Models; public static class ModelExtensions { + /// + /// Gets for a MonoGame . + /// Lists simplified matrices, textures and geometry for a group of meshes that live inside the model. + /// + /// The model to get the info from + /// A collection of associated information to each mesh of the model public static ModelInfo Get(Model model) { int geometryCount = 0; @@ -41,7 +47,13 @@ public static ModelInfo Get(Model model) return new ModelInfo(geometryData); } - public static ModelInfo GetMerged(Microsoft.Xna.Framework.Graphics.Model model) + /// + /// Gets a with a single entry for a MonoGame . + /// Merges meshes and parts that live inside the model according to their matrices. + /// + /// The model to get the merged model info from + /// A model info with a single instance of merged geometry + public static ModelInfo GetMerged(Model model) { var absoluteMatrices = new Matrix[model.Bones.Count]; model.CopyAbsoluteBoneTransformsTo(absoluteMatrices); @@ -218,6 +230,14 @@ public static ModelInfo GetMerged(Microsoft.Xna.Framework.Graphics.Model model) ]); } + /// + /// Gets for a MonoGame . + /// Lists simplified matrices, textures and geometry for a group of meshes that live inside the model. + /// Centers all geometry based on the averaged centered position of all meshes inside the model provided. + /// This method modifies the geometry to center all meshes + /// + /// The model to get the info from + /// A collection of associated information to each mesh of the model with geometry centered public static ModelInfo GetCentered(Model model) { int geometryCount = 0; @@ -350,6 +370,13 @@ public static ModelInfo GetCentered(Model model) return new ModelInfo(geometryData); } + /// + /// Gets for a MonoGame . + /// Lists simplified matrices, textures and geometry for a group of meshes that live inside the model. + /// Centers all geometry based on the averaged centered position across the XZ plane of all meshes inside the model provided. + /// + /// The model to get the info from + /// A collection of associated information to each mesh of the model with geometry centered across the XZ plane public static ModelInfo GetCenteredXZ(Model model) { int geometryCount = 0; @@ -482,7 +509,16 @@ public static ModelInfo GetCenteredXZ(Model model) return new ModelInfo(geometryData); } - public static ModelInfo GetMergedCentered(Microsoft.Xna.Framework.Graphics.Model model) + /// + /// Gets for a MonoGame . + /// Lists simplified matrices, textures and geometry for a group of meshes that live inside the model. + /// Centers all geometry based on the averaged centered position of all meshes inside the model provided. + /// Merges meshes and parts that live inside the model according to their matrices. + /// This method modifies the geometry to center all meshes + /// + /// The model to get the merged info from + /// A model info with a single instance of merged geometry + public static ModelInfo GetMergedCentered(Model model) { var absoluteMatrices = new Matrix[model.Bones.Count]; model.CopyAbsoluteBoneTransformsTo(absoluteMatrices); diff --git a/TGC.MonoGame.Samples/Models/ModelInfo.cs b/TGC.MonoGame.Samples/Models/ModelInfo.cs index 5d78faa..f3c7e7f 100644 --- a/TGC.MonoGame.Samples/Models/ModelInfo.cs +++ b/TGC.MonoGame.Samples/Models/ModelInfo.cs @@ -1,8 +1,8 @@ - +using System; namespace TGC.MonoGame.Samples.Models; -public class ModelInfo +public class ModelInfo : IDisposable { public GeometryData[] GeometryData { get; private set; } @@ -10,4 +10,10 @@ internal ModelInfo(GeometryData[] geometryData) { GeometryData = geometryData; } + + public void Dispose() + { + foreach(var geometryData in GeometryData) + geometryData.Geometry.Dispose(); + } } diff --git a/TGC.MonoGame.Samples/Samples/Models/Sponza.cs b/TGC.MonoGame.Samples/Samples/Models/Sponza.cs index 43ee3c0..0d8ddb5 100644 --- a/TGC.MonoGame.Samples/Samples/Models/Sponza.cs +++ b/TGC.MonoGame.Samples/Samples/Models/Sponza.cs @@ -1,16 +1,15 @@ using System; -using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TGC.MonoGame.Samples.Cameras; using TGC.MonoGame.Samples.Viewer; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Graphics; using TGC.MonoGame.Samples.Models; namespace TGC.MonoGame.Samples.Samples.Models; - +/// +/// A sample showing how to draw the sponza cathedral. +/// public class Sponza : TGCSample { /// @@ -29,7 +28,7 @@ public class Sponza : TGCSample private Effect _effect; /// - /// Model info containing data + /// Model info that facilitates rendering. /// private ModelInfo _info; @@ -112,11 +111,8 @@ public override void Draw(GameTime gameTime) protected override void UnloadContent() { - // Need to dispose the GeometryData because GetCenteredXZ copies the buffers - foreach (var geometryData in _info.GeometryData) - { - geometryData.Geometry.Dispose(); - } + // Need to dispose the model info + _info.Dispose(); } } From 5c92de8b9353f2d8ed14e220aa1e0e8f81596230 Mon Sep 17 00:00:00 2001 From: AVinitzca Date: Sun, 12 Oct 2025 23:53:43 -0300 Subject: [PATCH 03/10] Fixed the Merged methods --- .../Models/ModelExtensions.cs | 412 +++++++----------- TGC.MonoGame.Samples/Samples/Models/Sponza.cs | 2 +- 2 files changed, 155 insertions(+), 259 deletions(-) diff --git a/TGC.MonoGame.Samples/Models/ModelExtensions.cs b/TGC.MonoGame.Samples/Models/ModelExtensions.cs index c375e5e..36edb82 100644 --- a/TGC.MonoGame.Samples/Models/ModelExtensions.cs +++ b/TGC.MonoGame.Samples/Models/ModelExtensions.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using Vector2 = Microsoft.Xna.Framework.Vector2; +using Vector3 = Microsoft.Xna.Framework.Vector3; namespace TGC.MonoGame.Samples.Models; @@ -80,149 +83,10 @@ public static ModelInfo GetMerged(Model model) } } - VertexBuffer vertexBuffer; - { - // Extract vertices - var vertices = new VertexPositionColorNormalTexture[vertexCount]; - - Dictionary vertexData = new(); - - foreach (var mesh in model.Meshes) - { - var transform = absoluteMatrices[mesh.ParentBone.Index]; - - foreach (var part in mesh.MeshParts) - { - var partVertexBuffer = part.VertexBuffer; - - if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) - { - var declaration = partVertexBuffer.VertexDeclaration; - var vertexSize = declaration.VertexStride; - bufferData = new byte[vertexSize * partVertexBuffer.VertexCount]; - partVertexBuffer.GetData(bufferData); - vertexData.Add(partVertexBuffer, bufferData); - } - - int offsetByStride = part.VertexOffset * - part.VertexBuffer.VertexDeclaration.VertexStride; - - int numVerticesByStride = part.NumVertices * - part.VertexBuffer.VertexDeclaration.VertexStride; - - CopyTo(bufferData.AsSpan().Slice(offsetByStride, numVerticesByStride), - vertices.AsSpan().Slice(part.VertexOffset, part.NumVertices), - transform, partVertexBuffer.VertexDeclaration); - } - } - - vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), vertices.Length, BufferUsage.None); - vertexBuffer.SetData(vertices); - } - - - IndexBuffer indexBuffer = new IndexBuffer(device, - vertexCount > ushort.MaxValue ? IndexElementSize.ThirtyTwoBits : IndexElementSize.SixteenBits, - indexCount, BufferUsage.None); + Dictionary vertexData = new(); - { - // Extract indices - - Dictionary indexData = new(); - int currentIndexCount = 0; - - if (vertexCount > ushort.MaxValue) - { - var indices = new uint[indexCount]; - - foreach (var mesh in model.Meshes) - { - foreach (var part in mesh.MeshParts) - { - var partIndexBuffer = part.IndexBuffer; - - int stride = partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; - - if (!indexData.TryGetValue(partIndexBuffer, out var bufferData)) - { - var data = new byte[partIndexBuffer.IndexCount * stride]; - partIndexBuffer.GetData(data); - bufferData = data; - indexData.Add(partIndexBuffer, data); - } - - if (partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) - { - var shortIndexData = MemoryMarshal.Cast(bufferData.AsSpan() - .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); - - for (int i = 0; i < shortIndexData.Length; i++) - { - indices[i + currentIndexCount] = shortIndexData[i]; - } - - currentIndexCount += shortIndexData.Length; - } - else - { - var integerIndexData = MemoryMarshal.Cast(bufferData.AsSpan() - .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); - - integerIndexData.CopyTo(indices.AsSpan().Slice(currentIndexCount)); - currentIndexCount += integerIndexData.Length; - } - } - } - - indexBuffer.SetData(indices); - } - else - { - var indices = new ushort[indexCount]; - - foreach (var mesh in model.Meshes) - { - foreach (var part in mesh.MeshParts) - { - var partIndexBuffer = part.IndexBuffer; - - int stride = partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; - - if (!indexData.TryGetValue(partIndexBuffer, out var bufferData)) - { - var data = new byte[partIndexBuffer.IndexCount * stride]; - partIndexBuffer.GetData(data); - bufferData = data; - indexData.Add(partIndexBuffer, data); - } - - if (partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) - { - var shortIndexData = MemoryMarshal.Cast(bufferData.AsSpan() - .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); - - shortIndexData.CopyTo(indices.AsSpan().Slice(currentIndexCount)); - - currentIndexCount += shortIndexData.Length; - } - else - { - var integerIndexData = MemoryMarshal.Cast(bufferData.AsSpan() - .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); - - for (int i = 0; i < integerIndexData.Length; i++) - { - indices[i + currentIndexCount] = (ushort)integerIndexData[i]; - } - - currentIndexCount += integerIndexData.Length; - } - } - } - - indexBuffer.SetData(indices); - } - } + GetMergedBuffers(model, vertexCount, indexCount, absoluteMatrices, Matrix.Identity, + vertexData, device, out var vertexBuffer, out var indexBuffer); return new ModelInfo( [ @@ -582,147 +446,141 @@ public static ModelInfo GetMergedCentered(Model model) var centeringTransform = Matrix.CreateTranslation(-sum); - VertexBuffer vertexBuffer; + // Extract textures, vertices and indices + foreach (var mesh in model.Meshes) { - // Extract vertices - var vertices = new VertexPositionColorNormalTexture[vertexCount]; - - - foreach (var mesh in model.Meshes) + foreach (var part in mesh.MeshParts) { - var transform = absoluteMatrices[mesh.ParentBone.Index] * centeringTransform; + vertexCount += part.NumVertices; + indexCount += part.PrimitiveCount * 3; + + var mainTexture = ((BasicEffect)part.Effect).Texture; - foreach (var part in mesh.MeshParts) - { - var partVertexBuffer = part.VertexBuffer; + if (mainTexture != null) + textures.Add(mainTexture); + } + } - var data = vertexData[partVertexBuffer]; + GetMergedBuffers(model, vertexCount, indexCount, absoluteMatrices, centeringTransform, + vertexData, device, out var vertexBuffer, out var indexBuffer); - int offsetByStride = part.VertexOffset * - part.VertexBuffer.VertexDeclaration.VertexStride; - - int numVerticesByStride = part.NumVertices * - part.VertexBuffer.VertexDeclaration.VertexStride; + return new ModelInfo( + [ + new GeometryData(new Geometry(vertexBuffer, indexBuffer), Matrix.Identity, textures.ToArray()) + ]); + } - CopyTo(data.AsSpan().Slice(offsetByStride, numVerticesByStride), - vertices.AsSpan().Slice(part.VertexOffset, part.NumVertices), - transform, partVertexBuffer.VertexDeclaration); - } - } + private static void GetMergedBuffers(Model model, int vertexCount, int indexCount, + Matrix[] absoluteMatrices, in Matrix absoluteTransform, + Dictionary vertexData, + GraphicsDevice device, out VertexBuffer vertexBuffer, out IndexBuffer indexBuffer) + { + var vertices = new VertexPositionColorNormalTexture[vertexCount]; + + Dictionary indexData = new(); + + bool largeIndices = vertexCount > ushort.MaxValue; + + ushort[] indicesShort = null; + uint[] indicesInt = null; - vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), vertices.Length, BufferUsage.None); - vertexBuffer.SetData(vertices); - } + if(largeIndices) + indicesInt = new uint[indexCount]; + else + indicesShort = new ushort[indexCount]; - IndexBuffer indexBuffer = new IndexBuffer(device, - vertexCount > ushort.MaxValue ? IndexElementSize.ThirtyTwoBits : IndexElementSize.SixteenBits, - indexCount, BufferUsage.None); + int currentIndex = 0; + int vertexOffset = 0; + foreach (var mesh in model.Meshes) { - // Extract indices - - Dictionary indexData = new(); - int currentIndexCount = 0; + var transform = absoluteMatrices[mesh.ParentBone.Index]; - if (vertexCount > ushort.MaxValue) + foreach (var part in mesh.MeshParts) { - var indices = new uint[indexCount]; - - foreach (var mesh in model.Meshes) + var partVertexBuffer = part.VertexBuffer; + + if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) { - foreach (var part in mesh.MeshParts) - { - var partIndexBuffer = part.IndexBuffer; + var declaration = partVertexBuffer.VertexDeclaration; + var vertexSize = declaration.VertexStride; + bufferData = new byte[vertexSize * partVertexBuffer.VertexCount]; + partVertexBuffer.GetData(bufferData); + vertexData.Add(partVertexBuffer, bufferData); + } - int stride = partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; - - if (!indexData.TryGetValue(partIndexBuffer, out var bufferData)) - { - var data = new byte[partIndexBuffer.IndexCount * stride]; - partIndexBuffer.GetData(data); - bufferData = data; - indexData.Add(partIndexBuffer, data); - } - - if (partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) - { - var shortIndexData = MemoryMarshal.Cast(bufferData.AsSpan() - .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); - - for (int i = 0; i < shortIndexData.Length; i++) - { - indices[i + currentIndexCount] = shortIndexData[i]; - } - - currentIndexCount += shortIndexData.Length; - } - else - { - var integerIndexData = MemoryMarshal.Cast(bufferData.AsSpan() - .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); - - integerIndexData.CopyTo(indices.AsSpan().Slice(currentIndexCount)); - currentIndexCount += integerIndexData.Length; - } - } + int indexStride = part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; + if (!indexData.TryGetValue(part.IndexBuffer, out var indexBufferData)) + { + indexBufferData = new byte[part.IndexBuffer.IndexCount * indexStride]; + part.IndexBuffer.GetData(indexBufferData); + indexData.Add(part.IndexBuffer, indexBufferData); } + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; - indexBuffer.SetData(indices); - } - else - { - var indices = new ushort[indexCount]; + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + CopyTo(bufferData.AsSpan().Slice(offsetByStride, numVerticesByStride), + vertices.AsSpan().Slice(vertexOffset, part.NumVertices), + transform * absoluteTransform, partVertexBuffer.VertexDeclaration); + + int currentIndexCount = part.PrimitiveCount * 3; + + int startIndexStride = part.StartIndex * indexStride; + int countStride = currentIndexCount * indexStride; - foreach (var mesh in model.Meshes) + if (largeIndices) { - foreach (var part in mesh.MeshParts) + if (part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) { - var partIndexBuffer = part.IndexBuffer; - - int stride = partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; - - if (!indexData.TryGetValue(partIndexBuffer, out var bufferData)) - { - var data = new byte[partIndexBuffer.IndexCount * stride]; - partIndexBuffer.GetData(data); - bufferData = data; - indexData.Add(partIndexBuffer, data); - } - - if (partIndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) - { - var shortIndexData = MemoryMarshal.Cast(bufferData.AsSpan() - .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); - - shortIndexData.CopyTo(indices.AsSpan().Slice(currentIndexCount)); - - currentIndexCount += shortIndexData.Length; - } - else - { - var integerIndexData = MemoryMarshal.Cast(bufferData.AsSpan() - .Slice(part.StartIndex * stride, part.PrimitiveCount * 3 * stride)); - - for (int i = 0; i < integerIndexData.Length; i++) - { - indices[i + currentIndexCount] = (ushort)integerIndexData[i]; - } - - currentIndexCount += integerIndexData.Length; - } + CopyTo(indexBufferData.AsSpan().Slice(startIndexStride, countStride), + indicesInt.AsSpan().Slice(currentIndex, currentIndexCount), vertexOffset, sizeof(ushort)); + } + else + { + CopyTo(indexBufferData.AsSpan().Slice(startIndexStride, countStride), + indicesInt.AsSpan().Slice(currentIndex, currentIndexCount), vertexOffset, sizeof(uint)); } } - - indexBuffer.SetData(indices); + else + { + if (part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) + { + CopyTo(indexBufferData.AsSpan().Slice(startIndexStride, countStride), + indicesShort.AsSpan().Slice(currentIndex, currentIndexCount), vertexOffset, sizeof(ushort)); + } + else + { + CopyTo(indexBufferData.AsSpan().Slice(startIndexStride, countStride), + indicesShort.AsSpan().Slice(currentIndex, currentIndexCount), vertexOffset, sizeof(uint)); + } + } + + currentIndex += currentIndexCount; + vertexOffset += part.NumVertices; } } - return new ModelInfo( - [ - new GeometryData(new Geometry(vertexBuffer, indexBuffer), Matrix.Identity, textures.ToArray()) - ]); + vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), vertices.Length, BufferUsage.None); + vertexBuffer.SetData(vertices); + + indexBuffer = new IndexBuffer(device, + largeIndices ? IndexElementSize.ThirtyTwoBits : IndexElementSize.SixteenBits, + indexCount, BufferUsage.None); + + if (largeIndices) + { + indexBuffer.SetData(indicesInt); + } + else + { + indexBuffer.SetData(indicesShort); + } } - + private static uint GetMask(VertexElement[] elements) { uint mask = 0; @@ -746,6 +604,44 @@ private static void Sum(Span data, VertexDeclaration declaration, ref Vect sum += MemoryMarshal.AsRef(data.Slice(dataOffset + positionElement.Offset)); } } + + private static void CopyTo( + ReadOnlySpan sourceBytes, + Span destination, + int vertexOffset, + int stride) + where TFrom : unmanaged + where TTo : unmanaged + { + if (vertexOffset == 0 && stride == Unsafe.SizeOf() && typeof(TFrom) == typeof(TTo)) + { + MemoryMarshal.Cast(sourceBytes).CopyTo(destination); + return; + } + + int elementCount = sourceBytes.Length / stride; + for (int i = 0; i < elementCount; i++) + { + TFrom src = MemoryMarshal.Read(sourceBytes.Slice(i * stride)); + + uint value = typeof(TFrom) == typeof(ushort) + ? (uint)Unsafe.As(ref src) + : Unsafe.As(ref src); + + value += (uint)vertexOffset; + + if (typeof(TTo) == typeof(ushort)) + { + ushort dst = (ushort)value; + destination[i] = Unsafe.As(ref dst); + } + else + { + uint dst = value; + destination[i] = Unsafe.As(ref dst); + } + } + } private static void CopyTo(Span from, Span destination, in Matrix matrix, VertexDeclaration declaration) { diff --git a/TGC.MonoGame.Samples/Samples/Models/Sponza.cs b/TGC.MonoGame.Samples/Samples/Models/Sponza.cs index 0d8ddb5..818dc18 100644 --- a/TGC.MonoGame.Samples/Samples/Models/Sponza.cs +++ b/TGC.MonoGame.Samples/Samples/Models/Sponza.cs @@ -62,7 +62,7 @@ protected override void LoadContent() _effect = Game.Content.Load(ContentFolderEffects + "BasicTexture"); - // This copies the Vertex/Index Buffers into new Geometries. + // This could copy the Vertex/Index Buffers into new Geometries. // They must be disposed later _info = ModelExtensions.GetCenteredXZ(_model); From 03348057baa4f34411278f00ee297c710b90cd3f Mon Sep 17 00:00:00 2001 From: AVinitzca Date: Tue, 14 Oct 2025 21:59:34 -0300 Subject: [PATCH 04/10] Fixed codacy issues --- TGC.MonoGame.Samples/Models/Geometry.cs | 12 +- .../Models/ModelExtensions.cs | 380 ++++++++---------- TGC.MonoGame.Samples/Models/ModelInfo.cs | 2 + TGC.MonoGame.Samples/Samples/Models/Sponza.cs | 2 +- 4 files changed, 178 insertions(+), 218 deletions(-) diff --git a/TGC.MonoGame.Samples/Models/Geometry.cs b/TGC.MonoGame.Samples/Models/Geometry.cs index f96e417..b30e2e4 100644 --- a/TGC.MonoGame.Samples/Models/Geometry.cs +++ b/TGC.MonoGame.Samples/Models/Geometry.cs @@ -15,8 +15,6 @@ public class Geometry : IDisposable internal int PrimitiveCount; - public int VertexCount; - internal bool OwnsVertexBuffer = false; internal bool OwnsIndexBuffer = false; @@ -37,14 +35,12 @@ internal Geometry(VertexBuffer vertexBuffer, IndexBuffer indexBuffer) VertexOffset = 0; StartIndex = 0; PrimitiveCount = indexBuffer.IndexCount / 3; - VertexCount = vertexBuffer.VertexCount; OwnsVertexBuffer = true; OwnsIndexBuffer = true; } private Geometry(ModelMeshPart part) { - VertexCount = part.NumVertices; VertexBuffer = part.VertexBuffer; IndexBuffer = part.IndexBuffer; VertexOffset = part.VertexOffset; @@ -72,10 +68,14 @@ public void Draw(Effect effect) public void Dispose() { - if(OwnsVertexBuffer) + if (OwnsVertexBuffer) + { VertexBuffer.Dispose(); - + } + if (OwnsIndexBuffer) + { IndexBuffer.Dispose(); + } } } \ No newline at end of file diff --git a/TGC.MonoGame.Samples/Models/ModelExtensions.cs b/TGC.MonoGame.Samples/Models/ModelExtensions.cs index 36edb82..d15b978 100644 --- a/TGC.MonoGame.Samples/Models/ModelExtensions.cs +++ b/TGC.MonoGame.Samples/Models/ModelExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.Xna.Framework; @@ -120,79 +121,19 @@ public static ModelInfo GetCentered(Model model) var device = model.Meshes.First().Effects.First().GraphicsDevice; List vertexBuffers = new(); - - Dictionary vertexData = new(); - - { - Vector3 sum = Vector3.Zero; - - // Extract vertices - - int vertexBufferIndex = 0; - - foreach (var mesh in model.Meshes) - { - foreach (var part in mesh.MeshParts) - { - vertexCount += part.NumVertices; - - var partVertexBuffer = part.VertexBuffer; - - if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) - { - var declaration = partVertexBuffer.VertexDeclaration; - var vertexSize = declaration.VertexStride; - var data = new byte[vertexSize * partVertexBuffer.VertexCount]; - partVertexBuffer.GetData(data); - - bufferData.Index = vertexBufferIndex; - bufferData.Data = data; - bufferData.Vertices = new VertexPositionColorNormalTexture[partVertexBuffer.VertexCount]; - vertexData.Add(partVertexBuffer, bufferData); - - vertexBufferIndex++; - } - - int offsetByStride = part.VertexOffset * - part.VertexBuffer.VertexDeclaration.VertexStride; - - int numVerticesByStride = part.NumVertices * - part.VertexBuffer.VertexDeclaration.VertexStride; - - Sum(bufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), partVertexBuffer.VertexDeclaration, ref sum); - } - } - - sum /= vertexCount; - - var transform = Matrix.CreateTranslation(-sum); - - foreach (var mesh in model.Meshes) - { - foreach (var part in mesh.MeshParts) - { - var partVertexBuffer = part.VertexBuffer; - var vertexBufferData = vertexData[partVertexBuffer]; + ExtractVertexDataAndSum(model, vertexCount, out var vertexData, out Vector3 sum); + sum /= vertexCount; - int offsetByStride = part.VertexOffset * - part.VertexBuffer.VertexDeclaration.VertexStride; - - int numVerticesByStride = part.NumVertices * - part.VertexBuffer.VertexDeclaration.VertexStride; + var transform = Matrix.CreateTranslation(-sum); - CopyTo(vertexBufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), - vertexBufferData.Vertices.AsSpan().Slice(part.VertexOffset, part.NumVertices), - transform, partVertexBuffer.VertexDeclaration); - } - } - - foreach (var data in vertexData.Values) - { - var vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), data.Vertices.Length, BufferUsage.None); - vertexBuffer.SetData(data.Vertices); - vertexBuffers.Add(vertexBuffer); - } + CopyVertexBuffersIntoSingleBuffer(model, vertexData, transform); + + foreach (var data in vertexData.Values) + { + var vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), data.Vertices.Length, BufferUsage.None); + vertexBuffer.SetData(data.Vertices); + vertexBuffers.Add(vertexBuffer); } HashSet assignedVertexBuffers = new(); @@ -216,7 +157,6 @@ public static ModelInfo GetCentered(Model model) { VertexBuffer = vertexBuffers[vertexData[part.VertexBuffer].Index], PrimitiveCount = part.PrimitiveCount, - VertexCount = part.NumVertices, IndexBuffer = part.IndexBuffer, StartIndex = part.StartIndex, VertexOffset = part.VertexOffset, @@ -233,7 +173,30 @@ public static ModelInfo GetCentered(Model model) return new ModelInfo(geometryData); } - + + private static void CopyVertexBuffersIntoSingleBuffer(Model model, Dictionary vertexData, Matrix transform) + { + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + var partVertexBuffer = part.VertexBuffer; + + var vertexBufferData = vertexData[partVertexBuffer]; + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; + + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + CopyTo(vertexBufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), + vertexBufferData.Vertices.AsSpan().Slice(part.VertexOffset, part.NumVertices), + transform, partVertexBuffer.VertexDeclaration); + } + } + } + /// /// Gets for a MonoGame . /// Lists simplified matrices, textures and geometry for a group of meshes that live inside the model. @@ -260,78 +223,17 @@ public static ModelInfo GetCenteredXZ(Model model) List vertexBuffers = new(); - Dictionary vertexData = new(); - - { - Vector3 sum = Vector3.Zero; - - // Extract vertices - - int vertexBufferIndex = 0; - - foreach (var mesh in model.Meshes) - { - foreach (var part in mesh.MeshParts) - { - vertexCount += part.NumVertices; - - var partVertexBuffer = part.VertexBuffer; - - if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) - { - var declaration = partVertexBuffer.VertexDeclaration; - var vertexSize = declaration.VertexStride; - var data = new byte[vertexSize * partVertexBuffer.VertexCount]; - partVertexBuffer.GetData(data); - - bufferData.Index = vertexBufferIndex; - bufferData.Data = data; - bufferData.Vertices = new VertexPositionColorNormalTexture[partVertexBuffer.VertexCount]; - vertexData.Add(partVertexBuffer, bufferData); - - vertexBufferIndex++; - } - - int offsetByStride = part.VertexOffset * - part.VertexBuffer.VertexDeclaration.VertexStride; - - int numVerticesByStride = part.NumVertices * - part.VertexBuffer.VertexDeclaration.VertexStride; + ExtractVertexDataAndSum(model, vertexCount, out var vertexData, out Vector3 sum); - Sum(bufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), partVertexBuffer.VertexDeclaration, ref sum); - } - } - - sum /= vertexCount; - - var transform = Matrix.CreateTranslation(-sum.X, 0f, -sum.Z); - - foreach (var mesh in model.Meshes) - { - foreach (var part in mesh.MeshParts) - { - var partVertexBuffer = part.VertexBuffer; - - var vertexBufferData = vertexData[partVertexBuffer]; - - int offsetByStride = part.VertexOffset * - part.VertexBuffer.VertexDeclaration.VertexStride; - - int numVerticesByStride = part.NumVertices * - part.VertexBuffer.VertexDeclaration.VertexStride; + var transform = Matrix.CreateTranslation(-sum.X, 0f, -sum.Z); - CopyTo(vertexBufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), - vertexBufferData.Vertices.AsSpan().Slice(part.VertexOffset, part.NumVertices), - transform, partVertexBuffer.VertexDeclaration); - } - } - - foreach (var data in vertexData.Values) - { - var vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), data.Vertices.Length, BufferUsage.None); - vertexBuffer.SetData(data.Vertices); - vertexBuffers.Add(vertexBuffer); - } + CopyVertexBuffersIntoSingleBuffer(model, vertexData, transform); + + foreach (var data in vertexData.Values) + { + var vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), data.Vertices.Length, BufferUsage.None); + vertexBuffer.SetData(data.Vertices); + vertexBuffers.Add(vertexBuffer); } HashSet assignedVertexBuffers = new(); @@ -343,19 +245,13 @@ public static ModelInfo GetCenteredXZ(Model model) { var mainTexture = ((BasicEffect)part.Effect).Texture; - Texture[] textures; - - if (mainTexture != null) - textures = [mainTexture]; - else - textures = []; + Texture[] textures = mainTexture != null ? [mainTexture] : []; geometryData[geometryIndex] = new GeometryData( new Geometry() { VertexBuffer = vertexBuffers[vertexData[part.VertexBuffer].Index], PrimitiveCount = part.PrimitiveCount, - VertexCount = part.NumVertices, IndexBuffer = part.IndexBuffer, StartIndex = part.StartIndex, VertexOffset = part.VertexOffset, @@ -373,6 +269,52 @@ public static ModelInfo GetCenteredXZ(Model model) return new ModelInfo(geometryData); } + private static void ExtractVertexDataAndSum(Model model, int vertexCount, out Dictionary vertexBufferData, out Vector3 sum) + { + vertexBufferData = new(); + sum = Vector3.Zero; + + // Extract vertices + + int vertexBufferIndex = 0; + + foreach (var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + vertexCount += part.NumVertices; + + var partVertexBuffer = part.VertexBuffer; + + if (!vertexBufferData.TryGetValue(partVertexBuffer, out var bufferData)) + { + var declaration = partVertexBuffer.VertexDeclaration; + var vertexSize = declaration.VertexStride; + var data = new byte[vertexSize * partVertexBuffer.VertexCount]; + partVertexBuffer.GetData(data); + + bufferData.Index = vertexBufferIndex; + bufferData.Data = data; + bufferData.Vertices = new VertexPositionColorNormalTexture[partVertexBuffer.VertexCount]; + vertexBufferData.Add(partVertexBuffer, bufferData); + + vertexBufferIndex++; + } + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; + + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + Sum(bufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), partVertexBuffer.VertexDeclaration, ref sum); + } + } + + sum /= vertexCount; + } + /// /// Gets for a MonoGame . /// Lists simplified matrices, textures and geometry for a group of meshes that live inside the model. @@ -481,13 +423,7 @@ private static void GetMergedBuffers(Model model, int vertexCount, int indexCoun bool largeIndices = vertexCount > ushort.MaxValue; - ushort[] indicesShort = null; - uint[] indicesInt = null; - - if(largeIndices) - indicesInt = new uint[indexCount]; - else - indicesShort = new ushort[indexCount]; + byte[] indicesAsBytes = new byte[indexCount * (largeIndices ? sizeof(uint) : sizeof(ushort))]; int currentIndex = 0; int vertexOffset = 0; @@ -528,38 +464,20 @@ private static void GetMergedBuffers(Model model, int vertexCount, int indexCoun transform * absoluteTransform, partVertexBuffer.VertexDeclaration); int currentIndexCount = part.PrimitiveCount * 3; + int currentIndexStride = currentIndexCount * (largeIndices ? sizeof(uint) : sizeof(ushort)); - int startIndexStride = part.StartIndex * indexStride; - int countStride = currentIndexCount * indexStride; - if (largeIndices) { - if (part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) - { - CopyTo(indexBufferData.AsSpan().Slice(startIndexStride, countStride), - indicesInt.AsSpan().Slice(currentIndex, currentIndexCount), vertexOffset, sizeof(ushort)); - } - else - { - CopyTo(indexBufferData.AsSpan().Slice(startIndexStride, countStride), - indicesInt.AsSpan().Slice(currentIndex, currentIndexCount), vertexOffset, sizeof(uint)); - } + CopyIndexBuffer(part, indexBufferData.AsSpan(), + indicesAsBytes.AsSpan().Slice(currentIndex, currentIndexStride), vertexOffset); } else { - if (part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) - { - CopyTo(indexBufferData.AsSpan().Slice(startIndexStride, countStride), - indicesShort.AsSpan().Slice(currentIndex, currentIndexCount), vertexOffset, sizeof(ushort)); - } - else - { - CopyTo(indexBufferData.AsSpan().Slice(startIndexStride, countStride), - indicesShort.AsSpan().Slice(currentIndex, currentIndexCount), vertexOffset, sizeof(uint)); - } + CopyIndexBuffer(part, indexBufferData.AsSpan(), + indicesAsBytes.AsSpan().Slice(currentIndex, currentIndexStride), vertexOffset); } - currentIndex += currentIndexCount; + currentIndex += currentIndexStride; vertexOffset += part.NumVertices; } } @@ -571,14 +489,7 @@ private static void GetMergedBuffers(Model model, int vertexCount, int indexCoun largeIndices ? IndexElementSize.ThirtyTwoBits : IndexElementSize.SixteenBits, indexCount, BufferUsage.None); - if (largeIndices) - { - indexBuffer.SetData(indicesInt); - } - else - { - indexBuffer.SetData(indicesShort); - } + indexBuffer.SetData(indicesAsBytes); } private static uint GetMask(VertexElement[] elements) @@ -605,41 +516,88 @@ private static void Sum(Span data, VertexDeclaration declaration, ref Vect } } + private static void CopyIndexBuffer(ModelMeshPart part, ReadOnlySpan indexBufferData, + Span destination, int vertexOffset) + where TDestinationType : unmanaged, INumber + { + int indexStride = part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; + int currentIndexCount = part.PrimitiveCount * 3; + + int startIndexStride = part.StartIndex * indexStride; + int countStride = currentIndexCount * indexStride; + + if (part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) + { + AddAndCopy(indexBufferData.Slice(startIndexStride, countStride), + destination, vertexOffset); + } + else + { + AddAndCopy(indexBufferData.Slice(startIndexStride, countStride), + destination, vertexOffset); + } + } + + private static void AddAndCopy(ReadOnlySpan sourceBytes, + Span destinationBytes, int vertexOffset) + where TFrom : unmanaged, INumber + where TTo : unmanaged, INumber + { + if (vertexOffset == 0 && typeof(TFrom) == typeof(TTo)) + { + sourceBytes.CopyTo(destinationBytes); + return; + } + + var source = MemoryMarshal.Cast(sourceBytes); + var destination = MemoryMarshal.Cast(destinationBytes); + + var offsetConverted = TTo.CreateChecked(vertexOffset); + + for (int i = 0; i < source.Length; i++) + { + var converted = TTo.CreateChecked(source[i]); + destination[i] = converted + offsetConverted; + } + } + private static void CopyTo( ReadOnlySpan sourceBytes, - Span destination, - int vertexOffset, - int stride) + Span destination, + int vertexOffset) where TFrom : unmanaged where TTo : unmanaged { - if (vertexOffset == 0 && stride == Unsafe.SizeOf() && typeof(TFrom) == typeof(TTo)) + if (vertexOffset == 0 && typeof(TFrom) == typeof(TTo)) { - MemoryMarshal.Cast(sourceBytes).CopyTo(destination); + sourceBytes.CopyTo(destination); return; } - int elementCount = sourceBytes.Length / stride; - for (int i = 0; i < elementCount; i++) + int fromStride = Unsafe.SizeOf(); + int toStride = Unsafe.SizeOf(); + int i = 0; + int j = 0; + + if (fromStride == 2) { - TFrom src = MemoryMarshal.Read(sourceBytes.Slice(i * stride)); - - uint value = typeof(TFrom) == typeof(ushort) - ? (uint)Unsafe.As(ref src) - : Unsafe.As(ref src); - - value += (uint)vertexOffset; - - if (typeof(TTo) == typeof(ushort)) + for (; i < sourceBytes.Length; i += fromStride) { - ushort dst = (ushort)value; - destination[i] = Unsafe.As(ref dst); - } - else + BitConverter.GetBytes(BitConverter.ToUInt16(sourceBytes.Slice(i)) + vertexOffset).CopyTo(destination.Slice(j)); + j += toStride; + } + } + else + { + for (; i < sourceBytes.Length; i += fromStride) { - uint dst = value; - destination[i] = Unsafe.As(ref dst); - } + var asRef = MemoryMarshal.AsRef(sourceBytes.Slice(i)); + asRef += (uint)vertexOffset; + MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref asRef, 1)); + + BitConverter.GetBytes(BitConverter.ToUInt32(sourceBytes.Slice(i)) + vertexOffset).CopyTo(destination.Slice(j)); + j += toStride; + } } } diff --git a/TGC.MonoGame.Samples/Models/ModelInfo.cs b/TGC.MonoGame.Samples/Models/ModelInfo.cs index f3c7e7f..1dfc254 100644 --- a/TGC.MonoGame.Samples/Models/ModelInfo.cs +++ b/TGC.MonoGame.Samples/Models/ModelInfo.cs @@ -14,6 +14,8 @@ internal ModelInfo(GeometryData[] geometryData) public void Dispose() { foreach(var geometryData in GeometryData) + { geometryData.Geometry.Dispose(); + } } } diff --git a/TGC.MonoGame.Samples/Samples/Models/Sponza.cs b/TGC.MonoGame.Samples/Samples/Models/Sponza.cs index 818dc18..4acc900 100644 --- a/TGC.MonoGame.Samples/Samples/Models/Sponza.cs +++ b/TGC.MonoGame.Samples/Samples/Models/Sponza.cs @@ -49,7 +49,7 @@ public override void Initialize() size.Y /= 2; _camera = new FreeCamera(GraphicsDevice.Viewport.AspectRatio, new Vector3(0, 5, 0), size); _camera.BuildProjection( - GraphicsDevice.Viewport.AspectRatio, 0.1f, 100000f, + GraphicsDevice.Viewport.AspectRatio, 0.1f, 100_000f, MathF.PI / 3f); base.Initialize(); From 32609b6f0054c5c16384964b7563366ef54025e4 Mon Sep 17 00:00:00 2001 From: AVinitzca Date: Tue, 14 Oct 2025 23:43:52 -0300 Subject: [PATCH 05/10] Fixed yet another round of codacy warnings --- TGC.MonoGame.Samples/Models/Geometry.cs | 4 +- .../Models/ModelExtensions.cs | 70 +++++++++---------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/TGC.MonoGame.Samples/Models/Geometry.cs b/TGC.MonoGame.Samples/Models/Geometry.cs index b30e2e4..7443d50 100644 --- a/TGC.MonoGame.Samples/Models/Geometry.cs +++ b/TGC.MonoGame.Samples/Models/Geometry.cs @@ -15,9 +15,9 @@ public class Geometry : IDisposable internal int PrimitiveCount; - internal bool OwnsVertexBuffer = false; + internal bool OwnsVertexBuffer; - internal bool OwnsIndexBuffer = false; + internal bool OwnsIndexBuffer; internal static Geometry FromMeshPart(ModelMeshPart part) diff --git a/TGC.MonoGame.Samples/Models/ModelExtensions.cs b/TGC.MonoGame.Samples/Models/ModelExtensions.cs index d15b978..cb02ca0 100644 --- a/TGC.MonoGame.Samples/Models/ModelExtensions.cs +++ b/TGC.MonoGame.Samples/Models/ModelExtensions.cs @@ -86,8 +86,8 @@ public static ModelInfo GetMerged(Model model) Dictionary vertexData = new(); - GetMergedBuffers(model, vertexCount, indexCount, absoluteMatrices, Matrix.Identity, - vertexData, device, out var vertexBuffer, out var indexBuffer); + GetMergedBuffers(model, vertexCount, indexCount, Matrix.Identity, + vertexData, out var vertexBuffer, out var indexBuffer); return new ModelInfo( [ @@ -326,14 +326,9 @@ private static void ExtractVertexDataAndSum(Model model, int vertexCount, out Di /// A model info with a single instance of merged geometry public static ModelInfo GetMergedCentered(Model model) { - var absoluteMatrices = new Matrix[model.Bones.Count]; - model.CopyAbsoluteBoneTransformsTo(absoluteMatrices); - int vertexCount = 0; int indexCount = 0; - var device = model.Meshes.First().Effects.First().GraphicsDevice; - var textures = new List(); // Extract textures, vertices and indices @@ -403,8 +398,8 @@ public static ModelInfo GetMergedCentered(Model model) } } - GetMergedBuffers(model, vertexCount, indexCount, absoluteMatrices, centeringTransform, - vertexData, device, out var vertexBuffer, out var indexBuffer); + GetMergedBuffers(model, vertexCount, indexCount, centeringTransform, + vertexData, out var vertexBuffer, out var indexBuffer); return new ModelInfo( [ @@ -412,18 +407,21 @@ public static ModelInfo GetMergedCentered(Model model) ]); } - private static void GetMergedBuffers(Model model, int vertexCount, int indexCount, - Matrix[] absoluteMatrices, in Matrix absoluteTransform, - Dictionary vertexData, - GraphicsDevice device, out VertexBuffer vertexBuffer, out IndexBuffer indexBuffer) + private static void GetMergedBuffers(Model model, int vertexCount, int indexCount, in Matrix absoluteTransform, + Dictionary vertexData, out VertexBuffer vertexBuffer, out IndexBuffer indexBuffer) { + var absoluteMatrices = new Matrix[model.Bones.Count]; + model.CopyAbsoluteBoneTransformsTo(absoluteMatrices); + + var device = model.Meshes.First().Effects.First().GraphicsDevice; + var vertices = new VertexPositionColorNormalTexture[vertexCount]; Dictionary indexData = new(); bool largeIndices = vertexCount > ushort.MaxValue; - byte[] indicesAsBytes = new byte[indexCount * (largeIndices ? sizeof(uint) : sizeof(ushort))]; + byte[] indices = new byte[indexCount * (largeIndices ? sizeof(uint) : sizeof(ushort))]; int currentIndex = 0; int vertexOffset = 0; @@ -464,20 +462,23 @@ private static void GetMergedBuffers(Model model, int vertexCount, int indexCoun transform * absoluteTransform, partVertexBuffer.VertexDeclaration); int currentIndexCount = part.PrimitiveCount * 3; - int currentIndexStride = currentIndexCount * (largeIndices ? sizeof(uint) : sizeof(ushort)); - + if (largeIndices) { - CopyIndexBuffer(part, indexBufferData.AsSpan(), - indicesAsBytes.AsSpan().Slice(currentIndex, currentIndexStride), vertexOffset); + CopyIndexBuffer(part, indexBufferData.AsSpan(), + MemoryMarshal.Cast(indices.AsSpan()) + .Slice(currentIndex, currentIndexCount), + vertexOffset); } else { - CopyIndexBuffer(part, indexBufferData.AsSpan(), - indicesAsBytes.AsSpan().Slice(currentIndex, currentIndexStride), vertexOffset); + CopyIndexBuffer(part, indexBufferData.AsSpan(), + MemoryMarshal.Cast(indices.AsSpan()) + .Slice(currentIndex, currentIndexCount), + vertexOffset); } - currentIndex += currentIndexStride; + currentIndex += currentIndexCount; vertexOffset += part.NumVertices; } } @@ -489,7 +490,7 @@ private static void GetMergedBuffers(Model model, int vertexCount, int indexCoun largeIndices ? IndexElementSize.ThirtyTwoBits : IndexElementSize.SixteenBits, indexCount, BufferUsage.None); - indexBuffer.SetData(indicesAsBytes); + indexBuffer.SetData(indices); } private static uint GetMask(VertexElement[] elements) @@ -498,13 +499,13 @@ private static uint GetMask(VertexElement[] elements) for (int i = 0; i < elements.Length; i++) { - mask |= (uint)((1 << (int)elements[i].VertexElementUsage)); + mask |= (uint)(1 << (int)elements[i].VertexElementUsage); } return mask; } - private static void Sum(Span data, VertexDeclaration declaration, ref Vector3 sum) + private static void Sum(Span data, VertexDeclaration declaration, ref Vector3 addedSum) { var positionElement = declaration.GetVertexElements().First(e => e.VertexElementUsage == VertexElementUsage.Position); @@ -512,12 +513,12 @@ private static void Sum(Span data, VertexDeclaration declaration, ref Vect int dataOffset = 0; for (int i = 0; i < data.Length; i += declaration.VertexStride) { - sum += MemoryMarshal.AsRef(data.Slice(dataOffset + positionElement.Offset)); + addedSum += MemoryMarshal.AsRef(data.Slice(dataOffset + positionElement.Offset)); } } - private static void CopyIndexBuffer(ModelMeshPart part, ReadOnlySpan indexBufferData, - Span destination, int vertexOffset) + private static void CopyIndexBuffer(ModelMeshPart part, + ReadOnlySpan indexBufferData, Span destination, int vertexOffset) where TDestinationType : unmanaged, INumber { int indexStride = part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; @@ -528,30 +529,29 @@ private static void CopyIndexBuffer(ModelMeshPart part, ReadOn if (part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) { - AddAndCopy(indexBufferData.Slice(startIndexStride, countStride), + AddAndCopy( + MemoryMarshal.Cast(indexBufferData.Slice(startIndexStride, countStride)), destination, vertexOffset); } else { - AddAndCopy(indexBufferData.Slice(startIndexStride, countStride), + AddAndCopy( + MemoryMarshal.Cast(indexBufferData.Slice(startIndexStride, countStride)), destination, vertexOffset); } } - private static void AddAndCopy(ReadOnlySpan sourceBytes, - Span destinationBytes, int vertexOffset) + private static void AddAndCopy(ReadOnlySpan source, + Span destination, int vertexOffset) where TFrom : unmanaged, INumber where TTo : unmanaged, INumber { if (vertexOffset == 0 && typeof(TFrom) == typeof(TTo)) { - sourceBytes.CopyTo(destinationBytes); + MemoryMarshal.Cast(source).CopyTo(destination); return; } - var source = MemoryMarshal.Cast(sourceBytes); - var destination = MemoryMarshal.Cast(destinationBytes); - var offsetConverted = TTo.CreateChecked(vertexOffset); for (int i = 0; i < source.Length; i++) From e68018a619e2d2204541357290ebb3600e472366 Mon Sep 17 00:00:00 2001 From: AVinitzca Date: Tue, 14 Oct 2025 23:49:29 -0300 Subject: [PATCH 06/10] Yet another round of codacy issues --- .../Models/ModelExtensions.cs | 56 ++----------------- 1 file changed, 5 insertions(+), 51 deletions(-) diff --git a/TGC.MonoGame.Samples/Models/ModelExtensions.cs b/TGC.MonoGame.Samples/Models/ModelExtensions.cs index cb02ca0..8405e96 100644 --- a/TGC.MonoGame.Samples/Models/ModelExtensions.cs +++ b/TGC.MonoGame.Samples/Models/ModelExtensions.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; @@ -65,8 +64,6 @@ public static ModelInfo GetMerged(Model model) int vertexCount = 0; int indexCount = 0; - var device = model.Meshes.First().Effects.First().GraphicsDevice; - var textures = new List(); // Extract textures, vertices and indices @@ -80,7 +77,9 @@ public static ModelInfo GetMerged(Model model) var mainTexture = ((BasicEffect)part.Effect).Texture; if (mainTexture != null) + { textures.Add(mainTexture); + } } } @@ -145,15 +144,10 @@ public static ModelInfo GetCentered(Model model) { var mainTexture = ((BasicEffect)part.Effect).Texture; - Texture[] textures; - - if (mainTexture != null) - textures = [mainTexture]; - else - textures = []; - + Texture[] textures = mainTexture != null ? [mainTexture] : []; + geometryData[geometryIndex] = new GeometryData( - new Geometry() + new Geometry { VertexBuffer = vertexBuffers[vertexData[part.VertexBuffer].Index], PrimitiveCount = part.PrimitiveCount, @@ -561,46 +555,6 @@ private static void AddAndCopy(ReadOnlySpan source, } } - private static void CopyTo( - ReadOnlySpan sourceBytes, - Span destination, - int vertexOffset) - where TFrom : unmanaged - where TTo : unmanaged - { - if (vertexOffset == 0 && typeof(TFrom) == typeof(TTo)) - { - sourceBytes.CopyTo(destination); - return; - } - - int fromStride = Unsafe.SizeOf(); - int toStride = Unsafe.SizeOf(); - int i = 0; - int j = 0; - - if (fromStride == 2) - { - for (; i < sourceBytes.Length; i += fromStride) - { - BitConverter.GetBytes(BitConverter.ToUInt16(sourceBytes.Slice(i)) + vertexOffset).CopyTo(destination.Slice(j)); - j += toStride; - } - } - else - { - for (; i < sourceBytes.Length; i += fromStride) - { - var asRef = MemoryMarshal.AsRef(sourceBytes.Slice(i)); - asRef += (uint)vertexOffset; - MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref asRef, 1)); - - BitConverter.GetBytes(BitConverter.ToUInt32(sourceBytes.Slice(i)) + vertexOffset).CopyTo(destination.Slice(j)); - j += toStride; - } - } - } - private static void CopyTo(Span from, Span destination, in Matrix matrix, VertexDeclaration declaration) { var elements = declaration.GetVertexElements().AsSpan(); From bdc3405067dfb818b2e36777231216a560d54e6d Mon Sep 17 00:00:00 2001 From: AVinitzca Date: Wed, 15 Oct 2025 00:12:39 -0300 Subject: [PATCH 07/10] Hopefully final codacy rules --- TGC.MonoGame.Samples/Models/ModelExtensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TGC.MonoGame.Samples/Models/ModelExtensions.cs b/TGC.MonoGame.Samples/Models/ModelExtensions.cs index 8405e96..21e9007 100644 --- a/TGC.MonoGame.Samples/Models/ModelExtensions.cs +++ b/TGC.MonoGame.Samples/Models/ModelExtensions.cs @@ -242,7 +242,7 @@ public static ModelInfo GetCenteredXZ(Model model) Texture[] textures = mainTexture != null ? [mainTexture] : []; geometryData[geometryIndex] = new GeometryData( - new Geometry() + new Geometry { VertexBuffer = vertexBuffers[vertexData[part.VertexBuffer].Index], PrimitiveCount = part.PrimitiveCount, @@ -388,7 +388,9 @@ public static ModelInfo GetMergedCentered(Model model) var mainTexture = ((BasicEffect)part.Effect).Texture; if (mainTexture != null) + { textures.Add(mainTexture); + } } } From 90453a9bdfb0b3ba700347494d4d16ec2f04af59 Mon Sep 17 00:00:00 2001 From: AVinitzca Date: Wed, 15 Oct 2025 00:27:23 -0300 Subject: [PATCH 08/10] Final final.final --- TGC.MonoGame.Samples/Models/ModelExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TGC.MonoGame.Samples/Models/ModelExtensions.cs b/TGC.MonoGame.Samples/Models/ModelExtensions.cs index 21e9007..12fe2c0 100644 --- a/TGC.MonoGame.Samples/Models/ModelExtensions.cs +++ b/TGC.MonoGame.Samples/Models/ModelExtensions.cs @@ -336,7 +336,9 @@ public static ModelInfo GetMergedCentered(Model model) var mainTexture = ((BasicEffect)part.Effect).Texture; if (mainTexture != null) + { textures.Add(mainTexture); + } } } From 043059a3773962af22593403c1b10478538e33f8 Mon Sep 17 00:00:00 2001 From: AVinitzca Date: Sun, 19 Oct 2025 19:54:12 -0300 Subject: [PATCH 09/10] Addressed requested changes and added docs --- TGC.MonoGame.Samples/Content/Content.mgcb | 6 - TGC.MonoGame.Samples/Models/Geometry.cs | 32 +- TGC.MonoGame.Samples/Models/GeometryData.cs | 15 + .../Models/ModelExtensions.cs | 416 ++++++++---------- TGC.MonoGame.Samples/Samples/Models/Sponza.cs | 2 +- 5 files changed, 230 insertions(+), 241 deletions(-) diff --git a/TGC.MonoGame.Samples/Content/Content.mgcb b/TGC.MonoGame.Samples/Content/Content.mgcb index 0062302..8419623 100644 --- a/TGC.MonoGame.Samples/Content/Content.mgcb +++ b/TGC.MonoGame.Samples/Content/Content.mgcb @@ -238,12 +238,6 @@ /processorParam:DebugMode=Auto /build:Effects/BasicTexture.fx -#begin Effects/BasicTexture.fx -/importer:EffectImporter -/processor:EffectProcessor -/processorParam:DebugMode=Auto -/build:Effects/BasicTexture.fx - #begin Effects/BlinnPhong.fx /importer:EffectImporter /processor:EffectProcessor diff --git a/TGC.MonoGame.Samples/Models/Geometry.cs b/TGC.MonoGame.Samples/Models/Geometry.cs index 7443d50..178acb2 100644 --- a/TGC.MonoGame.Samples/Models/Geometry.cs +++ b/TGC.MonoGame.Samples/Models/Geometry.cs @@ -3,22 +3,24 @@ namespace TGC.MonoGame.Samples.Models; +/// +/// A representation of a simple triangle-based geometry that can be drawn. +/// public class Geometry : IDisposable { - internal VertexBuffer VertexBuffer; + protected VertexBuffer VertexBuffer; - internal IndexBuffer IndexBuffer; + protected IndexBuffer IndexBuffer; - internal int VertexOffset; + protected int VertexOffset; - internal int StartIndex; + protected int StartIndex; - internal int PrimitiveCount; + protected int PrimitiveCount; - internal bool OwnsVertexBuffer; - - internal bool OwnsIndexBuffer; + protected bool OwnsVertexBuffer; + protected bool OwnsIndexBuffer; internal static Geometry FromMeshPart(ModelMeshPart part) { @@ -50,6 +52,19 @@ private Geometry(ModelMeshPart part) OwnsIndexBuffer = false; } + internal Geometry(VertexBuffer vertexBuffer, IndexBuffer indexBuffer, + int vertexOffset, int startIndex, int primitiveCount, + bool ownsVertexBuffer, bool ownsIndexBuffer) + { + VertexBuffer = vertexBuffer; + IndexBuffer = indexBuffer; + VertexOffset = vertexOffset; + StartIndex = startIndex; + PrimitiveCount = primitiveCount; + OwnsVertexBuffer = ownsVertexBuffer; + OwnsIndexBuffer = ownsIndexBuffer; + } + public void Draw(Effect effect) { var graphicsDevice = effect.GraphicsDevice; @@ -64,7 +79,6 @@ public void Draw(Effect effect) VertexOffset, StartIndex, PrimitiveCount); } } - public void Dispose() { diff --git a/TGC.MonoGame.Samples/Models/GeometryData.cs b/TGC.MonoGame.Samples/Models/GeometryData.cs index c892c89..bd55f8e 100644 --- a/TGC.MonoGame.Samples/Models/GeometryData.cs +++ b/TGC.MonoGame.Samples/Models/GeometryData.cs @@ -3,11 +3,26 @@ namespace TGC.MonoGame.Samples.Models; +/// +/// Data associated to a geometry, generally extracted from a MonoGame . +/// Represents an instance to be drawn with special custom data that a mesh contained. +/// +/// A matrix that +/// public struct GeometryData(Geometry geometry, Matrix relativeMatrix, Texture[] textures) { + /// + /// The geometry extracted from a model. Contains methods to be drawn with an . + /// public Geometry Geometry { get; private set; } = geometry; + /// + /// A matrix that was used to place the geometry in the center of the model. + /// public Matrix RelativeMatrix { get; private set; } = relativeMatrix; + /// + /// A collection of textures associated to the geometry inside the model. + /// public Texture[] Textures { get; private set; } = textures; } \ No newline at end of file diff --git a/TGC.MonoGame.Samples/Models/ModelExtensions.cs b/TGC.MonoGame.Samples/Models/ModelExtensions.cs index 12fe2c0..ba5eabe 100644 --- a/TGC.MonoGame.Samples/Models/ModelExtensions.cs +++ b/TGC.MonoGame.Samples/Models/ModelExtensions.cs @@ -10,6 +10,9 @@ namespace TGC.MonoGame.Samples.Models; +/// +/// Provides utilities to get from MonoGame s. +/// public static class ModelExtensions { /// @@ -32,20 +35,17 @@ public static ModelInfo Get(Model model) var absoluteMatrices = new Matrix[model.Bones.Count]; model.CopyAbsoluteBoneTransformsTo(absoluteMatrices); - foreach (var mesh in model.Meshes) + IterateMeshAndParts(model, (mesh, part) => { - foreach (var part in mesh.MeshParts) - { - var mainTexture = ((BasicEffect)part.Effect).Texture; - Texture[] textures = mainTexture != null ? [mainTexture] : []; + var mainTexture = ((BasicEffect)part.Effect).Texture; + Texture[] textures = mainTexture != null ? [mainTexture] : []; - geometryData[geometryIndex] = - new GeometryData(Geometry.FromMeshPart(part), absoluteMatrices[mesh.ParentBone.Index], - textures); + geometryData[geometryIndex] = + new GeometryData(Geometry.FromMeshPart(part), absoluteMatrices[mesh.ParentBone.Index], + textures); - geometryIndex++; - } - } + geometryIndex++; + }); return new ModelInfo(geometryData); } @@ -66,22 +66,18 @@ public static ModelInfo GetMerged(Model model) var textures = new List(); - // Extract textures, vertices and indices - foreach (var mesh in model.Meshes) + IterateMeshAndParts(model, (_, part) => { - foreach (var part in mesh.MeshParts) - { - vertexCount += part.NumVertices; - indexCount += part.PrimitiveCount * 3; + vertexCount += part.NumVertices; + indexCount += part.PrimitiveCount * 3; - var mainTexture = ((BasicEffect)part.Effect).Texture; + var mainTexture = ((BasicEffect)part.Effect).Texture; - if (mainTexture != null) - { - textures.Add(mainTexture); - } + if (mainTexture != null) + { + textures.Add(mainTexture); } - } + }); Dictionary vertexData = new(); @@ -93,7 +89,7 @@ public static ModelInfo GetMerged(Model model) new GeometryData(new Geometry(vertexBuffer, indexBuffer), Matrix.Identity, textures.ToArray()) ]); } - + /// /// Gets for a MonoGame . /// Lists simplified matrices, textures and geometry for a group of meshes that live inside the model. @@ -138,57 +134,52 @@ public static ModelInfo GetCentered(Model model) HashSet assignedVertexBuffers = new(); int geometryIndex = 0; - foreach (var mesh in model.Meshes) + + IterateMeshAndParts(model, (mesh, part) => { - foreach (var part in mesh.MeshParts) - { - var mainTexture = ((BasicEffect)part.Effect).Texture; - - Texture[] textures = mainTexture != null ? [mainTexture] : []; - - geometryData[geometryIndex] = new GeometryData( - new Geometry - { - VertexBuffer = vertexBuffers[vertexData[part.VertexBuffer].Index], - PrimitiveCount = part.PrimitiveCount, - IndexBuffer = part.IndexBuffer, - StartIndex = part.StartIndex, - VertexOffset = part.VertexOffset, - OwnsVertexBuffer = assignedVertexBuffers.Add(part.VertexBuffer), - OwnsIndexBuffer = false - }, - absoluteMatrices[mesh.ParentBone.Index], - textures - ); + var mainTexture = ((BasicEffect)part.Effect).Texture; + + Texture[] textures = mainTexture != null ? [mainTexture] : []; + + geometryData[geometryIndex] = new GeometryData( + new Geometry + ( + vertexBuffers[vertexData[part.VertexBuffer].Index], + part.IndexBuffer, + part.VertexOffset, + part.StartIndex, + part.PrimitiveCount, + assignedVertexBuffers.Add(part.VertexBuffer), + false + ), + absoluteMatrices[mesh.ParentBone.Index], + textures + ); - geometryIndex++; - } - } + geometryIndex++; + }); return new ModelInfo(geometryData); } private static void CopyVertexBuffersIntoSingleBuffer(Model model, Dictionary vertexData, Matrix transform) { - foreach (var mesh in model.Meshes) + IterateMeshAndParts(model, (_, part) => { - foreach (var part in mesh.MeshParts) - { - var partVertexBuffer = part.VertexBuffer; + var partVertexBuffer = part.VertexBuffer; - var vertexBufferData = vertexData[partVertexBuffer]; + var vertexBufferData = vertexData[partVertexBuffer]; - int offsetByStride = part.VertexOffset * - part.VertexBuffer.VertexDeclaration.VertexStride; + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; - int numVerticesByStride = part.NumVertices * - part.VertexBuffer.VertexDeclaration.VertexStride; + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; - CopyTo(vertexBufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), - vertexBufferData.Vertices.AsSpan().Slice(part.VertexOffset, part.NumVertices), - transform, partVertexBuffer.VertexDeclaration); - } - } + CopyTo(vertexBufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), + vertexBufferData.Vertices.AsSpan().Slice(part.VertexOffset, part.NumVertices), + transform, partVertexBuffer.VertexDeclaration); + }); } /// @@ -233,80 +224,78 @@ public static ModelInfo GetCenteredXZ(Model model) HashSet assignedVertexBuffers = new(); int geometryIndex = 0; - foreach (var mesh in model.Meshes) + + IterateMeshAndParts(model, (mesh, part) => { - foreach (var part in mesh.MeshParts) - { - var mainTexture = ((BasicEffect)part.Effect).Texture; + var mainTexture = ((BasicEffect)part.Effect).Texture; - Texture[] textures = mainTexture != null ? [mainTexture] : []; + Texture[] textures = mainTexture != null ? [mainTexture] : []; - geometryData[geometryIndex] = new GeometryData( - new Geometry - { - VertexBuffer = vertexBuffers[vertexData[part.VertexBuffer].Index], - PrimitiveCount = part.PrimitiveCount, - IndexBuffer = part.IndexBuffer, - StartIndex = part.StartIndex, - VertexOffset = part.VertexOffset, - OwnsVertexBuffer = assignedVertexBuffers.Add(part.VertexBuffer), - OwnsIndexBuffer = false - }, - absoluteMatrices[mesh.ParentBone.Index], - textures - ); + geometryData[geometryIndex] = new GeometryData( + new Geometry + ( + vertexBuffers[vertexData[part.VertexBuffer].Index], + part.IndexBuffer, + part.VertexOffset, + part.StartIndex, + part.PrimitiveCount, + assignedVertexBuffers.Add(part.VertexBuffer), + false + ), + absoluteMatrices[mesh.ParentBone.Index], + textures + ); - geometryIndex++; - } - } - + geometryIndex++; + }); + return new ModelInfo(geometryData); } private static void ExtractVertexDataAndSum(Model model, int vertexCount, out Dictionary vertexBufferData, out Vector3 sum) { - vertexBufferData = new(); - sum = Vector3.Zero; + var generatedVertexData = new Dictionary(); - // Extract vertices - - int vertexBufferIndex = 0; + Vector3 calculatedSum = Vector3.Zero; - foreach (var mesh in model.Meshes) + int vertexBufferIndex = 0; + + IterateMeshAndParts(model, (_, part) => { - foreach (var part in mesh.MeshParts) - { - vertexCount += part.NumVertices; + vertexCount += part.NumVertices; - var partVertexBuffer = part.VertexBuffer; - - if (!vertexBufferData.TryGetValue(partVertexBuffer, out var bufferData)) - { - var declaration = partVertexBuffer.VertexDeclaration; - var vertexSize = declaration.VertexStride; - var data = new byte[vertexSize * partVertexBuffer.VertexCount]; - partVertexBuffer.GetData(data); - - bufferData.Index = vertexBufferIndex; - bufferData.Data = data; - bufferData.Vertices = new VertexPositionColorNormalTexture[partVertexBuffer.VertexCount]; - vertexBufferData.Add(partVertexBuffer, bufferData); + var partVertexBuffer = part.VertexBuffer; + + if (!generatedVertexData.TryGetValue(partVertexBuffer, out var bufferData)) + { + var declaration = partVertexBuffer.VertexDeclaration; + var vertexSize = declaration.VertexStride; + var data = new byte[vertexSize * partVertexBuffer.VertexCount]; + partVertexBuffer.GetData(data); + + bufferData.Index = vertexBufferIndex; + bufferData.Data = data; + bufferData.Vertices = new VertexPositionColorNormalTexture[partVertexBuffer.VertexCount]; + generatedVertexData.Add(partVertexBuffer, bufferData); - vertexBufferIndex++; - } + vertexBufferIndex++; + } - int offsetByStride = part.VertexOffset * - part.VertexBuffer.VertexDeclaration.VertexStride; + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; - int numVerticesByStride = part.NumVertices * - part.VertexBuffer.VertexDeclaration.VertexStride; + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; - Sum(bufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), partVertexBuffer.VertexDeclaration, ref sum); - } - } + Sum(bufferData.Data.AsSpan().Slice(offsetByStride, numVerticesByStride), + partVertexBuffer.VertexDeclaration, ref calculatedSum); + }); + sum = calculatedSum; sum /= vertexCount; + vertexBufferData = generatedVertexData; } /// @@ -325,77 +314,47 @@ public static ModelInfo GetMergedCentered(Model model) var textures = new List(); - // Extract textures, vertices and indices - foreach (var mesh in model.Meshes) - { - foreach (var part in mesh.MeshParts) - { - vertexCount += part.NumVertices; - indexCount += part.PrimitiveCount * 3; - - var mainTexture = ((BasicEffect)part.Effect).Texture; - - if (mainTexture != null) - { - textures.Add(mainTexture); - } - } - } - Vector3 sum = Vector3.Zero; - Dictionary vertexData = new(); + var vertexData = new Dictionary(); - // Extract vertices - - foreach (var mesh in model.Meshes) + IterateMeshAndParts(model, (_, part) => { - foreach (var part in mesh.MeshParts) + vertexCount += part.NumVertices; + indexCount += part.PrimitiveCount * 3; + + var mainTexture = ((BasicEffect)part.Effect).Texture; + + if (mainTexture != null) { - vertexCount += part.NumVertices; - - var partVertexBuffer = part.VertexBuffer; - - if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) - { - var declaration = partVertexBuffer.VertexDeclaration; - var vertexSize = declaration.VertexStride; - bufferData = new byte[vertexSize * partVertexBuffer.VertexCount]; - partVertexBuffer.GetData(bufferData); - vertexData.Add(partVertexBuffer, bufferData); - } - - int offsetByStride = part.VertexOffset * - part.VertexBuffer.VertexDeclaration.VertexStride; + textures.Add(mainTexture); + } - int numVerticesByStride = part.NumVertices * - part.VertexBuffer.VertexDeclaration.VertexStride; + var partVertexBuffer = part.VertexBuffer; - Sum(bufferData.AsSpan().Slice(offsetByStride, numVerticesByStride), partVertexBuffer.VertexDeclaration, ref sum); + if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) + { + var declaration = partVertexBuffer.VertexDeclaration; + var vertexSize = declaration.VertexStride; + bufferData = new byte[vertexSize * partVertexBuffer.VertexCount]; + partVertexBuffer.GetData(bufferData); + vertexData.Add(partVertexBuffer, bufferData); } - } + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; + + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + // Done this way so its captured + Sum(bufferData.AsSpan().Slice(offsetByStride, numVerticesByStride), partVertexBuffer.VertexDeclaration, ref sum); + }); sum /= vertexCount; var centeringTransform = Matrix.CreateTranslation(-sum); - // Extract textures, vertices and indices - foreach (var mesh in model.Meshes) - { - foreach (var part in mesh.MeshParts) - { - vertexCount += part.NumVertices; - indexCount += part.PrimitiveCount * 3; - - var mainTexture = ((BasicEffect)part.Effect).Texture; - - if (mainTexture != null) - { - textures.Add(mainTexture); - } - } - } - GetMergedBuffers(model, vertexCount, indexCount, centeringTransform, vertexData, out var vertexBuffer, out var indexBuffer); @@ -405,7 +364,7 @@ public static ModelInfo GetMergedCentered(Model model) ]); } - private static void GetMergedBuffers(Model model, int vertexCount, int indexCount, in Matrix absoluteTransform, + private static void GetMergedBuffers(Model model, int vertexCount, int indexCount, Matrix absoluteTransform, Dictionary vertexData, out VertexBuffer vertexBuffer, out IndexBuffer indexBuffer) { var absoluteMatrices = new Matrix[model.Bones.Count]; @@ -424,62 +383,58 @@ private static void GetMergedBuffers(Model model, int vertexCount, int indexCoun int currentIndex = 0; int vertexOffset = 0; - foreach (var mesh in model.Meshes) + IterateMeshAndParts(model, (mesh, part) => { var transform = absoluteMatrices[mesh.ParentBone.Index]; + var partVertexBuffer = part.VertexBuffer; - foreach (var part in mesh.MeshParts) + if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) { - var partVertexBuffer = part.VertexBuffer; - - if (!vertexData.TryGetValue(partVertexBuffer, out var bufferData)) - { - var declaration = partVertexBuffer.VertexDeclaration; - var vertexSize = declaration.VertexStride; - bufferData = new byte[vertexSize * partVertexBuffer.VertexCount]; - partVertexBuffer.GetData(bufferData); - vertexData.Add(partVertexBuffer, bufferData); - } - - int indexStride = part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; - if (!indexData.TryGetValue(part.IndexBuffer, out var indexBufferData)) - { - indexBufferData = new byte[part.IndexBuffer.IndexCount * indexStride]; - part.IndexBuffer.GetData(indexBufferData); - indexData.Add(part.IndexBuffer, indexBufferData); - } - - int offsetByStride = part.VertexOffset * - part.VertexBuffer.VertexDeclaration.VertexStride; - - int numVerticesByStride = part.NumVertices * - part.VertexBuffer.VertexDeclaration.VertexStride; + var declaration = partVertexBuffer.VertexDeclaration; + var vertexSize = declaration.VertexStride; + bufferData = new byte[vertexSize * partVertexBuffer.VertexCount]; + partVertexBuffer.GetData(bufferData); + vertexData.Add(partVertexBuffer, bufferData); + } - CopyTo(bufferData.AsSpan().Slice(offsetByStride, numVerticesByStride), - vertices.AsSpan().Slice(vertexOffset, part.NumVertices), - transform * absoluteTransform, partVertexBuffer.VertexDeclaration); - - int currentIndexCount = part.PrimitiveCount * 3; - - if (largeIndices) - { - CopyIndexBuffer(part, indexBufferData.AsSpan(), - MemoryMarshal.Cast(indices.AsSpan()) - .Slice(currentIndex, currentIndexCount), - vertexOffset); - } - else - { - CopyIndexBuffer(part, indexBufferData.AsSpan(), - MemoryMarshal.Cast(indices.AsSpan()) - .Slice(currentIndex, currentIndexCount), - vertexOffset); - } - - currentIndex += currentIndexCount; - vertexOffset += part.NumVertices; + int indexStride = part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; + if (!indexData.TryGetValue(part.IndexBuffer, out var indexBufferData)) + { + indexBufferData = new byte[part.IndexBuffer.IndexCount * indexStride]; + part.IndexBuffer.GetData(indexBufferData); + indexData.Add(part.IndexBuffer, indexBufferData); } - } + + int offsetByStride = part.VertexOffset * + part.VertexBuffer.VertexDeclaration.VertexStride; + + int numVerticesByStride = part.NumVertices * + part.VertexBuffer.VertexDeclaration.VertexStride; + + CopyTo(bufferData.AsSpan().Slice(offsetByStride, numVerticesByStride), + vertices.AsSpan().Slice(vertexOffset, part.NumVertices), + transform * absoluteTransform, partVertexBuffer.VertexDeclaration); + + int currentIndexCount = part.PrimitiveCount * 3; + + if (largeIndices) + { + CopyIndexBuffer(part, indexBufferData.AsSpan(), + MemoryMarshal.Cast(indices.AsSpan()) + .Slice(currentIndex, currentIndexCount), + vertexOffset); + } + else + { + CopyIndexBuffer(part, indexBufferData.AsSpan(), + MemoryMarshal.Cast(indices.AsSpan()) + .Slice(currentIndex, currentIndexCount), + vertexOffset); + } + + currentIndex += currentIndexCount; + vertexOffset += part.NumVertices; + }); vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorNormalTexture), vertices.Length, BufferUsage.None); vertexBuffer.SetData(vertices); @@ -600,4 +555,15 @@ private static void CopyTo(Span from, Span action) + { + foreach(var mesh in model.Meshes) + { + foreach (var part in mesh.MeshParts) + { + action(mesh, part); + } + } + } } \ No newline at end of file diff --git a/TGC.MonoGame.Samples/Samples/Models/Sponza.cs b/TGC.MonoGame.Samples/Samples/Models/Sponza.cs index 4acc900..a2a361c 100644 --- a/TGC.MonoGame.Samples/Samples/Models/Sponza.cs +++ b/TGC.MonoGame.Samples/Samples/Models/Sponza.cs @@ -57,7 +57,7 @@ public override void Initialize() protected override void LoadContent() { - // Load the chair model + // Load the sponza model _model = Game.Content.Load(ContentFolder3D + "sponza/Sponza"); _effect = Game.Content.Load(ContentFolderEffects + "BasicTexture"); From 4893491c41244e49e0cc25f0476f953693b39215 Mon Sep 17 00:00:00 2001 From: AVinitzca Date: Mon, 27 Oct 2025 22:33:47 -0300 Subject: [PATCH 10/10] Fixed problem related to Sonar, to check if it's resolved. --- .../Models/BuffersExtensions.cs | 114 ++++++++++++++++++ .../Models/ModelExtensions.cs | 108 +---------------- 2 files changed, 118 insertions(+), 104 deletions(-) create mode 100644 TGC.MonoGame.Samples/Models/BuffersExtensions.cs diff --git a/TGC.MonoGame.Samples/Models/BuffersExtensions.cs b/TGC.MonoGame.Samples/Models/BuffersExtensions.cs new file mode 100644 index 0000000..e9b8b1e --- /dev/null +++ b/TGC.MonoGame.Samples/Models/BuffersExtensions.cs @@ -0,0 +1,114 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Vector2 = Microsoft.Xna.Framework.Vector2; +using Vector3 = Microsoft.Xna.Framework.Vector3; + +namespace TGC.MonoGame.Samples.Models; + +/// +/// Provides utilities to copy and transform s and s. +/// +public static class BuffersExtensions +{ + internal static void CopyIndexBuffer(ModelMeshPart part, + ReadOnlySpan indexBufferData, Span destination, int vertexOffset) + where TDestinationType : unmanaged, INumber + { + int indexStride = part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; + int currentIndexCount = part.PrimitiveCount * 3; + + int startIndexStride = part.StartIndex * indexStride; + int countStride = currentIndexCount * indexStride; + + if (part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) + { + AddAndCopy( + MemoryMarshal.Cast(indexBufferData.Slice(startIndexStride, countStride)), + destination, vertexOffset); + } + else + { + AddAndCopy( + MemoryMarshal.Cast(indexBufferData.Slice(startIndexStride, countStride)), + destination, vertexOffset); + } + } + + private static void AddAndCopy(ReadOnlySpan source, + Span destination, int vertexOffset) + where TFrom : unmanaged, INumber + where TTo : unmanaged, INumber + { + if (vertexOffset == 0 && typeof(TFrom) == typeof(TTo)) + { + MemoryMarshal.Cast(source).CopyTo(destination); + return; + } + + var offsetConverted = TTo.CreateChecked(vertexOffset); + + for (int i = 0; i < source.Length; i++) + { + var converted = TTo.CreateChecked(source[i]); + destination[i] = converted + offsetConverted; + } + } + + internal static void CopyTo(Span from, Span destination, in Matrix matrix, VertexDeclaration declaration) + { + var elements = declaration.GetVertexElements().AsSpan(); + elements.Sort((a, b) + => a.VertexElementUsage.CompareTo(b.VertexElementUsage)); + + uint mask = GetMask(declaration.GetVertexElements()); + + int dataOffset = 0; + int elementIndex = 0; + for (int i = 0; i < destination.Length; i++) + { + if ((mask & (1 << (int)VertexElementUsage.Position)) != 0) + { + destination[i].Position = Vector3.Transform( + MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)), matrix); + elementIndex++; + } + + if ((mask & (1 << (int)VertexElementUsage.Color)) != 0) + { + destination[i].Color = MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)); + elementIndex++; + } + + if ((mask & (1 << (int)VertexElementUsage.TextureCoordinate)) != 0) + { + destination[i].TextureCoordinate = MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)); + elementIndex++; + } + + if ((mask & (1 << (int)VertexElementUsage.Normal)) != 0) + { + destination[i].Normal = Vector3.TransformNormal( + MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)), matrix); + } + + dataOffset += declaration.VertexStride; + elementIndex = 0; + } + } + + private static uint GetMask(VertexElement[] elements) + { + uint mask = 0; + + foreach (var element in elements) + { + mask |= (uint)(1 << (int)element.VertexElementUsage); + } + + return mask; + } + +} \ No newline at end of file diff --git a/TGC.MonoGame.Samples/Models/ModelExtensions.cs b/TGC.MonoGame.Samples/Models/ModelExtensions.cs index ba5eabe..06dcd31 100644 --- a/TGC.MonoGame.Samples/Models/ModelExtensions.cs +++ b/TGC.MonoGame.Samples/Models/ModelExtensions.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Numerics; using System.Runtime.InteropServices; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Vector2 = Microsoft.Xna.Framework.Vector2; using Vector3 = Microsoft.Xna.Framework.Vector3; namespace TGC.MonoGame.Samples.Models; @@ -176,7 +174,7 @@ private static void CopyVertexBuffersIntoSingleBuffer(Model model, Dictionary(indices.AsSpan()) .Slice(currentIndex, currentIndexCount), vertexOffset); } else { - CopyIndexBuffer(part, indexBufferData.AsSpan(), + BuffersExtensions.CopyIndexBuffer(part, indexBufferData.AsSpan(), MemoryMarshal.Cast(indices.AsSpan()) .Slice(currentIndex, currentIndexCount), vertexOffset); @@ -446,18 +444,6 @@ private static void GetMergedBuffers(Model model, int vertexCount, int indexCoun indexBuffer.SetData(indices); } - private static uint GetMask(VertexElement[] elements) - { - uint mask = 0; - - for (int i = 0; i < elements.Length; i++) - { - mask |= (uint)(1 << (int)elements[i].VertexElementUsage); - } - - return mask; - } - private static void Sum(Span data, VertexDeclaration declaration, ref Vector3 addedSum) { var positionElement = @@ -470,92 +456,6 @@ private static void Sum(Span data, VertexDeclaration declaration, ref Vect } } - private static void CopyIndexBuffer(ModelMeshPart part, - ReadOnlySpan indexBufferData, Span destination, int vertexOffset) - where TDestinationType : unmanaged, INumber - { - int indexStride = part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4; - int currentIndexCount = part.PrimitiveCount * 3; - - int startIndexStride = part.StartIndex * indexStride; - int countStride = currentIndexCount * indexStride; - - if (part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) - { - AddAndCopy( - MemoryMarshal.Cast(indexBufferData.Slice(startIndexStride, countStride)), - destination, vertexOffset); - } - else - { - AddAndCopy( - MemoryMarshal.Cast(indexBufferData.Slice(startIndexStride, countStride)), - destination, vertexOffset); - } - } - - private static void AddAndCopy(ReadOnlySpan source, - Span destination, int vertexOffset) - where TFrom : unmanaged, INumber - where TTo : unmanaged, INumber - { - if (vertexOffset == 0 && typeof(TFrom) == typeof(TTo)) - { - MemoryMarshal.Cast(source).CopyTo(destination); - return; - } - - var offsetConverted = TTo.CreateChecked(vertexOffset); - - for (int i = 0; i < source.Length; i++) - { - var converted = TTo.CreateChecked(source[i]); - destination[i] = converted + offsetConverted; - } - } - - private static void CopyTo(Span from, Span destination, in Matrix matrix, VertexDeclaration declaration) - { - var elements = declaration.GetVertexElements().AsSpan(); - elements.Sort((a, b) - => a.VertexElementUsage.CompareTo(b.VertexElementUsage)); - - uint mask = GetMask(declaration.GetVertexElements()); - - int dataOffset = 0; - int elementIndex = 0; - for (int i = 0; i < destination.Length; i++) - { - if ((mask & (1 << (int)VertexElementUsage.Position)) != 0) - { - destination[i].Position = Vector3.Transform( - MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)), matrix); - elementIndex++; - } - - if ((mask & (1 << (int)VertexElementUsage.Color)) != 0) - { - destination[i].Color = MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)); - elementIndex++; - } - - if ((mask & (1 << (int)VertexElementUsage.TextureCoordinate)) != 0) - { - destination[i].TextureCoordinate = MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)); - elementIndex++; - } - - if ((mask & (1 << (int)VertexElementUsage.Normal)) != 0) - { - destination[i].Normal = Vector3.TransformNormal( - MemoryMarshal.AsRef(from.Slice(dataOffset + elements[elementIndex].Offset)), matrix); - } - - dataOffset += declaration.VertexStride; - elementIndex = 0; - } - } - private static void IterateMeshAndParts(Model model, Action action) { foreach(var mesh in model.Meshes)