diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/MeshHelper.cs b/MonoGame.Framework.Content.Pipeline/Graphics/MeshHelper.cs index a653be6a1d6..78b252ef9d8 100644 --- a/MonoGame.Framework.Content.Pipeline/Graphics/MeshHelper.cs +++ b/MonoGame.Framework.Content.Pipeline/Graphics/MeshHelper.cs @@ -1,6 +1,11 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Design.Serialization; using System.Diagnostics; +using System.Linq; +using System.Reflection; +using Microsoft.Xna.Framework.Content.Pipeline.Processors; +using Microsoft.Xna.Framework.Graphics; namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics { @@ -361,10 +366,64 @@ public static BoneContent FindSkeleton(NodeContent node) return root; } - // We didn't find any bones! + // We didn't find any bones! return null; } + /// + /// Builds a set of BoneContent nodes and injects them into a Model which + /// contains Mesh based Animations but has Zero BoneContent nodes. + /// + /// The base note on which to build the new BoneContent + /// The root Skeleton Node. + internal static BoneContent BuildSkeleton (NodeContent node) + { + var nodes = node.AsEnumerable().SelectDeep(n => n.Children).ToList (); + var meshes = nodes.FindAll(n => n is MeshContent).Cast().ToList(); + BoneContent root = null; + foreach (var mesh in meshes) { + var parent = mesh.Parent; + parent.Children.Remove (mesh); + var bone = new BoneContent () { + Name = "bone_" + mesh.Name, + Transform = mesh.Transform, + }; + if (root == null) + root = bone; + bone.Parent = null; + mesh.Parent = null; + mesh.Transform = Matrix.Identity; + parent.Children.Add (bone); + bone.Children.Add (mesh); + // foreach (var animation in mesh.Animations) { + // if (!node.Animations.TryGetValue (animation.Key, out AnimationContent ac)) { + // ac = new AnimationContent (); + // node.Animations.Add (animation.Key, ac); + // ac.Duration = animation.Value.Duration; + // ac.Identity = animation.Value.Identity; + // } + + // // foreach (var c in animation.Value.Channels) { + // // var key = "bone_" + c.Key; + // // if (!ac.Channels.TryGetValue (key, out AnimationChannel chan)) { + // // ac.Channels.Add(key, c.Value); + // // } + // // chan.Add (c.Value.) + // // } + // } + foreach (var geometry in mesh.Geometry) { + var weightsName = VertexChannelNames.Weights (); + var xnaWeights = new List(); + var weights = new BoneWeightCollection(); + weights.Add(new BoneWeight("bone_" + mesh.Name, 1.0f)); + for (int i=0; i <= geometry.Vertices.VertexCount-1; i++) + xnaWeights.Add (weights); + geometry.Vertices.Channels.Add (weightsName, xnaWeights ); + } + } + return root; + } + /// /// Traverses a skeleton depth-first and builds a list of its bones. /// diff --git a/MonoGame.Framework.Content.Pipeline/MonoGame.Framework.Content.Pipeline.csproj b/MonoGame.Framework.Content.Pipeline/MonoGame.Framework.Content.Pipeline.csproj index 05ae4a62d78..c87ebb4e388 100644 --- a/MonoGame.Framework.Content.Pipeline/MonoGame.Framework.Content.Pipeline.csproj +++ b/MonoGame.Framework.Content.Pipeline/MonoGame.Framework.Content.Pipeline.csproj @@ -82,7 +82,7 @@ - + @@ -170,6 +170,11 @@ runtimes\osx\native\libfreetype6.dylib PreserveNewest + + libassimp.dylib + runtimes\osx\native\libassimp.dylib + PreserveNewest + runtimes\osx\native PreserveNewest diff --git a/MonoGame.Framework.Content.Pipeline/Processors/ModelProcessor.cs b/MonoGame.Framework.Content.Pipeline/Processors/ModelProcessor.cs index e5ea9b33f3a..c00e81e8eba 100644 --- a/MonoGame.Framework.Content.Pipeline/Processors/ModelProcessor.cs +++ b/MonoGame.Framework.Content.Pipeline/Processors/ModelProcessor.cs @@ -105,7 +105,6 @@ public override ModelContent Process(NodeContent input, ContentProcessorContext var scale = Matrix.CreateScale(Scale); MeshHelper.TransformScene(input, rotZ * rotX * rotY * scale); } - // Gather all the nodes in tree traversal order. var nodes = input.AsEnumerable().SelectDeep(n => n.Children).ToList(); @@ -113,6 +112,12 @@ public override ModelContent Process(NodeContent input, ContentProcessorContext var geometries = meshes.SelectMany(m => m.Geometry).ToList(); var distinctMaterials = geometries.Select(g => g.Material).Distinct().ToList(); + // var hasBones = MeshHelper.FindSkeleton (input) != null; + // var hasRootAnimations = input.Animations.Any (); + // var hasMeshAnimations = meshes.Any (x => x.Animations.Count > 0); + // if (!hasBones && (hasRootAnimations || hasMeshAnimations)) + // MeshHelper.BuildSkeleton (input); + // Loop through all distinct materials, passing them through the conversion method // only once, and then processing all geometries using that material. foreach (var inputMaterial in distinctMaterials) @@ -238,6 +243,7 @@ protected virtual void ProcessGeometryUsingMaterial(MaterialContent material, else if (material is SkinnedMaterialContent) { textureChannels = 1; + // if we don't have bones but have animations we need to add "fake" weights. vertexWeights = true; } else if (material is EnvironmentMapMaterialContent) diff --git a/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ModelWriter.cs b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ModelWriter.cs index 02d4addf800..be0b2c16040 100644 --- a/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ModelWriter.cs +++ b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ModelWriter.cs @@ -38,6 +38,7 @@ protected internal override void Write(ContentWriter output, ModelContent value) WriteBoneReference(output, value.Root, value.Bones); output.WriteObject(value.Tag); + // Add serialization of Animation data here. } private void WriteBones(ContentWriter output, ModelBoneContentCollection bones) diff --git a/MonoGame.Framework.DesktopGL.sln b/MonoGame.Framework.DesktopGL.sln index d5661193e55..c7d884e2d1a 100644 --- a/MonoGame.Framework.DesktopGL.sln +++ b/MonoGame.Framework.DesktopGL.sln @@ -7,6 +7,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Framework.DesktopG EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Tests.DesktopGL", "Tests\MonoGame.Tests.DesktopGL.csproj", "{3EA1BB98-6F7F-4CEC-9804-8553A599AA3B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame.Framework.Content.Pipeline", "MonoGame.Framework.Content.Pipeline\MonoGame.Framework.Content.Pipeline.csproj", "{50610F72-EE08-475E-84E4-88B86E05ED77}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{8792B021-CB1C-420C-8A1E-AB9CB3B7751F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame.Tools.Tests", "Tools\MonoGame.Tools.Tests\MonoGame.Tools.Tests.csproj", "{44A60138-0D95-4673-A597-36E528571D53}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,6 +47,30 @@ Global {3EA1BB98-6F7F-4CEC-9804-8553A599AA3B}.Release|x64.Build.0 = Release|Any CPU {3EA1BB98-6F7F-4CEC-9804-8553A599AA3B}.Release|x86.ActiveCfg = Release|Any CPU {3EA1BB98-6F7F-4CEC-9804-8553A599AA3B}.Release|x86.Build.0 = Release|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Debug|x64.ActiveCfg = Debug|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Debug|x64.Build.0 = Debug|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Debug|x86.ActiveCfg = Debug|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Debug|x86.Build.0 = Debug|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Release|Any CPU.Build.0 = Release|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Release|x64.ActiveCfg = Release|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Release|x64.Build.0 = Release|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Release|x86.ActiveCfg = Release|Any CPU + {50610F72-EE08-475E-84E4-88B86E05ED77}.Release|x86.Build.0 = Release|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Debug|x64.ActiveCfg = Debug|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Debug|x64.Build.0 = Debug|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Debug|x86.ActiveCfg = Debug|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Debug|x86.Build.0 = Debug|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Release|Any CPU.Build.0 = Release|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Release|x64.ActiveCfg = Release|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Release|x64.Build.0 = Release|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Release|x86.ActiveCfg = Release|Any CPU + {44A60138-0D95-4673-A597-36E528571D53}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -48,4 +78,7 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {847BFB97-3FE9-4E70-B4D0-0B0C8F6921A8} EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {44A60138-0D95-4673-A597-36E528571D53} = {8792B021-CB1C-420C-8A1E-AB9CB3B7751F} + EndGlobalSection EndGlobal diff --git a/MonoGame.Framework/Content/ContentExtensions.cs b/MonoGame.Framework/Content/ContentExtensions.cs index 1b6dac6e37f..f5aa61a6cb7 100644 --- a/MonoGame.Framework/Content/ContentExtensions.cs +++ b/MonoGame.Framework/Content/ContentExtensions.cs @@ -1,12 +1,17 @@ using System; using System.Reflection; using System.Linq; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Xna.Framework.Content { internal static class ContentExtensions { - public static ConstructorInfo GetDefaultConstructor(this Type type) + public static ConstructorInfo GetDefaultConstructor( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + this Type type) { #if NET45 var typeInfo = type.GetTypeInfo(); @@ -18,7 +23,11 @@ public static ConstructorInfo GetDefaultConstructor(this Type type) #endif } - public static PropertyInfo[] GetAllProperties(this Type type) + public static PropertyInfo[] GetAllProperties( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + this Type type) { // Sometimes, overridden properties of abstract classes can show up even with diff --git a/MonoGame.Framework/Content/ContentManager.cs b/MonoGame.Framework/Content/ContentManager.cs index 27bd2c5a410..205cfa21b9b 100644 --- a/MonoGame.Framework/Content/ContentManager.cs +++ b/MonoGame.Framework/Content/ContentManager.cs @@ -112,6 +112,7 @@ private static void RemoveContentManager(ContentManager contentManager) } } +#if __ANDROID__ internal static void ReloadGraphicsContent() { lock (ContentManagerLock) @@ -134,6 +135,7 @@ internal static void ReloadGraphicsContent() } } } +#endif // Use C# destructor syntax for finalization code. // This destructor will run only if the Dispose method @@ -406,6 +408,7 @@ protected virtual Dictionary LoadedAssets get { return loadedAssets; } } +#if __ANDROID__ protected virtual void ReloadGraphicsAssets() { foreach (var asset in LoadedAssets) @@ -420,6 +423,7 @@ protected virtual void ReloadGraphicsAssets() genericMethod.Invoke(this, new object[] { asset.Key, Convert.ChangeType(asset.Value, asset.Value.GetType()) }); } } +#endif protected virtual void ReloadAsset(string originalAssetName, T currentAsset) { diff --git a/MonoGame.Framework/Content/ContentReaders/ModelReader.cs b/MonoGame.Framework/Content/ContentReaders/ModelReader.cs index 537c2855f11..a547bc2a31f 100644 --- a/MonoGame.Framework/Content/ContentReaders/ModelReader.cs +++ b/MonoGame.Framework/Content/ContentReaders/ModelReader.cs @@ -128,9 +128,11 @@ protected internal override Model Read(ContentReader reader, Model existingInsta if (existingInstance != null) part = existingInstance.Meshes[i].MeshParts[(int)j]; else +#pragma warning disable CS0618 // Type or member is obsolete part = new ModelMeshPart(); +#pragma warning restore CS0618 // Type or member is obsolete - part.VertexOffset = reader.ReadInt32(); + part.VertexOffset = reader.ReadInt32(); part.NumVertices = reader.ReadInt32(); part.StartIndex = reader.ReadInt32(); part.PrimitiveCount = reader.ReadInt32(); @@ -191,6 +193,11 @@ protected internal override Model Read(ContentReader reader, Model existingInsta // Tag? model.Tag = reader.ReadObject(); + + if (reader.PeekChar () == -1) + return model; + + // We have additional data to read :D Ooooo animations return model; } diff --git a/MonoGame.Framework/Content/ContentReaders/ReflectiveReader.cs b/MonoGame.Framework/Content/ContentReaders/ReflectiveReader.cs index 111e8fca918..105131cb7d8 100755 --- a/MonoGame.Framework/Content/ContentReaders/ReflectiveReader.cs +++ b/MonoGame.Framework/Content/ContentReaders/ReflectiveReader.cs @@ -5,13 +5,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using MonoGame.Framework.Utilities; namespace Microsoft.Xna.Framework.Content { - internal class ReflectiveReader : ContentTypeReader + internal class ReflectiveReader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicProperties)] T> : ContentTypeReader { delegate void ReadElement(ContentReader input, object parent); diff --git a/MonoGame.Framework/Content/ContentTypeReader.cs b/MonoGame.Framework/Content/ContentTypeReader.cs index 49ae6f2bfa7..59380bbdf9e 100644 --- a/MonoGame.Framework/Content/ContentTypeReader.cs +++ b/MonoGame.Framework/Content/ContentTypeReader.cs @@ -3,12 +3,14 @@ // file 'LICENSE.txt', which is part of this source code package. using System; +using System.Diagnostics.CodeAnalysis; using System.IO; namespace Microsoft.Xna.Framework.Content { public abstract class ContentTypeReader { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] private Type _targetType; public virtual bool CanDeserializeIntoExistingObject @@ -16,6 +18,7 @@ public virtual bool CanDeserializeIntoExistingObject get { return false; } } + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] public Type TargetType { get { return _targetType; } @@ -26,7 +29,11 @@ public virtual int TypeVersion get { return 0; } // The default version (unless overridden) is zero } - protected ContentTypeReader(Type targetType) + protected ContentTypeReader( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + Type targetType) { _targetType = targetType; } diff --git a/MonoGame.Framework/Design/VectorConversion.cs b/MonoGame.Framework/Design/VectorConversion.cs index d1e972307e5..8df438427d0 100644 --- a/MonoGame.Framework/Design/VectorConversion.cs +++ b/MonoGame.Framework/Design/VectorConversion.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using Microsoft.Xna.Framework.Graphics.PackedVector; @@ -23,7 +24,11 @@ public static bool CanConvertTo(ITypeDescriptorContext context, Type destination return false; } - public static object ConvertToFromVector4(ITypeDescriptorContext context, CultureInfo culture, Vector4 value, Type destinationType) + public static object ConvertToFromVector4(ITypeDescriptorContext context, CultureInfo culture, Vector4 value, +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + Type destinationType) { if (destinationType == typeof(float)) return value.X; diff --git a/MonoGame.Framework/Graphics/SpriteBatcher.cs b/MonoGame.Framework/Graphics/SpriteBatcher.cs index 497ca2286f6..cf33369be46 100644 --- a/MonoGame.Framework/Graphics/SpriteBatcher.cs +++ b/MonoGame.Framework/Graphics/SpriteBatcher.cs @@ -5,15 +5,15 @@ using System; using System.Collections.Generic; -namespace Microsoft.Xna.Framework.Graphics -{ +namespace Microsoft.Xna.Framework.Graphics +{ /// /// This class handles the queueing of batch items into the GPU by creating the triangle tesselations /// that are used to draw the sprite textures. This class supports int.MaxValue number of sprites to be /// batched and will process them into short.MaxValue groups (strided by 6 for the number of vertices /// sent to the GPU). - /// - internal class SpriteBatcher + /// + internal class SpriteBatcher { /* * Note that this class is fundamental to high performance for SpriteBatch games. Please exercise @@ -27,10 +27,10 @@ internal class SpriteBatcher /// /// The maximum number of batch items that can be processed per iteration /// - private const int MaxBatchSize = short.MaxValue / 6; // 6 = 4 vertices unique and 2 shared, per quad + private const int MaxBatchSize = 4096;//short.MaxValue / 6; // 6 = 4 vertices unique and 2 shared, per quad /// /// Initialization size for the vertex array, in batch units. - /// + /// private const int InitialVertexArraySize = 256; /// @@ -52,24 +52,25 @@ internal class SpriteBatcher /// private short[] _index; - private VertexPositionColorTexture[] _vertexArray; - - public SpriteBatcher(GraphicsDevice device, int capacity = 0) - { - _device = device; - - if (capacity <= 0) - capacity = InitialBatchSize; - else - capacity = (capacity + 63) & (~63); // ensure chunks of 64. - + private VertexPositionColorTexture[] _vertexArray; + + public SpriteBatcher(GraphicsDevice device, int capacity = 0) + { + _device = device; + + if (capacity <= 0) + capacity = InitialBatchSize; + else + capacity = (capacity + 63) & (~63); // ensure chunks of 64. + _batchItemList = new SpriteBatchItem[capacity]; _batchItemCount = 0; for (int i = 0; i < capacity; i++) _batchItemList[i] = new SpriteBatchItem(); - EnsureArrayCapacity(capacity); + BuildIndexArray (); + EnsureArrayCapacity(capacity); } /// @@ -94,88 +95,91 @@ public SpriteBatchItem CreateBatchItem() return item; } + private unsafe void BuildIndexArray () + { + // int neededCapacity = 6 * MaxBatchSize; + // var newIndex = new short[neededCapacity]; + // int start = 0; + // fixed (short* indexFixedPtr = newIndex) + // { + // var indexPtr = indexFixedPtr + (start * 6); + // for (var i = start; i < neededCapacity - 1; i++, indexPtr += 6) + // { + // /* + // * TL TR + // * 0----1 0,1,2,3 = index offsets for vertex indices + // * | /| TL,TR,BL,BR are vertex references in SpriteBatchItem. + // * | / | + // * | / | + // * |/ | + // * 2----3 + // * BL BR + // */ + // // Triangle 1 + // *(indexPtr + 0) = (short)(i * 4); + // *(indexPtr + 1) = (short)(i * 4 + 1); + // *(indexPtr + 2) = (short)(i * 4 + 2); + // // Triangle 2 + // *(indexPtr + 3) = (short)(i * 4 + 1); + // *(indexPtr + 4) = (short)(i * 4 + 3); + // *(indexPtr + 5) = (short)(i * 4 + 2); + // } + // } + _index = new short[MaxBatchSize * 6]; + for (int i = 0, j = 0; i < MaxBatchSize; i += 6, j += 4) + { + _index[i] = (short) (j); + _index[i + 1] = (short) (j + 1); + _index[i + 2] = (short) (j + 2); + _index[i + 3] = (short) (j + 3); + _index[i + 4] = (short) (j + 2); + _index[i + 5] = (short) (j + 1); + } + //_index = result; + } + /// /// Resize and recreate the missing indices for the index and vertex position color buffers. /// /// private unsafe void EnsureArrayCapacity(int numBatchItems) { - int neededCapacity = 6 * numBatchItems; - if (_index != null && neededCapacity <= _index.Length) - { - // Short circuit out of here because we have enough capacity. - return; - } - short[] newIndex = new short[6 * numBatchItems]; - int start = 0; - if (_index != null) - { - _index.CopyTo(newIndex, 0); - start = _index.Length / 6; - } - fixed (short* indexFixedPtr = newIndex) - { - var indexPtr = indexFixedPtr + (start * 6); - for (var i = start; i < numBatchItems; i++, indexPtr += 6) - { - /* - * TL TR - * 0----1 0,1,2,3 = index offsets for vertex indices - * | /| TL,TR,BL,BR are vertex references in SpriteBatchItem. - * | / | - * | / | - * |/ | - * 2----3 - * BL BR - */ - // Triangle 1 - *(indexPtr + 0) = (short)(i * 4); - *(indexPtr + 1) = (short)(i * 4 + 1); - *(indexPtr + 2) = (short)(i * 4 + 2); - // Triangle 2 - *(indexPtr + 3) = (short)(i * 4 + 1); - *(indexPtr + 4) = (short)(i * 4 + 3); - *(indexPtr + 5) = (short)(i * 4 + 2); - } - } - _index = newIndex; - _vertexArray = new VertexPositionColorTexture[4 * numBatchItems]; - } - + } + /// /// Sorts the batch items and then groups batch drawing into maximal allowed batch sets that do not /// overflow the 16 bit array indices for vertices. /// /// The type of depth sorting desired for the rendering. /// The custom effect to apply to the drawn geometry - public unsafe void DrawBatch(SpriteSortMode sortMode, Effect effect) - { - if (effect != null && effect.IsDisposed) - throw new ObjectDisposedException("effect"); - + public unsafe void DrawBatch(SpriteSortMode sortMode, Effect effect) + { + if (effect != null && effect.IsDisposed) + throw new ObjectDisposedException("effect"); + // nothing to do - if (_batchItemCount == 0) - return; - - // sort the batch items - switch ( sortMode ) - { - case SpriteSortMode.Texture : - case SpriteSortMode.FrontToBack : + if (_batchItemCount == 0) + return; + + // sort the batch items + switch ( sortMode ) + { + case SpriteSortMode.Texture : + case SpriteSortMode.FrontToBack : case SpriteSortMode.BackToFront : - Array.Sort(_batchItemList, 0, _batchItemCount); - break; + Array.Sort(_batchItemList, 0, _batchItemCount); + break; } // Determine how many iterations through the drawing code we need to make int batchIndex = 0; - int batchCount = _batchItemCount; - - - unchecked + int batchCount = _batchItemCount; + + + unchecked { - _device._graphicsMetrics._spriteCount += batchCount; + _device._graphicsMetrics._spriteCount += batchCount; } // Iterate through the batches, doing short.MaxValue sets of vertices only. @@ -227,7 +231,7 @@ public unsafe void DrawBatch(SpriteSortMode sortMode, Effect effect) // Update our batch count to continue the process of culling down // large batches batchCount -= numBatchesToProcess; - } + } // return items to the pool. _batchItemCount = 0; } @@ -258,7 +262,7 @@ private void FlushVertexArray(int start, int end, Effect effect, Texture texture // ends up in Textures[0]. _device.Textures[0] = texture; - _device.DrawUserIndexedPrimitives( + _device.DrawUserIndexedPrimitives ( PrimitiveType.TriangleList, _vertexArray, 0, @@ -282,7 +286,7 @@ private void FlushVertexArray(int start, int end, Effect effect, Texture texture (vertexCount / 4) * 2, VertexPositionColorTexture.VertexDeclaration); } - } - } -} - + } + } +} + diff --git a/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj b/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj index 46b5fefb124..6d327ecc484 100644 --- a/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj +++ b/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj @@ -9,6 +9,8 @@ MonoGame.Framework.DesktopGL False True + true + CS1591;CS0419 diff --git a/MonoGame.Framework/Platform/Graphics/OpenGL.cs b/MonoGame.Framework/Platform/Graphics/OpenGL.cs index eed56af9abf..e451d892b74 100644 --- a/MonoGame.Framework/Platform/Graphics/OpenGL.cs +++ b/MonoGame.Framework/Platform/Graphics/OpenGL.cs @@ -1535,57 +1535,6 @@ internal unsafe static string GetString (StringName name) return Marshal.PtrToStringAnsi (GetStringInternal (name)); } - protected static IntPtr MarshalStringArrayToPtr (string[] strings) - { - IntPtr intPtr = IntPtr.Zero; - if (strings != null && strings.Length != 0) { - intPtr = Marshal.AllocHGlobal (strings.Length * IntPtr.Size); - if (intPtr == IntPtr.Zero) { - throw new OutOfMemoryException (); - } - int i = 0; - try { - for (i = 0; i < strings.Length; i++) { - IntPtr val = MarshalStringToPtr (strings [i]); - Marshal.WriteIntPtr (intPtr, i * IntPtr.Size, val); - } - } - catch (OutOfMemoryException) { - for (i--; i >= 0; i--) { - Marshal.FreeHGlobal (Marshal.ReadIntPtr (intPtr, i * IntPtr.Size)); - } - Marshal.FreeHGlobal (intPtr); - throw; - } - } - return intPtr; - } - - protected unsafe static IntPtr MarshalStringToPtr (string str) - { - if (string.IsNullOrEmpty (str)) { - return IntPtr.Zero; - } - int num = Encoding.ASCII.GetMaxByteCount (str.Length) + 1; - IntPtr intPtr = Marshal.AllocHGlobal (num); - if (intPtr == IntPtr.Zero) { - throw new OutOfMemoryException (); - } - fixed (char* chars = str + RuntimeHelpers.OffsetToStringData / 2) { - int bytes = Encoding.ASCII.GetBytes (chars, str.Length, (byte*)((void*)intPtr), num); - Marshal.WriteByte (intPtr, bytes, 0); - return intPtr; - } - } - - protected static void FreeStringArrayPtr (IntPtr ptr, int length) - { - for (int i = 0; i < length; i++) { - Marshal.FreeHGlobal (Marshal.ReadIntPtr (ptr, i * IntPtr.Size)); - } - Marshal.FreeHGlobal (ptr); - } - internal static string GetProgramInfoLog (int programId) { int length = 0; @@ -1605,10 +1554,13 @@ internal static string GetShaderInfoLog (int shaderId) { internal unsafe static void ShaderSource(int shaderId, string code) { - int length = code.Length; - IntPtr intPtr = MarshalStringArrayToPtr (new string[] { code }); - ShaderSourceInternal(shaderId, 1, intPtr, &length); - FreeStringArrayPtr(intPtr, 1); + var shaderBytecode = Encoding.ASCII.GetBytes (code); + int codeLength = shaderBytecode.Length; + fixed (void* pData = shaderBytecode) + { + void** ppData = &pData; + ShaderSourceInternal(shaderId, 1, new IntPtr(&pData), &codeLength); + } } internal unsafe static void GetShader (int shaderId, ShaderParameter name, out int result) diff --git a/MonoGame.Framework/Platform/Threading.cs b/MonoGame.Framework/Platform/Threading.cs index e9c83eb7b82..d43118fed46 100644 --- a/MonoGame.Framework/Platform/Threading.cs +++ b/MonoGame.Framework/Platform/Threading.cs @@ -167,8 +167,6 @@ static void ReturnResetEvent(ManualResetEventSlim resetEvent) _resetEventPool.Push(resetEvent); return; // return here to skip dispose } - - resetEvent.Dispose(); } /// diff --git a/MonoGame.Framework/Utilities/ReflectionHelpers.cs b/MonoGame.Framework/Utilities/ReflectionHelpers.cs index 89fcdd53853..f9ce5489e02 100644 --- a/MonoGame.Framework/Utilities/ReflectionHelpers.cs +++ b/MonoGame.Framework/Utilities/ReflectionHelpers.cs @@ -3,13 +3,18 @@ // file 'LICENSE.txt', which is part of this source code package. using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace MonoGame.Framework.Utilities { internal static partial class ReflectionHelpers { - public static bool IsValueType(Type targetType) + public static bool IsValueType( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + Type targetType) { if (targetType == null) { @@ -22,7 +27,11 @@ public static bool IsValueType(Type targetType) #endif } - public static Type GetBaseType(Type targetType) + public static Type GetBaseType( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + Type targetType) { if (targetType == null) { @@ -38,7 +47,11 @@ public static Type GetBaseType(Type targetType) /// /// Returns the Assembly of a Type /// - public static Assembly GetAssembly(Type targetType) + public static Assembly GetAssembly( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + Type targetType) { if (targetType == null) { diff --git a/Tests/Assets/Models/NonSkeletonAnimated2.fbx b/Tests/Assets/Models/NonSkeletonAnimated2.fbx new file mode 100644 index 00000000000..aa9e95e99df Binary files /dev/null and b/Tests/Assets/Models/NonSkeletonAnimated2.fbx differ diff --git a/Tests/Assets/Models/Soldier/Textures/colormap.png b/Tests/Assets/Models/Soldier/Textures/colormap.png new file mode 100644 index 00000000000..06a680f5a75 Binary files /dev/null and b/Tests/Assets/Models/Soldier/Textures/colormap.png differ diff --git a/Tests/Assets/Models/Soldier/character-soldier.fbx b/Tests/Assets/Models/Soldier/character-soldier.fbx new file mode 100644 index 00000000000..5f6da503b15 Binary files /dev/null and b/Tests/Assets/Models/Soldier/character-soldier.fbx differ diff --git a/Tests/Assets/Projects/.config/dotnet-tools.json b/Tests/Assets/Projects/.config/dotnet-tools.json new file mode 100644 index 00000000000..b5e051aaef6 --- /dev/null +++ b/Tests/Assets/Projects/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-mgcb": { + "version": "3.8.1.1-develop", + "commands": [ + "mgcb" + ] + } + } +} \ No newline at end of file diff --git a/Tests/Assets/Projects/BuildSimpleProject.csproj b/Tests/Assets/Projects/BuildSimpleProject.csproj index f01aead9d8d..f7c2fc6fbb5 100644 --- a/Tests/Assets/Projects/BuildSimpleProject.csproj +++ b/Tests/Assets/Projects/BuildSimpleProject.csproj @@ -1,50 +1,15 @@  - - + - Debug - AnyCPU - 8.0.30703 - 2.0 - {1A3C19CC-557D-4009-82D6-92B511EA4172} WinExe - Properties - BuildSimpleProject - BuildSimpleProject - 512 DesktopGL - v4.5 + net8.0 - - - true - bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\ - DEBUG;TRACE;LINUX - full - AnyCPU - prompt - false - 4 - - - - bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\ - TRACE;LINUX - true - pdbonly - AnyCPU - prompt - false - 4 - - - + - - + + - - diff --git a/Tests/Assets/Projects/NuGet.config b/Tests/Assets/Projects/NuGet.config new file mode 100644 index 00000000000..51aa4fe5ccd --- /dev/null +++ b/Tests/Assets/Projects/NuGet.config @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ThirdParty/Dependencies b/ThirdParty/Dependencies index 5689180380d..eca57965b49 160000 --- a/ThirdParty/Dependencies +++ b/ThirdParty/Dependencies @@ -1 +1 @@ -Subproject commit 5689180380df05c8a50d0fd25bf80b9e91745ad9 +Subproject commit eca57965b49e9d66b15243788d4dc5d06e65204b diff --git a/Tools/MonoGame.Tools.Tests/BuilderTargetsTest.cs b/Tools/MonoGame.Tools.Tests/BuilderTargetsTest.cs index 99455e4be0a..37b0bba68c6 100644 --- a/Tools/MonoGame.Tools.Tests/BuilderTargetsTest.cs +++ b/Tools/MonoGame.Tools.Tests/BuilderTargetsTest.cs @@ -8,31 +8,12 @@ namespace MonoGame.Tests.ContentPipeline [TestFixture] public class BuilderTargetsTest { - string[] msBuildFolders = new string[] + bool RunBuild(string buildTool, string projectFile, string workingDir, params string[] parameters) { - Path.Combine ("MSBuild", "15.0", "Bin", "MSBuild.exe"), - Path.Combine ("MSBuild", "14.0", "Bin", "MSBuild.exe"), - }; - string FindBuildTool(string buildTool) - { - if (Environment.OSVersion.Platform == PlatformID.Win32NT && buildTool == "msbuild") - { - var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); - foreach (var path in msBuildFolders) - { - if (File.Exists(Path.Combine(programFiles, path))) - return Path.Combine(programFiles, path); - } - } - return buildTool; - } - bool RunBuild(string buildTool, string projectFile, params string[] parameters) - { - var root = Path.GetDirectoryName(typeof(BuilderTargetsTest).Assembly.Location); - var psi = new ProcessStartInfo(FindBuildTool(buildTool)) + var psi = new ProcessStartInfo(buildTool) { - Arguments = projectFile + " /t:BuildContent " + string.Join(" ", parameters) + " /noconsolelogger \"/flp1:LogFile=build.log;Encoding=UTF-8;Verbosity=Diagnostic\"", - WorkingDirectory = root, + Arguments = "build " + projectFile + " -bl -t:IncludeContent " + string.Join(" ", parameters) + " /noconsolelogger \"/flp1:LogFile=build.log;Encoding=UTF-8;Verbosity=Diagnostic\"", + WorkingDirectory = workingDir, UseShellExecute = true, }; using (var process = Process.Start(psi)) @@ -42,31 +23,15 @@ bool RunBuild(string buildTool, string projectFile, params string[] parameters) } } - static object[] BuilderTargetsBuildTools = new object[] { - "msbuild", - "xbuild", - }; - [Test] - [TestCaseSource("BuilderTargetsBuildTools")] -#if DESKTOPGL - [Ignore("Fails on Mac build server with xbuild for some reason.")] -#else - [Ignore("This test need to be rewritten to properly test Tools\\MonoGame.Content.Builder instead of the old builder targets.")] -#endif - public void BuildSimpleProject(string buildTool) + public void BuildSimpleProject() { - if (buildTool == "xbuild" && Environment.OSVersion.Platform == PlatformID.Win32NT) - Assert.Ignore("Skipping xbuild tests on windows"); - var root = Path.GetDirectoryName(typeof(BuilderTargetsTest).Assembly.Location); var outputPath = Path.Combine(root, "Assets", "Projects", "Content", "bin"); if (Directory.Exists(outputPath)) Directory.Delete(outputPath, recursive: true); - var result = RunBuild(buildTool, Path.Combine("Assets", "Projects", "BuildSimpleProject.csproj"), new string[] { - "/p:MonoGameContentBuilderExe=" + Path.Combine(root, "MGCB.exe") - }); + var result = RunBuild("dotnet", Path.Combine("Assets", "Projects", "BuildSimpleProject.csproj"), root); Assert.AreEqual(true, result, "Content Build should have succeeded."); var contentFont = Path.Combine(outputPath, "DesktopGL", "Content", "ContentFont.xnb"); Assert.IsTrue(File.Exists(contentFont), "'" + contentFont + "' should exist."); diff --git a/Tools/MonoGame.Tools.Tests/ModelProcessorTests.cs b/Tools/MonoGame.Tools.Tests/ModelProcessorTests.cs index 0df69481aaf..226123265a9 100644 --- a/Tools/MonoGame.Tools.Tests/ModelProcessorTests.cs +++ b/Tools/MonoGame.Tools.Tests/ModelProcessorTests.cs @@ -213,6 +213,84 @@ public void BasicMeshTests() } } + [Test] + [TestCase ("Assets/Models/NonSkeletonAnimated.fbx")] + [TestCase ("Assets/Models/Soldier/character-soldier.fbx")] + [TestCase ("Assets/Models/Dude/dude_2011.fbx")] + public void SkinnedModelWithNodeAnimationTest (string fbx) + { + var context = new TestImporterContext("TestObj", "TestBin"); + var processorContext = new TestProcessorContext(TargetPlatform.DesktopGL, "TestBin"); + var importer = new OpenAssetImporter(); + var processor = new ModelProcessor (); + + var nodeContent = importer.Import(fbx, context); + Assert.NotNull(nodeContent); + var model = processor.Process (nodeContent, processorContext); + Assert.NotNull (model); + } + + [Test] + public void DefaultEffectWithNodeAnimationTest () + { + NodeContent input; + { + input = new NodeContent(); + + var mesh = new MeshContent() + { + Name = "Mesh1" + }; + mesh.Positions.Add(new Vector3(0, 0, 0)); + mesh.Positions.Add(new Vector3(1, 0, 0)); + mesh.Positions.Add(new Vector3(1, 1, 1)); + + var geom = new GeometryContent(); + geom.Vertices.Add(0); + geom.Vertices.Add(1); + geom.Vertices.Add(2); + geom.Indices.Add(0); + geom.Indices.Add(1); + geom.Indices.Add(2); + + geom.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(0), new[] + { + new Vector2(0,0), + new Vector2(1,0), + new Vector2(1,1), + }); + + mesh.Geometry.Add(geom); + input.Children.Add(mesh); + + var anim = new AnimationContent() + { + Name = "anim1", + Duration = TimeSpan.Zero + }; + var animChannel = new AnimationChannel (); + animChannel.Add (new AnimationKeyframe (TimeSpan.Zero, Matrix.Identity)); + animChannel.Add (new AnimationKeyframe (TimeSpan.FromSeconds (1), Matrix.CreateRotationX (180f))); + anim.Channels.Add ("mesh1",animChannel); + + mesh.Animations.Add(anim.Name, anim); + } + + var processorContext = new TestProcessorContext(TargetPlatform.Windows, "dummy.xnb"); + var processor = new ModelProcessor + { + DefaultEffect = MaterialProcessorDefaultEffect.SkinnedEffect, + }; + + var output = processor.Process(input, processorContext); + + // TODO: Not sure why, but XNA always returns a BasicMaterialContent + // even when we specify SkinnedEffect as the default. We need to fix + // the test first before we can enable the assert here. + + //Assert.IsInstanceOf(typeof(SkinnedMaterialContent), output.Meshes[0].MeshParts[0].Material); + } + [Test] public void DefaultEffectTest() { diff --git a/Tools/MonoGame.Tools.Tests/MonoGame.Tools.Tests.csproj b/Tools/MonoGame.Tools.Tests/MonoGame.Tools.Tests.csproj index 23b50f97026..14b53deb0c6 100644 --- a/Tools/MonoGame.Tools.Tests/MonoGame.Tools.Tests.csproj +++ b/Tools/MonoGame.Tools.Tests/MonoGame.Tools.Tests.csproj @@ -566,6 +566,12 @@ Assets\Projects\BuildSimpleProject.csproj + + Assets\Projects\NuGet.config + + + Assets\Projects\.config\dotnet-tools.json + Assets\Projects\Content\Content.mgcb @@ -584,6 +590,15 @@ Assets\Models\NonSkeletonAnimated.fbx + + Assets\Models\NonSkeletonAnimated2.fbx + + + Assets\Models\Soldier\character-soldier.fbx + + + Assets\Models\Soldier\Textures\colormap.png + Assets\Models\BlenderDefaultCube.xnb diff --git a/Tools/MonoGame.Tools.Tests/OpenAssetImporterTests.cs b/Tools/MonoGame.Tools.Tests/OpenAssetImporterTests.cs index 9c9a25ffe5c..3af26606543 100644 --- a/Tools/MonoGame.Tools.Tests/OpenAssetImporterTests.cs +++ b/Tools/MonoGame.Tools.Tests/OpenAssetImporterTests.cs @@ -25,9 +25,19 @@ public void Arguments() } [Test] -#if DESKTOPGL - [Ignore("This crashes inside Assimp on Mac!")] -#endif + [TestCase ("Assets/Models/NonSkeletonAnimated.fbx")] + [TestCase ("Assets/Models/Soldier/character-soldier.fbx")] + [TestCase ("Assets/Models/Dude/dude_2011.fbx")] + public void AnimationImport (string fbx) + { + var context = new TestImporterContext("TestObj", "TestBin"); + var importer = new OpenAssetImporter(); + + var nodeContent = importer.Import(fbx, context); + Assert.NotNull(nodeContent); + } + + [Test] public void BlenderTests() { var context = new TestImporterContext("TestObj", "TestBin");