Search Unity

Decal System

Discussion in 'Assets and Asset Store' started by Dantus, Jun 29, 2012.

  1. sburgoon

    sburgoon

    Joined:
    Jan 29, 2012
    Posts:
    38
    I'm not manually combining anything, but that's what Static Batching does automatically. Basically, if static batching (pro feature) is enabled, you can't access any mesh data from static objects anymore. It's a known limitation that, like I mentioned, we ran into already with A* Pathfinding (by aaron granberg) when trying to rebuild navmeshes at run-time. The solution was to manually build meshes for all the primitive colliders, and use them instead of the render geometry for static geometry. He ended up building that into the newer releases, but for your case, it makes more sense to just let the user do it, since it only effects when trying to do run-time dynamic decals. Might be worth putting in a warning though, since I can imagine people running into the issue and not knowing what's going on, and why they can't access the mesh (I never got a good explanation of why they block read access from unity, just that they do). Like I said, i've already written the collider mesherizer for my purposes (I only needed box colliders, so it was easy for me), but anyone else who stumbles on this, you have three options, disable static batching (not really worth it most of the time), use mesh colliders for everything (instead of primitive colliders) since they can still be read at runtime, or build meshes when needed (and cache them out by object instanceID in a hash) for any static objects with primitive colliders.

    -Sean
     
  2. sburgoon

    sburgoon

    Joined:
    Jan 29, 2012
    Posts:
    38
    SOLVED: (all on my end)

    Leaving the full comment here in case anyone happens on it, but it ended up all being my inability to correctly use a hash-table. hashing by InstanceId is great... until you have two separate systems needing to keep a reference to the same object... anyway, nothing to see here, just me being silly.

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

    Oh, and a question totally unrelated to my previous posts, is there an inherent issue with holding off on updating decal-meshes until the end of a frame? Each bullet hole that gets created in my game can create multiple decals (combination of static and dynamic objects as well), since it might cover more than one object, so instead of calling UpdateDecalsMeshes every time I create one, I've been batching them into a list, and then at the end of the event I update all of the needed decal meshes at once (I retain a pointer to the DS_Decals object and the DecalsMesh so I have a matching pair). This works 90% of the time, but suddenly, seemingly at random, I end up getting the following error:

    IndexOutOfRangeException: Array index is out of range.
    Edelweiss.DecalSystem.GenericDecalsMesh`3[D,P,DM].CalculateProjectedUV1 (Edelweiss.DecalSystem.GenericDecalProjectorBase a_Projector)
    Edelweiss.DecalSystem.GenericDecalsMesh`3[D,P,DM].RecalculateUVs ()
    Edelweiss.DecalSystem.GenericDecalsMeshBase.get_UVs ()
    Edelweiss.DecalSystem.Decals.ApplyToDecalsMeshRenderer (Edelweiss.DecalSystem.DecalsMeshRenderer a_DecalsMeshRenderer, Edelweiss.DecalSystem.DecalsMesh a_DecalsMesh)
    Edelweiss.DecalSystem.Decals.UpdateDecalsMeshes (Edelweiss.DecalSystem.DecalsMesh a_DecalsMesh)
    DecalManager.UpdateMeshes () (at Assets/CoreAssets/Scripts/Managers/DecalManager.cs:484)

    Since it's in the plugin, I can't really further debug it myself, so I was hoping you might have an idea of what might cause that. I'm using the same basic technique to create my decals as used in the bullet examples, and the only thing I changed that caused this sporadic issue was instead of running UpdateDecalsMeshes immediately after adding and cutting the mesh, I add the DS_Decals object and mesh to a queue that I then update once I've finished adding all the decals for that event (for example, if I have two static objects and the decal is created at the seam between them, I will end up creating a decal on both in the same for-loop, then updating just once at the end). I hope that makes sense. I'm assuming calling UpdateDecalsMeshes 5-10 times in a single frame is going to be bad for performance, and this batching in theory cuts that out, but I can't for the life of me figure out what's causing the error unless there's some requirement that the decalsmeshes be updated immediately.


    -Sean
     
    Last edited: Apr 16, 2013
  3. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Thanks for that input! I already integrated some read-only mesh warnings, but as you pointed out correctly, I have to extend it for static meshes as well to give the user the best possible feedback.
     
  4. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    Hi Dantus,
    I'd like to know if it's possible to refresh the decal by scripting, or force it always, because it seems only refreshed when it is selected into Unity.

    Thanks !
     
  5. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    You can certainly update the decals by scripting. Though the drawback it that there is no automatic refresh. The editor has a brute force algorithm to find the affected meshes, which is mostly not a good idea for runtime purposes. There is a bunch of runtime scripts that illustrate in detail how it can be achieved. The straight forward solution is obviously to use raycasting to find the meshes that are affected by the decal projector.
     
  6. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    Thanks for this very fast answer :)

    In my case, I've only one decal, which represents a tree shadow. The shadow is placed on the tree base, and the ground is not plane. This is why I'm using decal, to "fit" with the ground.
    I've to rotate around the tree pivot my decal, but it seems that the decal kept the original mesh, expect if I keep the decal projector focused into Unity.

    You're telling me there is a bunch of scripts that illustrate this, which ones?
    Do I have to use UpdateDecalsMeshes function from Decals?

    Thanks again :)
     
  7. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    You can pick any of the scripts. The central point for you is to understand how the DecalsMesh class works. Whenever you update the shadow, the whole thing needs to be recalculated. The example scripts update when you shoot, for you this will obviously not be the shooting. Other than that, it will be pretty much identical.
     
  8. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    Which means I've to Remove the projector, recalculate it, etc etc... ??
    If yes, I'm wondering about performances issues. My target is mobile platforms, from iPhone 4S/iPad2. I've nothing more than the decal and the tree/ground into my scene, but it could be too heavy for mobile platform.
     
  9. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Exactly, that's what is needed. Remove the projector, add projector, add meshes, perform the cut operation, ... .

    Considering the performance, there are lots of potential solutions if it is too slow.
    - You may precompute the decals.
    - Optimize the decal computation by not passing the whole mesh, but just a relevant part of it.

    Those kinds of optimizations are very project specific. In the case that you encounter performance issues, don't hesitate to contact me directly.
     
  10. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    An alternative is to use a projector from Unity, which could be better in my case at least.

    Thanks for the support anyway, here and on skype ;)
     
  11. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    You are right, projectors are always an alternative. When the shadow is constantly moving, projectors are probably a better solution. On the other hand, if the shadow is mostly not moving, decals are usually faster.
     
  12. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    For sure I'll use your fantastic system if I need it into our future project.
    Thanks for your amazing job and support, it's really appreciated.
     
  13. Hamesh81

    Hamesh81

    Joined:
    Mar 9, 2012
    Posts:
    405
    I have been doing some further testing of this system and I have a few questions:

    1) Can one of the example scripts be used to trigger decals based on gameobject "projectile" collision, since all the examples seem to be triggered via mouse click only. Would this be difficult to implement?

    2) I've noticed that the same decals are used no matter which objects are "hit", even though the decal sheet has separate "rectangles" in the editor such as "generic", "concrete" etc. I would like to be able to have only specific decals showing based on the kind of object that is hit. For example, objects tagged "concrete" would only show concrete decals, "wood" would only show wood etc.

    3) Correct me if I'm wrong, but the raycast seems to be coming from the camera. I think for my system this would be a problem since I use multiple cameras and switch between them at runtime. So I'm wondering is it possible to fire the ray from the player instead?

    Looking forward to your advice.
    Hamesh
     
  14. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Collisions of projectiles are usually handled with raycasts, because the collision detection doesn't work well with very fast moving objects. It would be difficult to implement it, simply to get a stable impact.

    This can certainly be achieved.

    Yes.

    Keep in mind that the Decal System is not a framework to help you integrate bullet holes. It provides you the functionality for the decals, but you have to create the rest on your own.
     
  15. devin8

    devin8

    Joined:
    Jul 15, 2012
    Posts:
    19
    Thanks a lot for this great framework.
    Is there a way to control mesh resolution. I am trying to make large scale decals to make an airport runway by using a tiling texture as described earlier in the thread. I will then make runway markings using another layer of decals. However, the mesh generated for base tarmac alone has very high resolution and will kill performance. Is there a way that I could lower that.
     
  16. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    It is planned to integrate a resolution control for terrains, but definitely not for meshes. Is your ground a mesh or a terrain?
     
  17. badescuga9

    badescuga9

    Joined:
    Dec 11, 2012
    Posts:
    37
    Hello! i have i short question..

    I created a projector and saved it as a prefab. I then load my prefab from code (as a child of Decals) and there is no projection on my terrain.. But then, if i move the projector from the editor just a bit, the projection appears. Why is this? i tried moving it from code and the projection doesn't appear(i.e. Tween). What can i call so that the 'refresh' is done from code?
     
  18. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    What exactly is your intention? Did the prefab loose the projection onto the terrain? Or do you want to change the projection anyways at runtime?
     
  19. devin8

    devin8

    Joined:
    Jul 15, 2012
    Posts:
    19
    I am trying to put decals on a plane that is supposed to be a runway. The decals are for runway markings. Now since the target mesh is just a plane, couldn't we get away with a lot fewer vertices than shown in the image?

    $forum.png
     
    Last edited: Apr 26, 2013
  20. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Seems the attachment is not showing up. Anyway, the number of vertices of the decals always depends on the number of vertices of the objects onto which they are projected. If you want to use the Decal System, you may think about using a huge cube as the ground. You can hide it in general, but the projection should only affect that cube. In a flat scenario, this should heavily reduce the number of vertices compared to a terrain projection. Not sure if this works for your situation, as I don't see the image.
     
  21. devin8

    devin8

    Joined:
    Jul 15, 2012
    Posts:
    19
    I am not sure why image is not showing. It does show in IE sometimes.
    Yes, if I uncheck "Terrains" in Affect options, it greatly reduces the resolution.
    Thanks a lot. :)
     
  22. badescuga9

    badescuga9

    Joined:
    Dec 11, 2012
    Posts:
    37
    Thanks for the reply and for the work you guys are putting into the library and support. You most certainly rock!

    I have managed to resolve the problem by using the code from the sample projects, but am facing other problems now..

    first of all, the decal system doesn't scale.. i've set decalProjectorScale to values from (0,0,0) to (20000,20000,20000) and the projection is still the same size. Am i missing something else i should be doing?

    second, i am trying to create a projection on the ground when an object colides with another. on impact, i destroy the object and start creating the projection. My projection rotation is:

    l_ProjectorRotation = Quaternion.LookRotation (l_ForwardDirection, l_UpDirection);

    and the position is:

    l_ProjectorPosition = positionTemp- (decalProjectorOffset * GameObject.FindGameObjectWithTag("MainCamera").transform.forward.normalized);

    where positionTemp is the position on the ground where i'm creating the projection..

    All works well most of the time, but on some ocasions there is no projection visible on impact. I suspect i'm handling the position or rotation wrong, could you give me a hint where i'm messing up?


     
  23. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @badescuga9: Unfortunately I can't help you with you issue because I don't have enough information. As you have pointed out there seems to be an issue with your projector scale. If the scale is wrong it is very likely that the projection does not show up at all, so those to may be related. You may try to visualize the decal area. That should help you to isolate the issue.
     
  24. badescuga9

    badescuga9

    Joined:
    Dec 11, 2012
    Posts:
    37
    @Dantus here's a fun fact: I've opened an example from the asset store package you provided, the Bootcamp Simple Colored scene. I then open the script responsible for the projections, namely ColoredBulletExampleCS. I then change the scale property, decalProjectorScale, from (0.2f, 2.0f, 0.2f) to (.2f,120,.2f) and even (1000,1000,1000) and the projections sizes are the same. Again, this is the code provided by you. Any ideas why?
     
    Last edited: Apr 30, 2013
  25. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @badescuga9: I expect that you are changing the value in the script, instead of the inspector. When you select the game object with the script in the scene, you need to change the value there because Unity loads that value in the case of public variables.
     
  26. badescuga9

    badescuga9

    Joined:
    Dec 11, 2012
    Posts:
    37
    of course, that was the problem. Sorry for the hassle, i know it's not your job to teach unity3d. Keep up the great work!
     
  27. Chris-Crafty

    Chris-Crafty

    Joined:
    May 7, 2013
    Posts:
    27
    Hello Dantus!

    I've found your Decal System very useful so far, thank you for making it available for free! I'm using it to highlight the path of combatants in a more-or-less tilebased tactical turnbased battle. I use a terrain as the "floor" for my combat arena, along with a (standard unity) projector to display the grid. To display the path for my warriors to run on, I've devised the following code, basically copied from one of your Demos:

    Code (csharp):
    1.  
    2.     DS_Decals decalsobj = null;
    3.     DecalsMesh decalsm = null;
    4.     Terrain terr = null;
    5.    
    6.     public Matrix4x4 m_TerrainToDecalsMatrix;
    7.  
    8.     void Start () {
    9.         decalsobj = pathholder.GetComponent<DS_Decals>();
    10.         if( decalsobj != null )
    11.             decalsm = new DecalsMesh( decalsobj );
    12.         terr = gameObject.GetComponentInChildren<Terrain>();
    13.         m_TerrainToDecalsMatrix = Matrix4x4.TRS (terr.transform.position, Quaternion.identity, Vector3.one) * decalsobj.transform.worldToLocalMatrix;
    14.     }
    15.  
    16.     void DrawPath() {
    17.         do {
    18.             // irrelevant stuff
    19.             Tile t = pp.LastStep;
    20.             Transform tp = // <- get tile world height
    21.            
    22.             DecalProjector dp = new DecalProjector(
    23.                     new Vector3( t.X, tp.localPosition.y + .2f, t.Y ),
    24.                     pathmarker.transform.rotation,
    25.                     Vector3.one,
    26.                     180f, 0.01f, ( pp.TotalCost > maxbp )?1:2, 0
    27.                 );
    28.            
    29.             decalsm.AddProjector( dp );
    30.             decalsm.Add( terr, m_TerrainToDecalsMatrix );
    31.             decalsm.OffsetActiveProjectorVertices ();
    32.             pp = pp.PreviousSteps;
    33.         }
    34.         while( pp != null );
    35.         decalsobj.UpdateDecalsMeshes(decalsm);
    36.     }
    37.  
    Tiles are supposed to be 1x1 world unity. This works fine on a small arena (9x9 world units), see picture 1:
    $path1.jpg

    But the UV-Mapping seems to be off when using it on a bigger arena (18x18 world units), see picture 2:
    $path2.jpg

    Maybe this is related to the fact that I use a prefab for the DS_Decals-Object plus Decals Mesh Renderer, but so far I couldn't really figure out why this is the case, as the "Terrain to Decals Matrix" is set dynamically and shouldn't be affected by the prefab. Do you have an idea what I'm doing wrong here? Any help would be appreciated, thanks!
     
  28. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Hi Chris

    Welcome to the forum!

    My first guess is that the UV rectangles of the second picture are wrong. The border around the red squared is thicker compared to the first picture, that's why I assume that different UV rectangles are used.
     
  29. Chris-Crafty

    Chris-Crafty

    Joined:
    May 7, 2013
    Posts:
    27
    Hey Dantus, thanks for the welcome and the quick response. And no, it is not the UV rectangles, as I said these are Prefabs, so the settings on DS_Decals for both of them are identical. If you look closely, you can also see that the Mesh borders are further outside the grid on the second picture than they are on the first. I don't know the inner workings of your Decals system precisely, but it seems to me that the UV coordinates on the generated mesh are just not correct or not as correct as on the first image.

    Giving the DecalProjector in Line 24 a smaller scaling than 1 would have been the obvious solution for me, but this just makes the decal smaller, but still projects too much of the original decal, check this picture with Line 24 changed to "Vector3.one * .5f":

    $path3.jpg
     
  30. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    I see. It looks as if something is scaled, either the decals or a parent of it, or the terrain, which doesn't work as far as I know. Could you compute m_TerrainToDecalsMatrix at the beginning of DrawPath for testing purposes? Let's see if that changes something.
     
  31. Chris-Crafty

    Chris-Crafty

    Joined:
    May 7, 2013
    Posts:
    27
    Did that, to no avail, same result. The resulting Matrix is:
    Code (csharp):
    1.  
    2. 1  0  0 -0.5
    3. 0  1  0  0
    4. 0  0  1 -0.5
    5. 0  0  0  1
    I also checked the matrix with the different sizes, it is identical. And I checked all the involved Gameobject scales, all of them are Vector3.one . I can try to get a minimal project together to let you have a look yourself, but as this is currently a rather big project (12 Gb Project Folder), the integration with all the parts is quite tight, so I'd prefer if we could somehow find a solution to this without that. Thanks for taking your time to look into this nonetheless, I'm sure we can find the reason for this problem somehow :).
     
  32. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    The scaling is clearly not the cause. As it works for your 9x9 arena, it makes no sense that it should be broken in a 18x18 arenas. there must be something different in the latter setup. As this seems to be project specific, I'll contact you directly.
     
  33. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Chris and me got rid of the issue. It was two folded. First the example code from the Decal System has a wrong order for the matrix multiplication for terrains which will certainly be resolved for the next release. The other part was that the code has no decals mesh cutter, that's why the size of the projectors was wrong.
     
  34. PolyMad

    PolyMad

    Joined:
    Mar 19, 2009
    Posts:
    2,350
    OMG why there's no more Unity 3 version????? :( :( :(
     
  35. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
  36. PolyMad

    PolyMad

    Joined:
    Mar 19, 2009
    Posts:
    2,350
    Thank you very much, man, that's fantastic.
    I wanted to say, that the version on the Asset Store only support Unity4.
     
  37. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Yes, because it uses Unity 4 features that are not available in Unity 3.x.
     
  38. PolyMad

    PolyMad

    Joined:
    Mar 19, 2009
    Posts:
    2,350
    Just a question: uploading to the Asset Store both versions is not allowed?
    I see many people have problems because of different versions...
    I understand that Unity company wants to push to v4, but I think to everybody has money to upgrade all time.
     
  39. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    There would be several ways to provide two version in the Asset Store. For me the point is, I don't have the time to maintain two versions. With Unity 4 there were quite some changes, regarding meshes, prefabs and some other features that are relevant in the Decal System. A lot of bugs were resolved for which I had to use dirty workarounds. After the first tests with Unity 4 beta, it was clear for me to stop the support for Unity 3 as soon as possible. But beside that everyone was still able to download Decal System 1.2.1 for Unity 3.
    So it was a conscious decision from myself to go on with Unity 4 only. For me, the only feasible way to provide the desired quality.

    That is all not related to the people who are facing problems because of different versions. I assume you mean that it is possible to download Unity 4 packages even if you are on Unity 3. This is simply a bug in the Asset Store. I submitted a bug report and that's all I can do. The only thing I can do about it is to provide the old Unity 3 compatible package, which I even did before the problem was known.

    As with all kinds of software, it is your decision if you get the newer versions or stick to the old one. But as a consequence you usually neither get new functionality, nor bug fixes or any other kind of updates. That's just the way it is. I described the reasons why I went to Unity 4, others made the decision to continue with Unity 3 for some time.
     
  40. PolyMad

    PolyMad

    Joined:
    Mar 19, 2009
    Posts:
    2,350
    No no you misunderstand, I COULDN'T download it, that's why I asked here on the forum.

    Thank you for everything, anyway. Hope I can pay you back for this help one day :wink:
     
  41. Sharkman

    Sharkman

    Joined:
    May 10, 2013
    Posts:
    1
    Hello then I'm trying to launch your Example scenes i get this error in the script,

    Error 1 Cannot implicitly convert type 'UnityEngine.Ray' to 'Ray' \Assets\Decal System Demos\Scripts\Bullet\BulletExampleCS.cs 85

    /Shark ;)
     
  42. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Welcome to the forum!

    Your project contains a script named Ray, although Unity already uses this name. Now the compiler assumes that your Ray script has to be used, instead of the one from Unity. You can either rename you script which is usually the best solution, or replace Ray by UnityEngine.Ray where you get the errors.
     
  43. reptilebeats

    reptilebeats

    Joined:
    Apr 28, 2012
    Posts:
    272
    looks good i've been using udk for a few months for a project and one of the features i liked from it was their decal system the rest is just buggy at best. coming back to unity in a few weeks will be downloading this straight away
     
  44. Aspiring_Failure

    Aspiring_Failure

    Joined:
    Jan 21, 2013
    Posts:
    42
    I have a question! But before I ask, I'd like to say that I'm VERY impressed your decal system. It's not only useful and free, but intuitive and flexible. I'm hoping you're really proud of this awesome tool, and if you ever release a paid version with even more functionality, -I am definitely a potential customer.

    But onto my question: I want to modify which UV Rect a projector is using through script. Can you tell me how to do this? And on that note, is it possible to toggle a projector on and off?
    I'm not trying to do anything fancy with dynamic bullet decals. I simply want a projected decal to change into something else under certain conditions, I.E. after the player hits a trigger.
     
  45. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    It doesn't matter how simple or complex any operation is, all kinds of runtime modifications have to be done with scrips comparable to the bullet example. This is required because a lot of additional information is needed to remove projectors, add new projectors, replace the uv's, ... . There is no way around it.
    I just implemented a helper class for that a few days ago. It is supposed to be placed in the same game object as the DS_Decals instance. Like that you can modify the the DS_DecalProjector's of it and then simply call UpdateProjector (...) of that helper script. If you want to get rid of a projector, just call RemoveProjector (...). There is a restriction, which is that you have to specify all MeshFilters or better the game objects that contain the meshes before you hit play. You have to drag them to the affected mesh filters field. If that is possible, this script may be usable for you.

    Code (csharp):
    1. //
    2. // Author:
    3. //   Andreas Suter (andy@edelweissinteractive.com)
    4. //
    5. // Copyright (C) 2013 Edelweiss Interactive (http://www.edelweissinteractive.com)
    6. //
    7.  
    8. using UnityEngine;
    9. using System.Collections;
    10. using System.Collections.Generic;
    11. using Edelweiss.DecalSystem;
    12.  
    13. public class RuntimeDecalsUpdater : MonoBehaviour {
    14.  
    15.     private DS_Decals m_Decals;
    16.     private List <DS_DecalProjector> m_UsedDecalProjectors = new List <DS_DecalProjector> ();
    17.     private List <WrappedDecalProjector> m_UsedWrappedDecalProjectors = new List <WrappedDecalProjector> ();
    18.    
    19.     [SerializeField] private MeshFilter[] m_AffectedMeshFilters;
    20.  
    21.     private DecalsMesh m_DecalsMesh;
    22.     private DecalsMeshCutter m_DecalsMeshCutter;
    23.  
    24.     private void Awake () {
    25.  
    26.             // Dummy to avoid warnings in console.
    27.         if (m_AffectedMeshFilters == null) {
    28.             m_AffectedMeshFilters = new MeshFilter [0];
    29.         }
    30.    
    31.         m_Decals = GetComponent <DS_Decals> ();
    32.         m_DecalsMesh = new DecalsMesh (m_Decals);
    33.         m_DecalsMeshCutter = new DecalsMeshCutter ();
    34.  
    35.         DS_DecalProjector[] l_DecalProjectors = GetComponentsInChildren <DS_DecalProjector> ();
    36.         foreach (DS_DecalProjector l_Projector in l_DecalProjectors) {
    37.             UpdateProjector (l_Projector);
    38.         }
    39.     }
    40.  
    41.     public void RemoveProjector (DS_DecalProjector a_Projector) {
    42.         RemoveProjectorInternal (a_Projector);
    43.         m_Decals.UpdateDecalsMeshes (m_DecalsMesh);
    44.     }
    45.  
    46.     private void RemoveProjectorInternal (DS_DecalProjector a_Projector) {
    47.         if (m_UsedDecalProjectors.Contains (a_Projector)) {
    48.             int l_Index = m_UsedDecalProjectors.IndexOf (a_Projector);
    49.             WrappedDecalProjector l_WrappedDecalProjector = m_UsedWrappedDecalProjectors [l_Index];
    50.             m_DecalsMesh.RemoveProjector (l_WrappedDecalProjector);
    51.             m_UsedDecalProjectors.RemoveAt (l_Index);
    52.             m_UsedWrappedDecalProjectors.RemoveAt (l_Index);
    53.         }
    54.     }
    55.  
    56.     public void UpdateProjector (DS_DecalProjector a_Projector) {
    57.         RemoveProjectorInternal (a_Projector);
    58.  
    59.         WrappedDecalProjector l_WrappedDecalProjector = new WrappedDecalProjector (a_Projector);
    60.         m_UsedDecalProjectors.Add (a_Projector);
    61.         m_UsedWrappedDecalProjectors.Add (l_WrappedDecalProjector);
    62.  
    63.         m_DecalsMesh.AddProjector (l_WrappedDecalProjector);
    64.        
    65.         foreach (MeshFilter l_MeshFilter in m_AffectedMeshFilters) {
    66.             Mesh l_Mesh = l_MeshFilter.sharedMesh;
    67.             Transform l_Transform = l_MeshFilter.transform;
    68.             Matrix4x4 l_WorldToMesh = l_Transform.worldToLocalMatrix;
    69.             Matrix4x4 l_MeshToWorld = l_Transform.localToWorldMatrix;
    70.             m_DecalsMesh.Add (l_Mesh, l_WorldToMesh, l_MeshToWorld);
    71.         }
    72.        
    73.         m_DecalsMeshCutter.CutDecalsPlanes (m_DecalsMesh);
    74.         m_DecalsMesh.OffsetActiveProjectorVertices ();
    75.         m_Decals.UpdateDecalsMeshes (m_DecalsMesh);
    76.     }
    77. }
     
  46. PolyMad

    PolyMad

    Joined:
    Mar 19, 2009
    Posts:
    2,350
    Hi, just installed it and tried, it's ubercool. Thank you very much again!
     
  47. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    I am glad you like it!
     
  48. SirManGuy

    SirManGuy

    Joined:
    Sep 16, 2010
    Posts:
    33
    Okay I attempted to make a DecalManager to be able to handle real-time movement of unlimited decal systems. I seem to be having trouble getting the orientation right, and I think I'm wasting my time using the Bullet code. I'm wondering if I should be using the RuntimeDecalsUpdater to utilize Mesh/Terrain overlapping as well. Below is my code and an image of what is happening - as you can see I'm getting stretching and cutoff of the mesh (should be a full circle). The stretching gets really bad as I pull towards the camera. Great work by the way!

    $ScreenShot.png
    Code (csharp):
    1.  
    2. public class DecalManager : MonoBehaviour
    3. {
    4.     private DecalsMeshCutter meshCutter;
    5.     private Matrix4x4 worldToDecalsMatrix = Matrix4x4.zero;
    6.  
    7.     // The raycast hits a collider at a certain position. This value indicated how far we need to
    8.     // go back from that hit point along the ray of the raycast to place the new decal projector. Set
    9.     // this value to 0.0f to see why this is needed.
    10.     public float ProjectorOffset = 0.5f;
    11.  
    12.     // The size of new decal projectors.
    13.     public Vector3 ProjectorScale = new Vector3(5f, 12.0f, 5f);
    14.     public float CullingAngle = 90.0f;
    15.     public float MeshOffset = 0.001f;
    16.  
    17.  
    18.     public List<GameObject> Prefabs;
    19.     public class ManagerDecalSystem
    20.     {
    21.         public string PrefabType;
    22.         public DS_Decals System;
    23.     }
    24.     public List<ManagerDecalSystem> Systems;
    25.     public Dictionary<int, List<DecalProjector>> Projectors;
    26.     public Dictionary<int, DecalsMesh> Meshes;
    27.     public bool Initialized = false;
    28.  
    29.     void Start()
    30.     {
    31.         foreach (var prefab in Prefabs)
    32.         {
    33.             var dsDecals = prefab.GetComponent<DS_Decals>();
    34.             if (dsDecals == null)
    35.             {
    36.                 throw new MissingComponentException("DecalManager.Prefabs['" + prefab.name + "'] does not contain a DS_Decals component!");
    37.             }
    38.         }
    39.         Systems = new List<ManagerDecalSystem>();
    40.         Projectors = new Dictionary<int, List<DecalProjector>>();
    41.         Meshes = new Dictionary<int, DecalsMesh>();
    42.         meshCutter = new DecalsMeshCutter();
    43.         Initialized = true;
    44.     }
    45.    
    46.     private GameObject FindPrefab(string prefab)
    47.     {
    48.         foreach (var go in Prefabs)
    49.         {
    50.             if (go.name == prefab)
    51.             {
    52.                 return go;
    53.             }
    54.         }
    55.         return null;
    56.     }
    57.     public int AddSystem(string prefabName)
    58.     {
    59.         var prefab = FindPrefab(prefabName);
    60.         if (prefab == null)
    61.         {
    62.             throw new UnityException("DecalManager.AddSystem - Trying to create a system with an unknown prefab.");
    63.         }
    64.         var instance = Instantiate(prefab) as GameObject;
    65.         var decalSystem = instance.GetComponentInChildren<DS_Decals>();
    66.         if (worldToDecalsMatrix == Matrix4x4.zero)
    67.         {
    68.             worldToDecalsMatrix = decalSystem.CachedTransform.worldToLocalMatrix;
    69.         }
    70.         Systems.Add(new ManagerDecalSystem() { PrefabType = prefabName, System = decalSystem });
    71.         var id = Systems.Count - 1;
    72.         var mesh = new DecalsMesh(decalSystem);
    73.         Meshes.Add(id, mesh);
    74.         Projectors.Add(id, new List<DecalProjector>());
    75.         return id;
    76.     }
    77.     public int GetInactiveSystem(string prefab)
    78.     {
    79.         // Find an inactive DS_Decal
    80.         for (int i = 0; i < Systems.Count; i++)
    81.         {
    82.             var system = Systems[i];
    83.             if (!system.System.gameObject.activeSelf  system.PrefabType == prefab)
    84.             {
    85.                 system.System.gameObject.SetActive(true);
    86.                 Projectors.Add(i, new List<DecalProjector>());
    87.                 return i;
    88.             }
    89.         }
    90.         // We haven't found an inactive system we need to create a new one
    91.         return AddSystem(prefab);
    92.     }
    93.     public void DeactivateSystem(int id)
    94.     {
    95.         if (id < 0 || id > Systems.Count - 1)
    96.         {
    97.             throw new UnityException("DecalManager.DeactivateSystem - on non-existant decal system.");
    98.         }
    99.         var system = Systems[id].System;
    100.         if (system.gameObject.activeSelf)
    101.         {
    102.             var mesh = Meshes[id];
    103.             var projectors = Projectors[id];
    104.             foreach (var projector in projectors)
    105.             {
    106.                 mesh.RemoveProjector(projector);
    107.             }
    108.             Projectors.Remove(id);
    109.             system.UpdateDecalsMeshes(mesh);
    110.             system.gameObject.SetActive(false);
    111.         }
    112.     }
    113.     public void SetMaterial(int id, Material newMaterial)
    114.     {
    115.         if (id < 0 || id > Systems.Count - 1)
    116.         {
    117.             throw new UnityException("DecalManager.SetMaterial - on non-existant decal system.");
    118.         }
    119.         var system = Systems[id].System;
    120.         if (system.gameObject.activeSelf)
    121.         {
    122.             system.CurrentMaterial = newMaterial;
    123.         }
    124.     }
    125.     public void SetColor(int id, Color newColor)
    126.     {
    127.         if (id < 0 || id > Systems.Count - 1)
    128.         {
    129.             throw new UnityException("DecalManager.SetMaterial - on non-existant decal system.");
    130.         }
    131.         var system = Systems[id].System;
    132.         if (system.gameObject.activeSelf)
    133.         {
    134.             system.CurrentMaterial.color = newColor;
    135.         }
    136.     }
    137.     public DecalProjector AddProjector(int id, Vector3 position, float rotation, Vector3 scale, int uvRect1, int uvRect2, Terrain terrain, List<Mesh> meshes, Matrix4x4 worldToMesh, Matrix4x4 meshToWorld)
    138.     {
    139.         if (id < 0 || id > Systems.Count - 1)
    140.         {
    141.             throw new UnityException("DecalManager.AddProjector - on non-existant decal system.");
    142.         }
    143.         if (terrain == null  (meshes == null || meshes.Count == 0))
    144.         {
    145.             throw new UnityException("DecalManager.AddProjector - with no terrain or meshes to apply to.");
    146.         }
    147.         if (!Projectors.ContainsKey(id))
    148.         {
    149.             return null;
    150.         }
    151.         Vector3 forwardDirection = Camera.main.transform.up;
    152.         Vector3 upDirection = -Camera.main.transform.forward;
    153.         Quaternion forwardUp = Quaternion.LookRotation(forwardDirection, upDirection);
    154.  
    155.         // Randomize the rotation.
    156.         Quaternion buildRotation = Quaternion.Euler(0.0f, rotation, 0.0f);
    157.         var projectorRotation = forwardUp * buildRotation;
    158.         var projector = new DecalProjector(position, projectorRotation, scale, CullingAngle, MeshOffset, uvRect1, uvRect2);
    159.         Projectors[id].Add(projector);
    160.         var mesh = Meshes[id];
    161.         mesh.AddProjector(projector);
    162.         if (terrain != null)
    163.         {
    164.             // The terrain data has to be converted to the decals instance's space.
    165.             Matrix4x4 terrainToDecalsMatrix = Matrix4x4.TRS(terrain.transform.position, Quaternion.identity, Vector3.one) * worldToDecalsMatrix;
    166.  
    167.             // Pass the terrain data with the corresponding conversion to the decals mesh.
    168.             mesh.Add(terrain, terrainToDecalsMatrix);
    169.  
    170.             // Cut the data in the decals mesh accoring to the size and position of the decal projector. Offset the
    171.             // vertices afterwards and pass the newly computed mesh to the decals instance, such that it becomes
    172.             // visible.
    173.             meshCutter.CutDecalsPlanes(mesh);
    174.             mesh.OffsetActiveProjectorVertices();
    175.         }
    176.         if (meshes != null  meshes.Count > 0)
    177.         {
    178.             foreach (var meshData in meshes)
    179.             {
    180.                 // Get the required matrices.
    181.                 //Matrix4x4 l_WorldToMeshMatrix = l_RaycastHit.collider.renderer.transform.worldToLocalMatrix;
    182.                 //Matrix4x4 l_MeshToWorldMatrix = l_RaycastHit.collider.renderer.transform.localToWorldMatrix;
    183.  
    184.                 // Add the mesh data to the decals mesh, cut and offset it before we pass it
    185.                 // to the decals instance to be displayed.
    186.                 mesh.Add(meshData, worldToMesh, meshToWorld);
    187.                 meshCutter.CutDecalsPlanes(mesh);
    188.                 mesh.OffsetActiveProjectorVertices();
    189.             }
    190.         }
    191.         Systems[id].System.gameObject.SetActive(true);
    192.         Systems[id].System.UpdateDecalsMeshes(mesh);
    193.         return projector;
    194.     }
    195.     public void RemoveProjector(int id, DecalProjector projector)
    196.     {
    197.         if (id < 0 || id > Systems.Count - 1)
    198.         {
    199.             throw new UnityException("DecalManager.RemoveProjector - on non-existant decal system.");
    200.         }
    201.         if (!Projectors.ContainsKey(id))
    202.         {
    203.             return;
    204.         }
    205.         if (Projectors[id].Contains(projector))
    206.         {
    207.             Projectors[id].Remove(projector);
    208.             Meshes[id].RemoveProjector(projector);
    209.             Systems[id].System.UpdateDecalsMeshes(Meshes[id]);
    210.         }
    211.     }
    212.  
    213.     public void MoveProjector(int id, DecalProjector projector, Vector3 position)
    214.     {
    215.         if (id < 0 || id > Systems.Count - 1)
    216.         {
    217.             throw new UnityException("DecalManager.MoveProjector - on non-existant decal system.");
    218.         }
    219.         if (!Projectors.ContainsKey(id))
    220.         {
    221.             return;
    222.         }
    223.         if (Projectors[id].Contains(projector))
    224.         {
    225.             // save the details
    226.  
    227.         }
    228.     }
    229.     public void RotateProjector(int id, DecalProjector projector, float rotation)
    230.     {
    231.         if (id < 0 || id > Systems.Count - 1)
    232.         {
    233.             throw new UnityException("DecalManager.RotateProjector - on non-existant decal system.");
    234.         }
    235.         if (!Projectors.ContainsKey(id))
    236.         {
    237.             return;
    238.         }
    239.         if (Projectors[id].Contains(projector))
    240.         {
    241.         }
    242.     }
    243.  
    244.     private int currentSystem = -1;
    245.     private DecalProjector lastProjector = null;
    246.     void Update()
    247.     {
    248.         if (Input.GetKey(KeyCode.LeftShift))
    249.         {
    250.             if (Input.mousePosition != null)
    251.             {
    252.                 if (Camera.current != null)
    253.                 {
    254.                     if (currentSystem == -1)
    255.                     {
    256.                         currentSystem = GetInactiveSystem("testSystem");
    257.                     }
    258.                     Ray ray = Camera.current.ScreenPointToRay(Input.mousePosition);
    259.                     RaycastHit hit;
    260.                     if (Physics.Raycast(ray, out hit, Mathf.Infinity))
    261.                     {
    262.                         if (lastProjector != null)
    263.                         {
    264.                             RemoveProjector(currentSystem, lastProjector);
    265.                         }
    266.                         GameObject otherObj = hit.collider.gameObject;
    267.                         Vector3 newPos = new Vector3(hit.point.x, hit.point.y, hit.point.z);
    268.                         if (hit.collider is TerrainCollider)
    269.                         {
    270.                             var terrain = otherObj.GetComponent<Terrain>();
    271.                             lastProjector = AddProjector(currentSystem, newPos, 0f, ProjectorScale, 0, 0, terrain, null, Matrix4x4.zero, Matrix4x4.zero);
    272.                         }
    273.                     }
    274.                 }
    275.             }
    276.         }
    277.         else
    278.         {
    279.             if (currentSystem != -1)
    280.             {
    281.                 DeactivateSystem(currentSystem);
    282.             }
    283.         }
    284.     }
    285. }
    286.  
     
  49. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @SirManGuy, the bullet code contains a bug for terrains that are not placed at the origin. You have to change the multiplication order at line 164 to

    Code (csharp):
    1. Matrix4x4 terrainToDecalsMatrix = worldToDecalsMatrix * Matrix4x4.TRS(terrain.transform.position, Quaternion.identity, Vector3.one);
    Hope this helps
     
  50. SirManGuy

    SirManGuy

    Joined:
    Sep 16, 2010
    Posts:
    33
    Well after some playing around the stretching seems better however I can't tell why the mesh/decal is being cut in half. The UVRect is set up correctly and works in the editor but obviously I'm blind as to what I'm missing:

     

    Attached Files:

    Last edited: May 21, 2013