Editor : dynamic asset creation and prefabs, best practice ?

Hello,

I am creating an editor extension (a voxel editor for creating models).

There is a lot of dynamic assets creation involved (texture, mesh, material, data)

I’ve managed to overcome most of the problems working with the editor using a lot of googling, and it is basically working, but I don’t know if I handle asset creation the right way.

Model creation :

  • create data asset (scriptable object stored in a .asset file)
  • create mesh asset (mesh object stored in a .asset file)
  • create texture asset (texture .png)
  • create material asset (material .mat)

Start editing model :

  • duplicate existing data asset (new .asset file)
  • duplicate existing mesh asset (new .asset file)
  • duplicate existing texture asset (new .png file)
  • duplicate existing material asset (new .mat file)

Now, I guess you are asking : why duplicate assets every time ?

If the user creates a prefab with the voxel model, and edit an instance of the prefab, we need separate data or all prefab instances will be updated, even if the user never click “Apply”.

Of course because of that, every time we start editing a model a lot of slow IO takes place. Also a lot of unused asset files might be generated.

Is there a better way to handle dynamic asset file creation / prefab generation ?

I know prefab need physical asset files to retain the link when the prefab is created, but gameobjects do not. Is there a way to generate the files only when the prefab is created by the user ?

Thanks a lot for your time

Hi joel,

I’m doing dynamic meshes too and the problem is, that Meshes are not serializable, so they don’t save with the scene and don’t survive the game start in the Editor or dragging the gameObjects containing them into the project to create a prefab. This also means they don’t support any kind of Undo.

There are different ways to overcome this.

For me, the created mesh is easy to recreate - I only need to detect the following events, because they need to be handled and the System doesn’t always tell me when they happen:

1 Duplication in Hierarchy
I need to handle this, because everything that is serializable gets properly duplicated - that is: serialized and deserialized after which you have a separate copy of everything, BUT: you sharedMesh just gets referenced - so now you have something that is partly an instance, so changing parameters on any of the gameObjects, recreates the same (one and only) sharedMesh. So Depending on when you recreate your mesh, you see the following:

  • Recreation on request/parameter change => both meshes change (again: because they are the same referenced sharedMesh)
  • Recreation every Frame: The same mesh gets recreated twice a Frame and you only see the last one . if you deactivate the last script for tests, you see the first one while the second is deactivated.

Now I haven’t talked about mesh/sharedMesh yet, but you need to use the later, or the Editor duplicates or instances the mesh in sharedMesh and then complains about meshes being leaked.

2 GameStart

  • That’s easy - this actually gets communicated with Awake() and Start(), so I can create a sharedMesh and create my dynamic Mesh.

3 Instantiating a copy from Prefab while in Editor or while the Game runs.
Same as for GameStart, I get Awake/Start


Now in your case, you said you have a voxel editor. If by this you mean that you have data structures like a 3D Array and you can completely regenerate your mesh from this(with marching cube or whatever), you basically are in the same position as me, where you don’t actually need to save the mesh, because you can recreate it anytime, but you may still want to be able to bake the Mesh, so you don’t have to do complicated calculations at runtime on mobile for example.

So that’s the way I’m goign at the moment. Recreate the mesh every time it’s not there(new, new from prefab, gamestart etc.) or the parameters changed and allow to bake it, which saves a copy of the mesh as assets, which then automatically will be linked in prefabs I create, so that when I instantiate the prefab, the mesh is already there, so my normal checks if there is a mesh work out and I don’t recreate the mesh.
Now for duplication, I need to find out if two objects point to the same sharedMesh and if so, I simply give the new object its own sharedMesh.
I also need to create a new sharedMesh if I started with a baked Mesh, but want to then dynamicly change something. I should not use the baked mesh any longer or I will change the prefab forever.

However, if your Mesh is your number one citizen that you’re editing, it gets more complicated, because then you need to have some other way of implementing Undo and Undo is needed for Tools you want to sell in the AssetStore.
The GameDraw (see AssetStore) guys implemented their own serializable copy of Mesh for this, so they don’t need to handle Undo themselves, but can let the editor do it. You basically have your own editor, so all the low-level data in your ScriptableObject doesn’t show, but write all changes to the ScriptableObjects, sso you get Undo almost for free.

Another thing I wanted to have a look into, but didn’t had time yet is AssetModificationProcessor

So… that’s pretty much my initial brain dump to this issue. I probably forgot something…
More ideas anyone?

Ah yes… another thing: Why duplicate all the time? The user is working on his model - you don’t need every step saved or do you?

Also regarding my baking mesh process - I wanted to do this in a way that I can reuse the same mesh if mutiple objects with the same parameter get baked.
I’m planing on doing this by implementing GetHashCode for my parameters and then create a hash of all parameters (All parameter hashes XORed) and add this in the asset name…

So it will be something like:
TypeName_BakedMesh_3482497653.asset

where 3482497653 would be the Hashcode, so if I bake a Mesh with the same Parameter Settings, I can check if the asset already exists and if so, just link it, not resave it.

This also helps with baked Meshes that are put in dynamic mode again, but then reverted to the same parameter settings and baked again - those also will not duplicate mesh assets.

hello, thanks for your reply :slight_smile:

Your guess is correct, I have voxel data that I use to create the mesh dynamically.

At first, I also thought I did not need to save the mesh data, as I can create it when I need it. But I realized that a mesh asset is needed to display the nice preview image when creating a prefab. No mesh asset, no preview.

My main problem is the following :

  • create a voxel model
  • drag the gameobject to a project folder to create a prefab
  • create several instances of the prefab
  • modify one of the prefab instances to be different from the prefab (by adding some voxels)

The modified prefab instance should have a different data and mesh from the prefab and other prefab instances. Do you have the same problem ? How do you handle this without having to duplicate the data everytime you edit a model ?

Ok, I found a neat little function that tells me if an object is a prefab or not : PrefabUtility.GetPrefabObject

This will allow me to duplicate asset data only when editing an prefab instance, which will save a lot of assets creation.

When duplicating a gameobject in the hierarchy (CTRL-D), I don’t need to duplicate the data, as editing one model should modify all the copies anyway.

I understand that you want the Mesh saved for the preview, but why not edit that saved mesh? Why create copies of it?
Prefabs in the original sense also doesn’t really make sense here, because it’s not like you have 20 values saved in the prefab and then you just change one. You basically change all the data there is when you edit the voxel model.

So you can basically treat all copies of your voxel Gameobject either as a copy and copy the voxel data in the background like you do now… OR… which would actually be better: Let the user know how this works:

  • I create a VoxelObject
  • Editor shows: “No Voxel Data” and you can link to a Voxel-Data-ScriptableObject or press Button [Create New VoxelData]
  • Then you can create one Mesh per ScriptableObject and also edit that Mesh and not save a new one when you save.

Then you also have no Problems with duplicates etc.

You can also detect that you are working on a Copy or freshly dragged in prefab, if you have a single nonserialized bool, that you set to true in the initialization, when you create it, but it will always be false if it’s duplicated or instatiated from a prefab.

Here, I found the video again: http://www.youtube.com/watch?v=Tb88xyxa9mE

He also talks about the AssetPostprocessor there.

When the user edit one instance of a voxel model, if I do not create a new copy of the mesh, all other instances will show the new mesh, which is wrong. Only the model the user is currently modifying should show a modification. Then the user can click “Apply”, which will set all the other meshes to the new one. This is the way prefabs work, this is what the user expects.

I am confused, is there another way to handle this without creating a new mesh ?

Well, they kinda don’t expect it for raw asset like Texture and Meshes etc.
And your VoxelData is like that. It’s like a Canva in 3D.
Your VoxelFilter(?) references it, your VoxelRenderer(?) renders it to Meshes, but it just has a current State, like a Texture

Yo can use a Texture in different GUI elements and those GUI elements can be prefabed, but the actual raw texture data is just referenced and not prefabed.

They don’t expect if for textures and meshes because you can’t edit them in Unity.

The problem is with dynamic asset creation we have the inverse workflow from the normal Unity workflow. Usually you import assets into Unity, and those asses won’t every change in Unity (texture, meshes, …)

For editor extensions that allow to dynamically create and update assets inside Unity, we need to be creative and do things a bit differently from the normal Unity workflow. I’m not sure how RagePixel handles it (texture edition), but I am sure it runs into the same problems.

I do not say you need to do the same thing, every editor is different, but for me it feels it is the right way to do it.

Well, anyway, I am now pretty happy with my assets workflow in my voxel editor, it works ok :

model creation

  • create data, mesh, texture and material
  • call start edit and stop edit quickly to save physical assets

start edit

  • if it is a prefab, duplicate data, mesh, texture and material

stop edit

  • create physical assets (data, mesh, texture, material) if they do not already exist

Next step is being sure Undo/Redo works properly.

Sure, it’s your call. Whatever works for you.

But I still have to disagree.
I don’t think Textures just don’t have Prefab functionality because you can’t edit them in Unity.
Check Terrain. It’s editable in unity, but it still doesn’t have Prefab Functionality. Every Terrain you create is bound to it’s own Terraindata it creates at the same Time. Duplicates or Copies dragged from Prefabs all point to the same Terrain data and this can’t be changed.
And even for the other Values like Material where you could use Prefab functionality, “Revert to Prefab” does nothing.

IMHO Prefabs only make sense where you can have incremental changes.

Example:
I drag a cube into the scene.
I configure:

  • Name=Pillar

  • Scale

  • Collider

  • Receive/Cast Shadows

  • Material

  • Now I create a Pillar Prefab

  • I drag 20 Pillars into my scene and customize Scale.y for everyone, so it fits my level design

No that only makes sense, because the Scale.y change was incremental, meaning all other Values it takes from the Prefab and then it changes the Scale on top of that.

This means I can still go to my Prefab and turn Shadows on/off for all Pillars etc.

Now, if changes wouldn’t be incremental, me changing the Scale, would create a whole new copy of the Pillar. Later changes to the Shadows wouldn’t go through anymore, because copies have their own Value of Shadows on/off, even if they are the same at the start.

Some Data isn’t changed incrementally, so Prefabs functionality doesn’t make sense.

Coming back to Textures: If I’d have a Texture of a Coat and I could create a Prefab where I just change the color of the few pixels that make up a gem on the coat, then Prefabs would make Sense… But usually a Texture isn’t layered like that, but just some binary Data that you take as a whole.
Even being able to edit it in Unity woudn’t change that.

You might be right, maybe it does not make a lot of sense to have the whole voxel data as a prefab attribute.

My goal was ease of use for the user. For example if I create a floor tile out of voxels, and I create an entire floor with tile prefab instances, I want the user to be able to quickly make one or more tiles to look different.

Maybe it is better if I provide a “Clone” button that would make a deep copy of the model data/assets ?

Yes, either that, or let the user link/unlink/createNew/Clone the VoxelData used. That way, they could once they tiled the whole floor with the “prefab”, create a variation of Tile with Clone and then go back to some of the Tiles and just link the Variation, without having to move a new cloned Prefab.

That would put you miles ahead of Terrain and TreeCreator anyway…

mm… letting the user manually relink would be tricky, I would need to find the matching texture/mesh/material and relink them as well automatically. It could work if I name all the assets for a model with the same name though (this is not the case atm), or use a hashcode as you mentioned, but it would create very long and not user friendly asset names.

Have your Voxeldata link to the matching Texture/Material etc.

And your Voxel GameObject link to your Voxeldata.

The Voxeldata then can be renamed from you Voxel-GO-Editor or named when you Clone/CreateNew.

This way all the Data stays together as a chunk from the users perspective and when he wants to link to a Variation, he just links to VoxelData_Tile_Variation2b, which has the links to the matching textures etc.

The user just has to link one thing and can’t match the wrong texture to the right voxeldata etc.

But if he wants, he can still click on the Voxeldata and see in its Editor which Material and Textures are referenced etc. and change those if he knows what he’s doing.

That’s a good idea, but the problem is the texture and material are directly referenced by the material component and the mesh by the meshfilter component, so it would not work without some button action that would remap the data. I think that would be confusing for the user.

I think I will stick to the “Clone” button idea and forget the data relinking.

Ah, I see the problem.

A last option would be your VoxelBehaviour to kinda own the Mesh Filter and Mesh Renderer

  • Rename them: I know MeshFilter shows its name, not sure about the MeshRenderer. So name them “VoxelMeshFilter” or “VoxelMeshFilter(Don’t touch)”(optional), close them in the inspector - I think there is a method somewhere.
  • show Mesh-Link and Material-Links etc. in your VoxelData-Editor.
  • so the actuall links are kept in the MeshFilter/MeshRenderer, but the VoxelDataEditor lets them edit you as if they were his and it also keeps track of the last linked Mesh and Materials, so even if the VoxelData gets unlinked from all VoxelBehaviours, it still knows its last Mesh/Material combo and if it gets relinked, the MeshFilter and MeshRenderer can be configured to match

For this to work in Editor and in Code, you just provide a Property for the VoxelData. Your Editor can show a simple VoxelData-Linkfield for it, but call the Property to set it, so the Property gets called both times and can setup MEshFiler and Renderer

For my tool I recently built,

I first had a main ScriptableObject serialized to disc and then voxel data as ScriptableObjects and every mesh instance all serialized to disc as children of the main asset.

After many changes I came to realize Monobehaviour is king of the castle when it comes to usability and features in regards to editor extensions

So I changed all to Monobehaviours instead and did not serialize anything to disc - they are already serialized in the scene and working on them like that using custom inspectors is way faster.

Only when I generate the final model from this editable rig do I then create a prefab and serialize.

I only had one drawback from this set up and that is when you delete the editable objects from the scene , unity gives that annoying “leaked” message. This is in fact harmless as unity is aware of the unreferenced objects and has cleaned up the objects … just like the message says, pointless really.

The message is also ( extremely annoying ) output each time you save thereafter … that is until you enter/exit playmode just once. Doing that will clear the console reports for good.

I tried tested and searched long and hard for a means to handle this but to no avail, the problem is that its is impossible to detect a delete action done by the user alone. I even created a thread here in the forums about this and nobody answered with a working solution.

anyway I decided this is acceptable for now until some solution pops up, its harmless in the end.

You don’t have to leak Meshes, then you also don’t get that message.

Only use filter.sharedMesh and make sure it’s not null. If it is, create a new Mesh and assign it to sharedMesh

The only problem is as explained above: if you duplicate a GameObject, it does do a shallow copy, so you reference the same sharedMesh wheras you actually want you own mesh.

To solve this problem, I keep a static dictionary of MonoBehaviour/Mesh pairs and if a MonoBehaviour with the same InstanceID says it references the same sharedMesh, I create a new one for this MonoBehaviour.

I am using sharedMesh… I still get the error message

just wondering now if perhaps the problem is I have a reference to the mesh stored in the component itself and then in my method which creates the geometry for the component i create the mesh to this reference and then assign it to shared mesh:

public Mesh voxelMesh;

..
..
..

void SomeMethod(){

    voxelMesh = new Mesh();
    ..
    ..
    ..
    meshFilter.sharedMesh = voxelMesh;


}

I need to investigate it again , but im wondering if this reference also serves me well in not having my objects lose reference when unity does its whole shebang between playmodes scene loads open /close etc…

The Problem is that you probably created a new Mesh each time you generated it…
There’s no need to.

Just do a mesh.Clear() before you start.