• Create Account

# Geometry Instancing is not working correctly

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

19 replies to this topic

### #1MoruganKodi  Members   -  Reputation: 127

Like
1Likes
Like

Posted 02 August 2013 - 03:51 AM

Hello Guys.

It really bugs me when my first post in any community I join has to be a request for help, so I have tried to hold off asking for assistance - but now this is holding up my development.

I have been having a headache over this for 2 weeks. There are plenty of examples and topics on the internet demonstrating this using primitives, and plenty that imply you want to use a content processor, but hardly any that ( none that I could find ) that shows how to implement this correctly when you want to copy vertices and indices from and existing model... and being fairly new to XNA ( specifically 3D programming ) , i have been losing sleep over only one and one particular issue: Geometry Instancing.

To begin - I started my implementation using the examples Here and Here, but with two notable differences:

- A: I did not need the "atlas texture" thing.
- B: I dont want to use generated geometry prrimitives, but instead copy the vertices from an existing model.

The MSDN Instanced Geometry Example doesnt help because the so called "instanced mesh" class mentioned in the article cannot be found.

In my particular project I need to check individual sub meshes ( + transforms ) to the camera's view frustum - so as a result - I want to maintain each individual mesh part in their own seperate vertexbuffers ( only examples I found that copy existing meshdata combines all meshparts into a single vertex buffer, making it impossible to manage individual mesh parts seperatly ).

In my project I have created the following classes:

• OSModel
• OSModelMesh
• OSModelMeshPart

I want OSModelMeshPart to contain my VertexBuffer / InstanceBuffer, as this would be initialized from an existing ModelMeshPart, and this is how I have written it.

At runtime, my entire draw routine passes through (during step through debugging) without raising any exceptions - however nothing displays. One of those issues where theres is nothing telling me what isnt working. VertexBuffers appear full ( and all copied data matches lengths and types of the original sources ). Its driving me nuts.

So I tried the following:

• Test: Forcing all instanced positions into the view frustum:
Result:  FPS drops as if all positions in my instance buffer are being drawn, but they seem completly invisible.
• Test: Alternate positioning by sending only a matrix to use as position transform
Result: Failed
• Test: Tried using Generated Geometry ( manually typed in cube ).. as in the examples:
Result: It worked - which is why I believe Im copying existing model data incorrectly...
• Test: A none instanced version of my shader:
Result: Passed - and renders correctly

In the attached images - all None-Instanced tests - all my positions are working - and my shaders are working, frustum checks are working, but I need this same result with Instancing, because I can maintain 20000+ ships in the game world, but I cannot put more than a few hundred of them inside my view frustum because I hit my batch limit ( too many draw calls ). I need to be able to put 2000 ships in my view frustum, in which some of these would be huge titans, I intend to LOD and Frustum Occlude each individual mesh part.

But when initializing these same models ( Skybox, Planets, and ships ) using the below  instancing code, they do not render.

After banging my head against the wall a few times I figured the problem must be originating from the areas that differ from the examples:

• Either I am copying Initializing my VertexBuffer, and IndexBuffer, for each OSModelMeshPart when copying from a ModelMeshPart incorrectly... or....
• Maybe the extra elements ( currently only a vector 4, I removed others ) are not being passes to the shader correctly ( which I doubt since It appears the positions are working correctly ? )

My goal:

• I want to maintain seperate draw lists for every mesh part in the end, therefore seperate instancebuffers for each meshpart - each meshpart is tested against the cameras view frustum on update - and the final instance buffer would contain only those visible in the view frustum.
• DrawStates are controlled by OSModelMesh, and are ordered by OSModel (Opaque meshes are stored in a seperate lists from Alpha and additive meshes )
• For it to actually work.

My entire instancing takes place in OSModelMeshPart - which I wanted to attach instead of making my post too long, but it appears .VB files are not permitted....

This is the curent state of my OSModelMeshPart object in my framework: RenderHelper contains StateManagement and Frustum Checks, etc...

Imports OS.Graphics.RenderHelper

Namespace Graphics

Public Class OSModelMeshPart
Inherits OS.iGameObject

Friend m_VertexDeclaration As VertexDeclaration, _
_
m_GeometryBuffer As VertexBuffer,
m_IndexBuffer As IndexBuffer,
_
m_InstanceBuffer As VertexBuffer

Friend m_Bindings As VertexBufferBinding()

Friend m_VertexStreamElements As VertexElement()

Private m_Owner As OSModelMesh

Friend m_Vertices As VertexPositionTexture()

Friend m_VertexCount As Integer,
m_VertexOffset As Integer,
m_IndexCount As Integer,
m_PrimitiveCount As Integer,
_
m_Last_InstanceCount As Integer = 0

Private m_BoundingSphere As BoundingSphere,
m_BoundingBox_FromSphere As BoundingBox,
m_BoundingBox_FromVertices As BoundingBox

Private m_Cache_LastPositions As ModelPositionInfo()

' instance list ( we TRIM/ ADD per cycle - do not CLEAR  - optimization )
Private m_Instances As New List(Of InstanceInfo)

Private m_ListIDCap As Integer = -1

' weather we have anything to draw:
Private m_CanDraw As Boolean = False

' bounds for each instance per cycle:
Friend m_Cache_Instances_BoundingSpheres As BoundingSphere() = {}
Friend m_Cache_Instances_BoundingBoxes As BoundingBox() = {}

''' <summary>
''' Returns Bounding Spheres for this mesh part for all previous instances passed.
''' Bounding spheres are returned for all instanced regardless of View Frustum visibility.
''' EG: Length of array will always metch the positioninfo list length.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Get
Return m_Cache_Instances_BoundingSpheres
End Get
End Property

''' <summary>
''' Returns all instance boundingboxes for this meshpart from the last instance positions list passed .
''' Bounding spheres are returned for all instanced regardless of View Frustum visibility.
''' EG: Length of array will always metch the positioninfo list length.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Get
Return m_Cache_Instances_BoundingBoxes
End Get
End Property

Get
Return m_Owner
End Get
End Property

Get
Return m_Owner.Owner_Model
End Get
End Property

'Private Structure InstanceInfo
'    Public World As Vector4

'    Public TeamColor_1 As Vector4 ' for meshes that support TeamColor on RED   regionmap channel
'    Public TeamColor_2 As Vector4 ' for meshes that support TeamColor on GREEN region map channel
'    Public TeamColor_3 As Vector4 ' for meshes that support TeamColor on BLUE  region map channel
'End Structure

Private Structure InstanceInfo
Public World As Vector4
End Structure

Friend Sub New(Owner As OSModelMesh, Part As ModelMeshPart)
MyBase.New(Owner.Game)

m_Owner = Owner

_Initialize_VertexDeclaration(Part)

_Initialize_Geometry(Part)

_Initialize_BoundingSphere()
_Initialize_BoundingBox_From_BoundingSphere()
_Initialize_BoundingBox_From_Vertices()

#If DEBUG Then
#End If
End Sub

Private Sub _Initialize_VertexDeclaration(Part As ModelMeshPart)

'0 = POSITION1  : Model Transforms per instance

'1 = COLOR1     : Per Instance Team Color1
'2 = COLOR2     : Per Instance Team Color2
'3 = COLOR3     : Per Instance Team Color3

'           m_VertexStreamElements =
'               {
'                   New VertexElement(0, VertexElementFormat.Vector4, VertexElementUsage.Position, 1)
'_
'                   New VertexElement(SizeOfVector4 * 4, VertexElementFormat.Vector4, VertexElementUsage.Color, 1),
'                   New VertexElement(SizeOfVector4 * 5, VertexElementFormat.Vector4, VertexElementUsage.Color, 2),
'                   New VertexElement(SizeOfVector4 * 6, VertexElementFormat.Vector4, VertexElementUsage.Color, 3)
'_
'               }

m_VertexStreamElements =
{
New VertexElement(0, VertexElementFormat.Vector4, VertexElementUsage.Position, 1)
}

m_VertexDeclaration = New VertexDeclaration(m_VertexStreamElements)
End Sub

Private Sub _Initialize_Geometry(Part As ModelMeshPart)

Dim Result_Vertex As VertexPositionTexture() = {}

With Part

' Initialize Vertices
m_VertexCount = .PrimitiveCount * 3
m_VertexOffset = .VertexOffset

m_PrimitiveCount = .PrimitiveCount

Array.Resize(Result_Vertex, m_VertexCount)

.VertexBuffer.GetData(Of VertexPositionTexture)(Result_Vertex)

' execute the original vertexbuffer
.VertexBuffer.Dispose()

' Bake Bone transforms into vertices:
' NOTE: if animated / dynamic bones are needed - then this should be removed - and bone transforms would need to be supplied somehow per instance
g_ApplyBoneTransforms(Result_Vertex, Owner_Mesh.m_ParentBone)

m_GeometryBuffer = New VertexBuffer(Owner_Model.GraphicsDevice, VertexPositionTexture.VertexDeclaration, Result_Vertex.Length, BufferUsage.None)

' apply:
m_GeometryBuffer.SetData(Of VertexPositionTexture)(Result_Vertex)

' Indices:
m_IndexCount = .IndexBuffer.IndexCount

' ----------------------------------------
Dim Result_Indices As Short() = {}
Array.Resize(Result_Indices, m_IndexCount)

.IndexBuffer.GetData(Of Short)(Result_Indices, 0, Result_Indices.Length)

m_IndexBuffer = New IndexBuffer(Owner_Model.GraphicsDevice, Part.IndexBuffer.IndexElementSize, .IndexBuffer.IndexCount, BufferUsage.None)

' apply
m_IndexBuffer.SetData(Of Short)(Result_Indices)

' ----------------------------------------

End With

' a copy of vertices remains in memory because we create bounds from them for thsi meshpart:
m_Vertices = Result_Vertex

End Sub

' we dont have dynamic bones in our model structure ( we dont need them in the current game ) - so we will instead bake the bone transforms into the vertices
Private Sub g_ApplyBoneTransforms(ByRef Vertices As VertexPositionTexture(), Bone As ModelBone)
If Bone Is Nothing Then Return
For I = 0 To Vertices.Length - 1
Vertices(I).Position = Vector3.Transform(Vertices(I).Position, GetAbsoluteBoneTransform(Bone))
Next
End Sub

' get absolute bone transforms: ( which will be hardbaked into our model )
Private Function GetAbsoluteBoneTransform(Bone As ModelBone) As Matrix
If Bone Is Nothing Then Return Matrix.Identity
Return Bone.Transform * GetAbsoluteBoneTransform(Bone.Parent)
End Function

Private Sub _Initialize_BoundingSphere()
Dim R As Single = 0

' use bone transform
' vertices already have bone transformations applied...
Dim BoundingSpherePosition As Vector3 = Vector3.Transform(Vector3.Zero, GetAbsoluteBoneTransform(Owner_Mesh.m_ParentBone))

For I = 0 To m_Vertices.Length - 1

Dim D = System.Math.Abs(Vector3.Distance(BoundingSpherePosition, m_Vertices(I).Position))

If D > R Then R = D
Next

m_BoundingSphere = New BoundingSphere(BoundingSpherePosition, R)
End Sub

Private Sub _Initialize_BoundingBox_From_BoundingSphere()
m_BoundingBox_FromSphere = BoundingBox.CreateFromSphere(m_BoundingSphere)
End Sub

' vertices at this point only have bone transforms applied: bounding box represents all verticies with bone transforms
' - so world transforms would have to be applied seperatly ( in a cached instance list )
Private Sub _Initialize_BoundingBox_From_Vertices()
Dim MIN As Vector3 = Vector3.Zero
Dim MAX As Vector3 = Vector3.Zero

For I = 0 To m_Vertices.Length - 1
MIN = Vector3.Min(MIN, m_Vertices(I).Position)
MAX = Vector3.Max(MAX, m_Vertices(I).Position)
Next

m_BoundingBox_FromVertices = New BoundingBox(MIN, MAX)
End Sub

' Update cache list of boundingboxes for all instances (sphere too )
Private Sub Update_Instance_BoundingBox(Position As ModelPositionInfo, Index As Integer)
m_Cache_Instances_BoundingBoxes(Index) = New BoundingBox(
Vector3.Transform(m_BoundingBox_FromVertices.Min, Position.GetTransform),
Vector3.Transform(m_BoundingBox_FromVertices.Max, Position.GetTransform)
)
m_Cache_Instances_BoundingSpheres(Index) = New BoundingSphere(
Vector3.Transform(m_BoundingSphere.Center, Position.GetTransform),
)
End Sub

Friend Sub Update_Instance_Information(Positions As ModelPositionInfo())

If Not m_Cache_LastPositions Is Nothing And Not Positions Is Nothing Then
If m_Cache_LastPositions.GetHashCode = Positions.GetHashCode Then
'OPTIMIZATION:  nothing has changed - use previous instance information:
If Not m_InstanceBuffer Is Nothing Then Return
End If
End If

m_CanDraw = False
m_Last_InstanceCount = 0

If Positions Is Nothing Then Return
If Positions.Count < 1 Then Return

' bounding boxes lengths:
If m_Cache_Instances_BoundingSpheres.Length <> Positions.Length Then Array.Resize(m_Cache_Instances_BoundingSpheres, Positions.Length)
If m_Cache_Instances_BoundingBoxes.Length <> Positions.Length Then Array.Resize(m_Cache_Instances_BoundingBoxes, Positions.Length)

' make sure we are not repeatedly growing the list in the loop per instance:
If m_Instances.Capacity < Positions.Length Then m_Instances.Capacity = Positions.Length

Dim Visible As Integer = 0

Dim Index As Integer = 0 ' so we may scale our list:
Dim TrueIndex As Integer = 0

For Each P As ModelPositionInfo In Positions
If P.Enabled Then
' we only check with wour bounding sphere because the method has extra checks in case of bad accuracy:
If g_IsInViewOfCamera(P.Position, m_BoundingSphere, Owner_Model.GameState.Camera) Then

' TODO: check LOD and determine weather to / or how to draw this meshpart  Per Instance

Dim INSTANCE As New InstanceInfo With
{
.World = Vector4.Transform(Vector3.Zero, P.GetTransform)
}

If Owner_Mesh.IsBillboard Then
' TODO: Billboard Tranformation for this model instance ( must face camera )

End If

If m_ListIDCap < Index Then
' index does not exist in list yet:

m_ListIDCap = Index
Else
' index exists : replace... ( avoid clearing list every cycle )
m_Instances(Index) = INSTANCE
End If

' done:
Index += 1
End If
End If

' finally - ensure we have a bounding box and bounding sphere for each instance:
' bounds are cached for ALL positions regardless of weather they are enabled, or in the view frustum
Update_Instance_BoundingBox(P, TrueIndex)

TrueIndex += 1
Next

' remove excess positions that are not drawn this time around
' ( extra instance information from a previous draw cycle ( eg: an instance is no longer in the view frustum) )
If m_Instances.Count >= Index Then
For I = Index To m_Instances.Count - 1
m_Instances.RemoveAt(I)
Next
End If

' check that we actually have vertices to send:
If m_Instances.Count < 1 Then
' nothing to draw:
m_InstanceBuffer = Nothing
m_CanDraw = False
m_Last_InstanceCount = 0
End If

' if not created:
If m_InstanceBuffer Is Nothing Then
m_InstanceBuffer = New VertexBuffer(Owner_Model.GraphicsDevice, m_VertexDeclaration, m_Instances.Count, BufferUsage.WriteOnly)
End If

' if count is extactly the same as previous cycle - then do not redeclare - simply replace the data:
'If m_InstanceBuffer.VertexCount <> m_Instances.Count Then
m_InstanceBuffer = New VertexBuffer(Owner_Model.GraphicsDevice, m_VertexDeclaration, m_Instances.Count, BufferUsage.WriteOnly)
' End If

' apply:
m_InstanceBuffer.SetData(m_Instances.ToArray)

' set draw information states:
m_CanDraw = True
m_Last_InstanceCount = m_Instances.Count

' hold a copy of previous positions list for hash comparison next cycle:
m_Cache_LastPositions = Positions

End Sub

' must be called from OSModelMesh.Draw() AFTER 'Update_Instance_Information()' is called
' returns total instances drawn:
Friend Function Draw() As Integer
' check is flag is set notifying that we have nothing to draw this time round:
If m_CanDraw = False Then Return 0

m_Bindings = Nothing

' prepare bindings:
m_Bindings = {
New VertexBufferBinding(m_GeometryBuffer),
New VertexBufferBinding(m_InstanceBuffer, 0, 1)
}

With Owner_Model.GraphicsDevice
.Indices = m_IndexBuffer

' single pass

.SetVertexBuffers(m_Bindings)
.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0, m_VertexCount, 0, m_PrimitiveCount, m_Last_InstanceCount)
Else
For Each P As EffectPass In Owner_Model.Effect.Shader.CurrentTechnique.Passes
P.Apply()

.SetVertexBuffers(m_Bindings)

.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0, m_VertexCount, 0, m_PrimitiveCount, m_Last_InstanceCount)

Next
End If

'.SetVertexBuffers(Nothing)
End With
Return m_Last_InstanceCount
End Function

End Class

End Namespace



I think my problem is here - were I initialize my geometry:


Private Sub _Initialize_Geometry(Part As ModelMeshPart)

Dim Result_Vertex As VertexPositionTexture() = {}

With Part

' Initialize Vertices
m_VertexCount = .PrimitiveCount * 3
m_VertexOffset = .VertexOffset

m_PrimitiveCount = .PrimitiveCount

Array.Resize(Result_Vertex, m_VertexCount)

.VertexBuffer.GetData(Of VertexPositionTexture)(Result_Vertex)

.VertexBuffer.Dispose()

' Bake Bone transforms into vertices:
' NOTE: if animated / dynamic bones are needed - then this should be removed - and bone transforms would need to be supplied somehow per instance
g_ApplyBoneTransforms(Result_Vertex, Owner_Mesh.m_ParentBone)

m_GeometryBuffer = New VertexBuffer(Owner_Model.GraphicsDevice, VertexPositionTexture.VertexDeclaration, Result_Vertex.Length, BufferUsage.None)

' apply:
m_GeometryBuffer.SetData(Of VertexPositionTexture)(Result_Vertex)

' Indices:
m_IndexCount = .IndexBuffer.IndexCount

' ----------------------------------------
Dim Result_Indices As Short() = {}
Array.Resize(Result_Indices, m_IndexCount)

.IndexBuffer.GetData(Of Short)(Result_Indices, 0, Result_Indices.Length)

m_IndexBuffer = New IndexBuffer(Owner_Model.GraphicsDevice, Part.IndexBuffer.IndexElementSize, .IndexBuffer.IndexCount, BufferUsage.None)

' apply
m_IndexBuffer.SetData(Of Short)(Result_Indices)

' ----------------------------------------

End With

' a copy of vertices remains in memory because we create bounds from them:
m_Vertices = Result_Vertex

End Sub



Notes:

- Update_Instance_Information() would be called at the end of every update cycle once - and is required before draw ( at the moment it is called below draw ):

- OSModel.Draw is called - wich contains each mesh sorted by Blendstate (opaque first, Additive second, Alpha Last ) , which calls OSModelMesh.Draw - and finally OSModelMeshPart.Draw().

- Shader effect parameters are set before calling OSModelMeshPart.Draw.

I am also supplying the shader I am trying to test my instancing with - which should should only be returning white for now:


// #include "functions.hlsl"

float4x4 View;
float4x4 Projection;

texture InputTexture;
sampler InputTextureSampler = sampler_state
{
texture = <InputTexture>;
mipfilter = LINEAR;
minfilter = LINEAR;
magfilter = LINEAR;
};

struct INSTANCED_VSI{
float4 Position				: POSITION0;
};

struct INSTANCED_VSO{
float4 Position				: POSITION0;
};

INSTANCED_VSO VS_MAIN(INSTANCED_VSI input, float4 InstanceTransform : POSITION1)
{
INSTANCED_VSO output;
// -->

float4x4 WVP = View * Projection;

float4 WorldPosition = input.Position + InstanceTransform;
output.Position		 = mul(WorldPosition, WVP);

return output;
}

float4 PS_MAIN(INSTANCED_VSO input) : COLOR0
{
float4 result = float4(1,1,1,1);
// we are ignoring the input texture for now....
return (result) ;
}

technique RENDER_WITH_PS
{
pass p0
{
//ZWriteEnable = false;
//AlphaTestEnable = false;
//AlphaBlendEnable = true;
//SrcBlend = SRCALPHA;
//DestBlend = ONE;
}
}

//technique RENDER_WITHOUT_PS
//{
//    pass p0
//    {
//		//ZWriteEnable = false;
//		//AlphaTestEnable = false;
//		//AlphaBlendEnable = true;
//		//SrcBlend = SRCALPHA;
//		//DestBlend = ONE;
//    }
//}


Any help and explanation would be much appreciated. I can read both C# and VB  ,  so any examples can be described in either language. I am writing in VB because of personal preference.

#### Attached Thumbnails

Edited by MoruganKodi, 02 August 2013 - 04:00 AM.

### #2unbird  Crossbones+   -  Reputation: 8262

Like
0Likes
Like

Posted 02 August 2013 - 03:34 PM

This quite a lot of code to inspect, especially since you use a "unpopular" language (no offence intended. On the contrary: I consider it quite nice. It's probably the first time I see someone using XNA with VB). Maybe translate it to C# with reflector or ILSpy, so more people can read it.

What you're trying to do is surely possible. Can't easily read the VB part but the shader caught my eye. I've seen troubles with using the POSITION1 semantic in some thread around here. Maybe you can't use it for instancing. Use a texcoord for a change (and make sure it doesn't collide with the geometry part).

Such troubles are best investigated with PIX (needs the DirectX June 2010 SDK) or a similar tool though. Problem is, with XNA you're probably not familiar with the underlying D3D9 calls, so not sure this will help.

Edit: Are you familiar with the Direct3D debug runtimes ?. I suspect another problem with your vertex declaration (I'm not sure one can use a COLOR2 semantic).

Edited by unbird, 03 August 2013 - 10:44 AM.

### #3MoruganKodi  Members   -  Reputation: 127

Like
0Likes
Like

Posted 04 August 2013 - 02:58 AM

This quite a lot of code to inspect, especially since you use a "unpopular" language (no offence intended. On the contrary: I consider it quite nice. It's probably the first time I see someone using XNA with VB). Maybe translate it to C# with reflector or ILSpy, so more people can read it.

What you're trying to do is surely possible. Can't easily read the VB part but the shader caught my eye. I've seen troubles with using the POSITION1 semantic in some thread around here. Maybe you can't use it for instancing. Use a texcoord for a change (and make sure it doesn't collide with the geometry part).

Such troubles are best investigated with PIX (needs the DirectX June 2010 SDK) or a similar tool though. Problem is, with XNA you're probably not familiar with the underlying D3D9 calls, so not sure this will help.

Edit: Are you familiar with the Direct3D debug runtimes ?. I suspect another problem with your vertex declaration (I'm not sure one can use a COLOR2 semantic).

Teamcolor lines were commented out as well as all related code. I wanted to be able to pass instance spefic colors as well ( teamcolors,)  Im looking into the debug runtimes now.

Your also right that im not familiar with underling direct3D calls.

Im tried replacing use of Position1 with Texcoord. After reading what you said - while looking at http://roecode.wordpress.com/2008/03/17/xna-framework-gameengine-development-part-19-hardware-instancing-pc-only/ , it hit me that Texcoord is used their as well ( to encode a matrix in 4 texture coordinates , were I thought this would have been done in 4 positions)  - so in my case ( were I wanted just one world position ) - so I rewrote it to send the entire matrix instead.

However. The instances are still not drawing ( and DirectXSDK is still downloading, so i cant try the utilities in there yet ) All the results I have again - are exactly as they were before. And no exceptions are being raised.

Changes:

I changed my Vertexshader function as follows:

INSTANCED_VSO VS_MAIN(INSTANCED_VSI input, float4x4 InstanceTransform : TEXCOORD1)
{
INSTANCED_VSO output;

float4x4 WVP = View * Projection;

float4 WorldPosition = mul(input.Position, InstanceTransform);
output.Position		 = mul(WorldPosition , WVP);

return output;
}


My InstanceInfo structure's World Position field is now a Matrix instead of a Vector4:


Private Structure InstanceInfo

Public World As Matrix

End Structure



Which led to my VertexDeclaration changing to :


Private Sub _Initialize_VertexDeclaration(Part As ModelMeshPart)

m_VertexStreamElements =
{
New VertexElement(0                , VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 1),
New VertexElement(SizeOfVector4 * 1, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 2),
New VertexElement(SizeOfVector4 * 2, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 3),
New VertexElement(SizeOfVector4 * 3, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 4)
}

m_VertexDeclaration = New VertexDeclaration(m_VertexStreamElements)
End Sub



And instead of transforming a Vector4 by a matrix before sending it - I am just sending the instance specific Matrix:

            For Each P As ModelPositionInfo In Positions
If P.Enabled Then

If g_IsInViewOfCamera(P.Position, m_BoundingSphere, Owner_Model.GameState.Camera) Then

Dim INSTANCE As New InstanceInfo With
{
.World = P.GetTransform
}

If m_ListIDCap < Index Then
' index does not exist in list yet:

m_ListIDCap = Index
Else
' index exists : replace... ( avoid clearing list every cycle )
m_Instances(Index) = INSTANCE
End If

' done:
Index += 1
End If
End If

Update_Instance_BoundingBox(P, TrueIndex)

TrueIndex += 1
Next


Basically it's all doing the same thing in a different way, and the only major difference now from examples being the routine used to initialize the Vertex and Index buffers.

Step-Through debugging also passes through once again - without error, but still not displaying.

I still think Im not copying existing model data correctly for use with instancing.

--------------

BRB 20 minutes ( oops - this is a forum , not IRC )

Edited by MoruganKodi, 04 August 2013 - 03:37 AM.

### #4MoruganKodi  Members   -  Reputation: 127

Like
0Likes
Like

Posted 04 August 2013 - 04:21 AM

---------------

I decided to try another fix. I think the only vertex elements I was sending were my own?

I commented out the original Initialize subroutine for my vertex declaration and tried using this replacement:


Private Sub _Initialize_VertexDeclaration2(Part As ModelMeshPart)
Dim OriginalVertexElements = Part.VertexBuffer.VertexDeclaration.GetVertexElements

Dim Offset As Integer = 0

Offset = OriginalVertexElements(OriginalVertexElements.Length - 1).Offset

Dim ExtraElements As VertexElement() =
{
New VertexElement(Offset + (SizeOfVector4 * 1), VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 2),
New VertexElement(Offset + (SizeOfVector4 * 2), VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 3),
New VertexElement(Offset + (SizeOfVector4 * 3), VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 4),
New VertexElement(Offset + (SizeOfVector4 * 4), VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 5)
}

' destroy original vertexdeclaration:
Part.VertexBuffer.VertexDeclaration.Dispose()

Dim Length = OriginalVertexElements.Length + ExtraElements.Length

Dim FinalElements As VertexElement() = {}
Array.Resize(FinalElements, Length)

OriginalVertexElements.CopyTo(FinalElements, 0)

ExtraElements.CopyTo(FinalElements, OriginalVertexElements.Length)

m_VertexDeclaration = New VertexDeclaration(FinalElements)

End Sub
End Class



However I made a new discovery while writing and testing it:

• A TEXCOORD1  ( elementusage 1) already existed in the vertexdeclaration supplied by the original model, so I decided to leave it as it is by not replacing it ( just in case). This resulted in me having to begin my matrix elements at ElementUsage 2 ( TEXCOORD2 ).

In the attached images:

• 1: The original vertex elements array supplied by the original vertexdeclaration supplied by the model - see the second TextureCoordinate supplied by the original model.
• 2: My vertex elements, but offset so they are just added to the eisting elements without replacing or conflicting with existing texturecoordinate elements.
• 3: My completed stream elements

As a result I made a version of my shader that expects a TEXCOORD2 instead of TEXCOORD1 - because I am not replacing the original - but instead just adding more:

INSTANCED_VSO VS_MAIN(INSTANCED_VSI input, float4x4 InstanceTransform : TEXCOORD2)
{
INSTANCED_VSO output;

float4x4 WVP = View * Projection;

float4 WorldPosition = mul(input.Position, InstanceTransform);
output.Position		 = mul(WorldPosition , WVP);

return output;
}


Results:
- still renders nothing, absolutly no change in rendering results. FPS still drops when reacting to 10000 positions forced into view frustum - whil still rendering as invisible.

- still assuming I am initializing my geometry incorrectly when copying from the original model.

- A dont know why the original model's VertexDeclaration Stream Elements define a second Texture Coordinate VertexElement , I could probably use some kind of enlightenment on this.

3DSMax FBX Exporter ( 2012 )

### #5unbird  Crossbones+   -  Reputation: 8262

Like
0Likes
Like

Posted 04 August 2013 - 04:44 AM

Look at the example you linked to more closely. If you send a full world matrix you need to transpose it, either beforehand when filling the instance stream or - as in the example - by transposing in the shader. This is because the mul(float4, matrix) needs the matrix layed out in column-major (For shader parameters like View/Projection this is happening automatically with the effect framework).

Why there's a second texcoord ? Well, if that mesh needs/has one, so be it. Could be for e.g. multitexturing. Anyway you made it right by offsetting so the semantics don't collide.

I had a look at the reflected XNA code. You should get exceptions if the drawing fails. Still, the debug runtimes gives more clues about the why.

Hmmm, what is FinalElements for ? I don't think you need to setup the combined declaration manually, at least not for XNA 4.0 since the VertexBufferBinding/VertexBuffers will take care of that for you (see here).

Edited by unbird, 04 August 2013 - 04:50 AM.

### #6MoruganKodi  Members   -  Reputation: 127

Like
0Likes
Like

Posted 04 August 2013 - 05:21 AM

OK - even though I figured PIX would be difficult to use - it gave me the verification I needed .... My geometry is getting messed up, and now I am convinced I am initializing my geometry incorrectly:

Heres what I did:

First - I set my test scene to pass only one Instance position to my draw call - so that it should draw only one fighter model using the instanced draw routines.

Next - I had my shader ignore the matrix  transformation but simply using Output.Position = Input.Position:

INSTANCED_VSO VS_MAIN(INSTANCED_VSI input, float4x4 InstanceTransform : TEXCOORD2)
{
INSTANCED_VSO output;

//float4x4 WVP = View * Projection;

//float4 WorldPosition = mul(input.Position, InstanceTransform);
//output.Position		 = mul(WorldPosition , WVP);

output.Position = input.Position;
return output;
}


Then - when I ran it - I got  the first image result attached. Holy hell, now we see something. But the model mesh is now broken in this geometry buffer obviously ( see the Fighter Model in the previously attached screenshots from the Non-Instanced draws ).

I managed to isolate the Draw call in pix after running it again to test using F12 capture.

I also tested this WITH my matrix transformation -  which appears to be working ( the broken mesh data is now simply transformed... )

The results - . I dont know how to fix this, but now im convinced that I am definetly not copying my model's data corectly.

(Skybox is not drawn as instanced, only the fighter model)

The model is supposed to look like this:

### #7MoruganKodi  Members   -  Reputation: 127

Like
0Likes
Like

Posted 04 August 2013 - 05:29 AM

Look at the example you linked to more closely. If you send a full world matrix you need to transpose it, either beforehand when filling the instance stream or - as in the example - by transposing in the shader. This is because the mul(float4, matrix) needs the matrix layed out in column-major (For shader parameters like View/Projection this is happening automatically with the effect framework).

Why there's a second texcoord ? Well, if that mesh needs/has one, so be it. Could be for e.g. multitexturing. Anyway you made it right by offsetting so the semantics don't collide.

I had a look at the reflected XNA code. You should get exceptions if the drawing fails. Still, the debug runtimes gives more clues about the why.

Hmmm, what is FinalElements for ? I don't think you need to setup the combined declaration manually, at least not for XNA 4.0 since the VertexBufferBinding/VertexBuffers will take care of that for you (see here).

Sorry - I responded and didnt notice your response before I posted. In my own project I have corrected my shader now. However my geometry is now obviously broken before I send it to the  GPU.

The reason I tried a combined Elementstream - was because the ROE example seems to be doing the same thing.

After reading your post I tested both by original elemenstream and the finalstream now that I can actually see something ( the broken geometry ) .  Both results are exactly the same on display

I'm excited not that things are actually telling me what's wrong ( specifically - how pix showed me what is happening to my geometry ) ...

The problem now Is figuring out why my geometry is so broken.

Excuse my typos - I didnt sleep last night.

Edited by MoruganKodi, 04 August 2013 - 05:30 AM.

### #8unbird  Crossbones+   -  Reputation: 8262

Like
0Likes
Like

Posted 04 August 2013 - 05:34 AM

No problem. Congrats you got familiar with PIX so fast.

That really helps. You said the original mesh has a second texcoord. Make sure you use an appropriate vertex struct ( VertexPositionTexture only has one texcoord). XNA probably does not come with a suitable struct, so you need to define one yourself.

Actually I wonder if you need to copy at all. You might as well bind the original buffers from the mesh part.

Edit: Nah, scratch that. I just realized you apply the bone transforms, not just copy.

Edited by unbird, 04 August 2013 - 05:40 AM.

### #9MoruganKodi  Members   -  Reputation: 127

Like
0Likes
Like

Posted 04 August 2013 - 06:44 AM

- I wish I had known about PIX before. The only problem is what I "guessed" was my problem before, was only confirmed by pix... regarding my geometry.

Yeah - because I don't need animated bones, I applied them directly to the mesh when trying to copy.

Note - I was using the wrong ( simpler) model with the instanced test, but the results are the same regardless of what model I load into my OSModel heirachy.

I also tested with bone transforms call commented out - mesh is broken either way ( and I dont think I applied the transforms wrong anyhow ).

The following screenshots from pix are with the shader applying color from TEXCOORD0, in addition to using the InstanceTransform.

Also - see what Texcoord1 looks like in pix? , it's just a single float always evaluating to zero. D:? I can ignore this, but who knows when I may actually need that extra TEXCOORD.... ( this is when I am only sending my extra stream elements, however still with the offsets assuming that TEXCOORD1 is populated, which I will change later - I'm now just worried about the geometry problem )

And just to be safe - my VertexShader function looks like this:


INSTANCED_VSO VS_MAIN(INSTANCED_VSI input, float4x4 InstanceTransform : TEXCOORD2)
{
INSTANCED_VSO output;

output.Position = mul(input.Position, transpose(InstanceTransform));
output.Position = mul(output.Position, WorldViewProjection);
output.TextureCoordinate = input.TextureCoordinate;
// output.Position = input.Position;
return output;
}


The results:

Wireframe output from pix:

And

Render:

Alot of vertices appear to be converging on approximatly 0,0,0....

This is frustrating. I cant figure out why my geometry is broken.

Edited by MoruganKodi, 04 August 2013 - 06:49 AM.

### #10unbird  Crossbones+   -  Reputation: 8262

Like
0Likes
Like

Posted 04 August 2013 - 07:21 AM

The instancing streams looks bad, too (TEXCOORD5). I'd recommend inspecting the data you send manually from the VB side (log it in tabular form, so you can compare it easily with the PIX table). Also check if the stride (last paramter of SetStreamSource) matches the vertex struct size (in C# Marshal.SizeOf(typeof(VertexType))).

Also, in PIX you can inspect all states and resources by clicking on the "hyperlinks" (device, textures, buffers, declarations). Especially the buffers and the declarations are of interest. For the buffers you can first provide a HLSL-like definition to get it into the type-correct tabular form.

### #11MoruganKodi  Members   -  Reputation: 127

Like
0Likes
Like

Posted 04 August 2013 - 07:22 AM

Fixed one more thing - the reason texcoord1 was being seen as a float was because my offsets were slightly wrong ( one short short):

It was fixed by adding additional offset evaluating to the length of a float:

Offset = OriginalVertexElements(OriginalVertexElements.Length - 1).Offset + SizeOfFloat

However, the second texture coordinate was evaluating to (0,0) for all instances, so I chose to replace it instead ( starting my World Transform matrix at TEXCOORD1 instead of TEXCOORD2 )

I can now also verify my world transforms are working, as each instance ( there are 1001 positions in this screenshot ), is positioned correction ( random scale applied o each instance, and random orientation )

Your help here has helped me step foreward considerably on this issue, so far. Thank m8.

All that remains now is figuring out why my geometry is broken. I now have a clear understanding of what was wrong with everything else except my broken geometry problem. =(

I'm hoping someone will know of an article that correctly explains how I should have initialized my geometry so the rat nests I am rendering now can be fixed - because it seems simply copying the Vertices and Indices doesn't work the way I expected it to.

Game Render:

Pix ( which doesn't show the skybox because my skybox uses a separate rendertarget )

what the mesh looks like in pix:

Wireframe:

Edit: Corrected screenshots

Edited by MoruganKodi, 04 August 2013 - 07:28 AM.

### #12unbird  Crossbones+   -  Reputation: 8262

Like
0Likes
Like

Posted 04 August 2013 - 07:28 AM

You're getting there, instancing stream looks now good.

Edit: You know what ? I'd actually go a step back and draw the model normally without instancing at all. Then inspect with PIX and check what declaration (and strides) you get. I actually wonder if one can have different declarations for different parts. Maybe that's a another problem.

Edit2: Hmmm, my suspicion might hold. Get the vertex buffer from the ModelMeshPart and inspect the VertexDeclaration thereof (and show us).

Edited by unbird, 04 August 2013 - 07:56 AM.

### #13MoruganKodi  Members   -  Reputation: 127

Like
0Likes
Like

Posted 04 August 2013 - 08:22 AM

The trouble here is that on my model ( in the non instanced exampled seen in the opening post ) , I needed to use different blendstates (engine glow built into the model requires alpha ). I also would like to be able to access individual mesh parts when rendering because I want some of my ships to have a spinning gravity ring without having to load a seperate model just to do it.  Also some of my models will be huge, ( so big they could potentially extend all the way up to the end of my camera's view distance ) - in which I intend to LOD/OccludeTest individual mesh parts of a model ( large portions of supercapital ship, would be outside the camera's view frustum if you get close to it ).

While unsure how to get VertexStride and other buffer related data ( besides just the buffers themselves ) out of PIX, heres a check of what things look like to pix with my non instances test gamestate:

Primary Rendertarget from the end of the Final Compositor's compositing stage:

Planets and Ships render to two seperate geometry buffers in my project ( planets require a much much larger draw distance )

What the fighter looks like in Pix:

What my Planet looks like in Pix:

Edit: Original Vertex Declaration

Edit: My Vertex declaration for comparison:

Not sure exaactly what it means, but I assume I should have manually specified a new vertex stride to accommodate my  extra elements?

Im not exactly sure exactly what my Vertex Stride needs to be.

Edit: Also noticed now the positions of vertexes differ from the non instanced draw, as seen

Edit: This was being posted as your reply came in. Will try using VertexPositionNormalTexture

Edited by MoruganKodi, 04 August 2013 - 08:45 AM.

### #14unbird  Crossbones+   -  Reputation: 8262

Like
0Likes
Like

Posted 04 August 2013 - 08:43 AM

Well, I actuall meant VertexDeclaration.GetVertexElements. But now it's already obvious from that PIX screenshot. The vertex has normals so you should rather use VertexPositionNormalTexture when using VertexBuffer.GetData.

### #15MoruganKodi  Members   -  Reputation: 127

Like
1Likes
Like

Posted 04 August 2013 - 09:04 AM

I changed it to VertexPositionNormalTexture .

Last PIX run final rendertarget:

The first MeshPart of the above fighter model:

Second and Third:

Also did an output with the shader set to translate normal to color, but I think this doesn't matter:

While I can see differences in vertex positions compared to the original non instanced geometry's vertex positions, I am totally failing to understand still how they could be so different though.

Edit: Above numbers are with bone transforms applied, my mistake. This is the first mesh part without bone transforms applied:

Edited by MoruganKodi, 04 August 2013 - 09:13 AM.

### #16MoruganKodi  Members   -  Reputation: 127

Like
0Likes
Like

Posted 04 August 2013 - 09:23 AM

When staring at my PIX screenshots this just hit me:

My semantics are getting scrambled. Vertex positions are mixing into the Normals and texture coordinate channels....  But why =( [edit - i think I understand why ]

Edit: -------------------

The two different models I have tested with supply different elements ( also, the fighter above has 5 total mesh parts across 3 meshes, while the other only has one total)

The elements supplied by the first original fighter ModelMeshPart: (which should be the same across all mesh parts )

And elements from the other simpler model - the model were the strange extra texture coordinate was coming from:

Which I dont understand, because both were modelled in 3DSMax and exported using the exact same FBX Exporter configuration in 3DSMax.

However with both models I get the same anomoly were vertex positions are mixed into the other element channels, in addition to being mixed in with other ( not needed ) elements. ( I dont need binormals or that pesky extra texture coordinate in this project)

The solution to my problem is now figuring out how to fix this problem and get onlt Position, Normal and TextureCoordinate into the output vertex buffers and work exactly the same for all models that I supply as long as they have the 3 elements I need, while discarding all extra elements.

If I figure this piece out now - then my problem is 1000% solved,.

But yeah - your suggestion to use PIX  - Im loving this utility already. Makes no sense why no one else has ever mentioned it to me before.

Edited by MoruganKodi, 04 August 2013 - 09:45 AM.

### #17unbird  Crossbones+   -  Reputation: 8262

Like
0Likes
Like

Posted 04 August 2013 - 09:46 AM

Your original vertex data also has tangents and binormals. Make sure you have a struct which matches. It's probably like this:

[StructLayout(LayoutKind.Sequential)]
public struct VertexPNTTB
{
public Vector3 Position;
public Vector3 Normal;
public Vector2 TexCoord;
public Vector3 Tangent;
public Vector3 Binormal;
}


I can't help you with 3DSMax, but I'm not surprised a model can have different formats. Either you go further with this, meaning "force" it to have one format only on the API side by converting/copying, or alternatively use the buffers and formats as-is like already suggested. For the latter you need to provide an additional matrix for the bone (shader constant) and draw the subsets separately. Maybe actually a better idea: Sounds like the parts need different states (blending) and maybe even different shaders anyway.

Nice fighter, by the way

PS: Yep, PIX is great.

### #18MoruganKodi  Members   -  Reputation: 127

Like
0Likes
Like

Posted 04 August 2013 - 11:52 AM

Your original vertex data also has tangents and binormals. Make sure you have a struct which matches. It's probably like this:

[StructLayout(LayoutKind.Sequential)]
public struct VertexPNTTB
{
public Vector3 Position;
public Vector3 Normal;
public Vector2 TexCoord;
public Vector3 Tangent;
public Vector3 Binormal;
}


The trouble though is thet the other model doesnt for some reason. I would like to eliminate Tangent and Binormal because none of my shaders actually use them ( while also eliminating the extra texturecoordinate the second model supplies ).

They way I thought about it was like this:

VertexBuffer.GetData(of Single)( temp, 'not sure how to calculate length' )

... with all of the original elements in a 1 dimensionall array, and coolecting only the Position, Normal, and TextureCoordinate from it, and discarding the rest. To completly replace the original elements.

this is under my understanding that using GetData to retrieve floats instead of structures will contain floats in this order :

1, Position ( 3 floats )
2, Normal ( 3 floats )
3, TextureCoordinate ( 2 floats )
...
4+ everything else

... loop -->

Dim Vertex As New VertexPositionNormalTexture

Vertex.Position = New Vector3(RAW(I), RAW(I + 1), RAW(I + 2))

Vertex.Normal = New Vector3(RAW(I + 3), RAW(I + 5), RAW(I + 6))

Vertex.TextureCoordinate = New Vector2(RAW(I + 7), RAW(I + 8))

' skip the unwanted data

I += StepSize

... <-- loop

so that regardless of how many elements a model loaded from FBX has declared, I would have replaced it with a vertex declaration that only contains the elements Position, Normal, TextureCoordinate, so that in the future if I encounter another model that defines unusual extra elements ( like a second texture Coordinate ), then It would have those stripped so that I can maintain an exact output element stream that is the same for all models.

EDIT:

The above worked exactly as I was hoping it would - by removing any elements manually that dont fit in VertexPositionNormalTexture, and fixed the errors in my VertexStream elements:

While there is probably a better way to do it, it at least allows me to prevent errors against models with variating element lengths - and ill probably need to improve on it:

I created this new subroutine:



Private Function Get_Vertices(Part As ModelMeshPart) As VertexPositionNormalTexture()
Dim OriginalElements As VertexElement() = {}

OriginalElements = Part.VertexBuffer.VertexDeclaration.GetVertexElements

Dim RAW As Single() = {}

Dim I As Integer = 0

Dim Result As New List(Of VertexPositionNormalTexture)

Dim StepSize = 8 ' number of floats per vertex

' TODO: maybe check all types?
Dim Length = 0
If OriginalElements.Length > 3 Then
StepSize = 0
For Each E As VertexElement In OriginalElements
If E.VertexElementFormat = VertexElementFormat.Vector2 Then
StepSize += 2

End If
If E.VertexElementFormat = VertexElementFormat.Vector3 Then
StepSize += 3
End If
Next
End If

Dim L = (Part.VertexBuffer.VertexCount) * (StepSize)

Array.Resize(RAW, L)

While I < RAW.Length - 1
Dim Vertex As New VertexPositionNormalTexture

Vertex.Position = New Vector3(RAW(I), RAW(I + 1), RAW(I + 2))

Vertex.Normal = New Vector3(RAW(I + 3), RAW(I + 4), RAW(I + 5))

Vertex.TextureCoordinate = New Vector2(RAW(I + 6), RAW(I + 7))

' skip the unwanted data

I += StepSize

End While

Return Result.ToArray
End Function


And moodified my _Initialize_Geometry function the following:

.VertexBuffer.GetData(Of VertexPositionTexture)(Result_Vertex)


is now changed to

Result_Vertex = Get_Vertices(Part)



- were each vertex now only contains Position, Normal and TextureCoordinate , and remaining elements being discarded.

My code can do with a cleanup now - but now it's at least rendering the mesh correctly.

A bit more testing to try and verify weather Normal is stored before TextureCoordinate, or the other way around - just to make sure everything is correct... Texture Coordinate is working perfectly.

Pix Results:

I really appreciate the feedback. Your help was great.

Edited by MoruganKodi, 04 August 2013 - 12:31 PM.

### #19unbird  Crossbones+   -  Reputation: 8262

Like
0Likes
Like

Posted 04 August 2013 - 12:09 PM

Congrats, you're getting closer.

Hmmm, yeah, VertexBuffer.GetData has some overloads, but one still needs some type (currently your float). Pity one can't work with the blob directly, with VertexDeclaration.VertexStride and the offsets from the elements one could do this generically (see here for something along those lines). You could probably use a GetData(Of Byte) and some marshalling tricks to achieve that, but I doubt it will be simpler/easier than what you're doing now.

### #20MoruganKodi  Members   -  Reputation: 127

Like
0Likes
Like

Posted 04 August 2013 - 12:41 PM

edited previous post  D;

Im pretty confident now I can start implementing and experimenting with new features and carry on by myself again ( after I commit my source code to my repository  - in case I break it ) - in addition to looking at how I could implement LOD with instancing.

I am pretty curious about using COLOR1,COLOR2,COLOR3 on a per instance level as well ( which is what I originally intended ). I think I could perhaps have my effect objects notify models of extra elements they would expect - or something....

And I can now update my existing shaders to make use of the instancing.

It feels a bit wierd because up till now everything else in my framework was written without help. But getting feedback definetly has it's perks - especially if it gets me looking at things differently  or using tools I never knew existed that end up saving my brain from melting into a puddle of radioactive glowing yellow goo..

Once again m8, thanks for the feedback.

Hopefully in a few weeks my next forum thread here will be me showing off basic game-play features. Writing my game framework is full of l learning experiences.

Edited by MoruganKodi, 04 August 2013 - 12:48 PM.

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

PARTNERS