Instancing

Nov 8, 2011 at 11:48 PM
Edited Nov 9, 2011 at 1:30 AM

I'm currently trying out the instancing within the framework with an aim to adding skinning functionality as per this demo http://ionixxgames.wordpress.com/2009/03/25/xna-sample-skinned-model-instancing/ however i've fallen at the first hurdle, and get an exception "example is written to use the VertexPositionNormalTextureBump format" within the InstanceModel.VerifyVertexFormat method. I'm using the EnemyBeast model from the codeplex XNAAnimation project and the model is definitely set to use Model - Sunburn Lighting System as the Content Processor. Any suggestions as to where i've messed up...my test code at the moment is as below - By the looks of that error message i'm guessing that code is a bit of copypasta from the Sunburn instancing sample so that will probably be my starting point now to figure out what is going on but if you've got any ideas to speed things up and point me in the right direction ?

EDIT - Issue may be related to the model, replacing it with a simple cube model exported from blender and everything work, slightly odd though since i've successfully used that model with Sunburn in the past.

EDIT - Just tried with the dude model from the App Hub skinning sample and he doesn't work either (Sunburns new resource system seems broken for me at the moment so can't use their model)

FINAL EDIT - Ignore this i've nailed it - bit of a facepalm moment...the instancing code is all configured to use VertexPositionTextureBump vertex elements rather than VertexPositionTextureBumpSkin.

        public void InitializeInstancing()
        {
            Model spiderBeast = Application.ContentManager.Load<Model>("Models\\SpiderBeast\\EnemyBeast");
            InstanceModel spiderBeastInstanceModel = new InstanceModel(spiderBeast);
            DeferredObjectEffect instanceEffect = new DeferredObjectEffect(Application.GraphicsDevice);
            _instancingManager = SunBurn.GetManager<IInstancingManager>(true);
            _instanceFactory = _instancingManager.CreateInstanceFactory(spiderBeastInstanceModel,instanceEffect);
            _instanceEntity1 = _instanceFactory.CreateInstance("TestInstance1", Matrix.Identity);
            Application.SunBurn.ObjectManager.Submit(_instanceEntity1);
        }

Coordinator
Nov 9, 2011 at 7:31 AM

Seems like you found it ;)

As you potentially noticed looking at the source code, the Instancing system found in IGF wraps most of the SunBurn Instancing sample structure and therefore uses the SceneObject skin bone indices to reference up to 75 instances per SceneObject (due to the 75 bones limit in the skinning support).

Not sure, it can be used for skinned models right away since I never played with it so far but if you get anywhere with it, it'd be cool if you could share some screenies or videos ;)

Thanks

Philippe

Nov 9, 2011 at 9:05 AM
Edited Nov 9, 2011 at 9:07 AM

Cheers - I'll send you the code if/when i get it working. I don't think i can use much of the existing code since it hijacks the skinning stuff for instancing. The example i linked encodes the animation in a texture and passes instance transforms as shader parameters. My aim though is to follow the same code structure though so that it can potentially be included in the framework when it's done and has at least an almost identical API (ie have a SkinnedInstancingManager etc). Im doubtful if it's possible to have a generic method which handles both situations cleanly. I know it can be done http://www.youtube.com/watch?v=nQHSaJToE4k&hd=1 , http://www.synapsegaming.com/forums/p/1331/7054.aspx :)

Nov 9, 2011 at 5:11 PM
Edited Nov 9, 2011 at 9:59 PM

Good progress so far...got the shader and animation aspect pretty much working now (needs a it of refactoring to bring it into line with the static skinning API and integrate the animation playing into the SkinnedInstancedEntity). Little screengrab showing the skinning (and although you cant see it the animation) http://imgur.com/3Lqya - few issues coming up so far though - duplicating the mesh data just produces garbage at the moment and i need to deal with multipart models such as the XNA dude which has different textures for each meshpart in order to get the instancing to work for any model. Evening of reading and code digging tonight i think.

Coordinator
Nov 10, 2011 at 8:47 AM

Cool! Doesn't really matter if you can't have one single class to handle all cases.

If you can make it, I guess the best would be to have one unique InstancingManager, one InstancingEntity and modify IInstanceSource and create a InstanceSkinnedModel to be use by them.

Nov 15, 2011 at 2:38 PM
Edited Nov 15, 2011 at 3:15 PM

Quick progress update.....

http://imgur.com/HHtEs

All the technical work is now pretty much done - as you'll see from the screen shot stats it's rendering 1 sceneobject which is the instance container. It's rendering 36 meshes - this is because the spider model contains 9 meshParts (each of which could have a different material and therefore need to be instanced separately) and each of which are rendered 4 times by sunburn for deferred lighting.In this case though all the parts use the same material so the data could be optimised to take that into account.

Since the skinned model can actually contain a number of renderable meshes - I can't use IInstanceSource , so I was thinking extend InstancingManager with

SkinnedInstanceFactory CreateInstanceFactory(ISkinnedInstanceSource source, Effect shader);

then SkinnedInstanceFactory.CreateInstance returns SkinnedInstanceEntity.

Still to do...

Implement animation player functionality within SkinnedInstanceEntiry.

At the moment it is using shader instancing since it works on both Windows and XBOX - I'm not sure whether supporting WP7 is possible yet. I'll probably modify it at some stage to used hardware instancing on the XBOX, i can't think of a mechanism to do it on Windows since it requires using a 2nd vertex stream which i don't think i can do through sunburn.

EDIT - slightly more impressive screengrab - 40 spiders in a container http://imgur.com/VgdYv and 15 dudes in a container http://imgur.com/Rh0hQ (with working materials)

Coordinator
Nov 15, 2011 at 3:39 PM

Huge progress! Love the screens ;)

I believe we could consider changing the CreateInstanceFactory() method to a generic by first adding a Create() method to the IInstanceSource interface.

Then we could change/add a generic method to the InstancingManager class:

public InstanceFactory CreateInstanceFactory<TInstanceSource>(TInstanceSource source, Effect shader) where T:IInstanceSource
{
}

And inside this method make a call to the source.Create() which would return a valid typed instance to work with.

What do you think?

Nov 15, 2011 at 3:45 PM

That works for me -  I'll try and get the API stuff done tonight and start on some basic animation player functionality...once thats done i'll stick the code somewhere for you to download and have a play with.

Coordinator
Nov 15, 2011 at 3:53 PM

Thanks. Don't know if you are aware of it but you can submit patches to the project on the Source Code tab in the above menu.

There is an Upload Patch link on the top of the check-ins list.

Nov 15, 2011 at 5:39 PM

Ah cool i'll do that then when i've got the code in a suitable state - i have just got it running on my xbox with shader instancing - working on getting the xbox to use vfetch now instead which should be faster i think and should use considerably less memory since you don't have to duplicate the the vertex data. It would be nice to be able to use hardware instancing for windows for the same reason, but i don't think there is a mechanism in sunburn for accessing a secondary vertex stream. 

(I may ask on the sunburn forums when i get to that point) WP7 is a non starter at the moment since it requires a customshader.

One nice added bonus of encoding the animation as a texture is that you could use an InstancedSceneObject with a single instance to animate models with more bones than the limit in sgMotion and the animation itself is considerably less cpu intensive since bone transforms are all precalculated. (My main motivation for implementing this is so that i can finish this game http://www.youtube.com/watch?v=829fPOVhdv0 - i want a whole lot more beasties attacking - with sgMotion any more than around 20 animated creatures onscreen started to kill my framerate on the xbox)

Coordinator
Nov 16, 2011 at 8:51 AM

I like your idea of using your code even for an unskinned model, I have in my post 1.0 release task list a task to make Instancing feature using a custom shader to avoid the 75 instances per SceneObject current limitation. If your code can get us there, feel free to dig in ;)

Nov 16, 2011 at 12:15 PM
Edited Nov 18, 2011 at 10:14 PM

I've uploaded a patch with the work as it stands. At the moment the API is almost completely separate from standard instancing since i din't want to change any existing code for the time being. There is a major issue with the way i'm duplicating vertices at the moment - the code will break for models which don't have the expected vertex structure. I think i need to do some work in the content processor to fix this. I haven't done any optimization on the code yet so there is plenty of room for improvement in there.

Note - There is a bug in the patch which significantly degrades performance - when setting the instance transforms effect.Apply is called when it shouldn't be - commenting out this will dramtically improve the performance. I'm working on a modified content pipeline processor which will deal with any model at the moment and will post that and a version which can use vfetch instancing on xbox soon.

Nov 18, 2011 at 10:23 PM

I doubt that we can do anything better than the 75 instances for static geometry - The limit is actually smaller for this method since it uses shader parameters for the instance transforms and there is a hardware limit. I'm getting 50 instances per SceneObject. I'm not sure, but if we could use hardware instancing which uses a second vertex stream for the transforms it might be possible but i don't think Sunburn exposes any mechanism for doing this at the moment.

Nov 24, 2011 at 1:40 PM

I've now almost completed this - unfortunately for it to work seamlessly with all sorts of models with varying vertex channels, animations etc i now need to write a fairly complex content pipeline imported which is taking a bit of time to get right...

Coordinator
Nov 25, 2011 at 12:37 PM

That's fine. No need to hurry. That's my motto: make it right so that it gets clear, clean and clever ;)

Nov 27, 2011 at 2:33 PM

Just a quick video to show things in action - sorry it's a really poor quality video, but fraps etc demolish the frame rate on my laptop so it's shot with my phone camera running on the Xbox but shows the instancing in action - also in this case each instance has full collision and physics integration (farseer rather than sunburn since its only a 2d game really).  As above my current work is the content pipeline processor to split an process the animations and convert models into a form suitable for instancing. 

http://www.youtube.com/watch?v=AXfiHSCAyZY

 

Coordinator
Nov 27, 2011 at 10:07 PM

Great video! Can't wait to see this feature implemented in the framework and what other developers come up with ;)

Dec 3, 2011 at 5:39 PM
Edited Dec 3, 2011 at 6:01 PM

Hello I'm trying to use instancing too.

When I do this :

 

            SceneObject obj = new SceneObject(model, effect, "");
            obj.World = Matrix.CreateScale(50);
            Application.SunBurn.ObjectManager.Submit(obj);

 

I can see my box.

But when I try to use instancing :

 

            LightingEffect effect = manager.Load<LightingEffect>("Models/wood_planks");
            Model model = manager.Load<Model>("Models/box");

            InstanceModel instancedBlock = new InstanceModel(model);
            InstancingManager instancingManager = Application.SunBurn.GetManager<InstancingManager>(true);
            InstanceFactory instanceFactory = instancingManager.CreateInstanceFactory(instancedBlock, effect);
            InstanceEntity instanceEntity1 = instanceFactory.CreateInstance("TestInstance1", Matrix.CreateScale(50.0f));
            Application.SunBurn.ObjectManager.Submit(instanceEntity1);

There's nothing on the screen !
Do you have any idea ?
Stats tell me that the box is rendered.
Do you think the material doens't work with instancing ? 

Thank you for your (really good) work

EDIT : I've watched it with pix and it sounds like the world transformation isn't applied ! Why ?
( Changing the camera I can see my box : the transformation is the problem :/
Any idea ? ) 
Dec 4, 2011 at 2:08 PM

For the standard instancing to work you needed to set effect.Skinned = true on your material effect.

Dec 4, 2011 at 8:06 PM
bamyazi wrote:

For the standard instancing to work you needed to set effect.Skinned = true on your material effect.

It does the job. 
Thank you !