Search Unity

Decal System

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

  1. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    It would be a lot easier to help, if you could show the code. Your way sounds right, that's why I assume it is related to your code.
     
  2. justinl

    justinl

    Joined:
    Aug 27, 2012
    Posts:
    58
    Thanks Dantus,

    I basically took your example file and tweaked it to work how I needed it to. Here's the code. It works perfectly. The only thing it doesn't do is show bullet holes on both sides of a window pane. The window is only .01 units deep FYI. Thanks!

    Code (csharp):
    1.  
    2.     // The reference to the instantiated prefab's DS_Decals instance.
    3. private DS_Decals m_Decals;
    4.     //private Matrix4x4 m_WorldToDecalsMatrix;
    5.    
    6.     // All the projectors that were created at runtime.
    7. private List <DecalProjector> m_DecalProjectors = new List <DecalProjector> ();
    8.    
    9.     // Intermediate mesh data. Mesh data is added to that one for a specific projector
    10.     // in order to perform the cutting.
    11. private DecalsMesh m_DecalsMesh;
    12. private DecalsMeshCutter m_DecalsMeshCutter;
    13. public static float decalProjectorOffset = 0.025f;
    14. public static Vector3 decalProjectorScale = new Vector3 (0.15f, 0.15f, 0.15f);
    15. public static float cullingAngle = 180.0f;
    16. public static float meshOffset = 0.001f;
    17.  
    18. private void Start () {
    19.             // Instantiate the prefab and get its decals instance.
    20.         GameObject l_Instance = Instantiate (decalsPrefab) as GameObject;
    21.         m_Decals = l_Instance.GetComponentInChildren <DS_Decals> ();
    22.        
    23.         if (m_Decals == null) {
    24.             Debug.LogError ("The 'decalsPrefab' does not contain a 'DS_Decals' instance!");
    25.         } else {
    26.            
    27.                 // Create the decals mesh (intermediate mesh data) for our decals instance.
    28.                 // Further we need a decals mesh cutter instance and the world to decals matrix.
    29.             m_DecalsMesh = new DecalsMesh (m_Decals);
    30.             m_DecalsMeshCutter = new DecalsMeshCutter ();
    31.         }
    32.     }
    33.  
    34.  
    35. void CreateDecal(){
    36.     // Collider hit.
    37.             // Make sure there are not too many projectors.
    38.         if (decalProjectors.Count >= maxNumDecalsAllowed) {
    39.        
    40.                 // If there are more than 50 projectors, we remove the first one from
    41.                 // our list and certainly from the decals mesh (the intermediate mesh
    42.                 // format). All the mesh data that belongs to this projector will
    43.                 // be removed.
    44.             var decalProjectorForRemoval = decalProjectors [0];
    45.             decalProjectors.RemoveAt (0);
    46.             decalsMesh.RemoveProjector (decalProjectorForRemoval);
    47.         }
    48.        
    49.             // Calculate the position and rotation for the new decal projector.
    50.         Vector3 projectorPosition = raycastHit.point - (decalProjectorOffset * direction.normalized);
    51.         Quaternion projectorRotation = Quaternion.FromToRotation(Vector3.up, raycastHit.normal);
    52.        
    53.             // Randomize the rotation but keep things on 90 degree angles
    54.         Quaternion randomRotation = Quaternion.Euler (0.0f, Tools.RoundUpToNearestMultiple(Random.Range (0.0f, 360.0f), decalRandomAngleMultiple), 0.0f);
    55.         projectorRotation = projectorRotation * randomRotation;
    56.  
    57.             // We hit a collider. Next we have to find the mesh that belongs to the collider.
    58.             // That step depends on how you set up your mesh filters and collider relative to
    59.             // each other in the game objects. It is important to have a consistent way in order
    60.             // to have a simpler implementation.
    61.        
    62.         MeshCollider meshCollider = raycastHit.collider.GetComponent<MeshCollider>();
    63.         MeshFilter meshFilter = raycastHit.collider.GetComponent<MeshFilter>();
    64.        
    65.         if (meshCollider != null || meshFilter != null) {
    66.             Mesh mesh = null;
    67.             //Material hitMaterial = null;
    68.            
    69.             if (meshCollider != null) {
    70.            
    71.                     // Mesh collider was hit. Just use the mesh data from that one.
    72.                 mesh = meshCollider.sharedMesh;
    73.             } else if (meshFilter != null) {
    74.            
    75.                     // Otherwise take the data from the shared mesh.
    76.                 mesh = meshFilter.sharedMesh;
    77.             }
    78.            
    79.             if (mesh != null) {
    80.                
    81.                
    82.                 // Create the decal projector and use a random UV rectangle
    83.                 int randomUVIndex = GetRandomUVRectangleIndex(uvList);
    84.                
    85.                 DecalProjector l_DecalProjector = new DecalProjector (projectorPosition, projectorRotation, decalProjectorScale, cullingAngle, meshOffset, randomUVIndex, randomUVIndex);
    86.  
    87.                     // Add the projector to our list and the decals mesh, such that both are
    88.                     // synchronized. All the mesh data that is now added to the decals mesh
    89.                     // will belong to this projector.
    90.                 decalProjectors.Add (l_DecalProjector);
    91.                 decalsMesh.AddProjector (l_DecalProjector);
    92.                
    93.                     // Get the required matrices.
    94.                 Matrix4x4 worldToMeshMatrix = raycastHit.collider.renderer.transform.worldToLocalMatrix;
    95.                 Matrix4x4 meshToWorldMatrix = raycastHit.collider.renderer.transform.localToWorldMatrix;
    96.                
    97.                     // Add the mesh data to the decals mesh, cut and offset it before we pass it
    98.                     // to the decals instance to be displayed.
    99.                 decalsMesh.Add (mesh, worldToMeshMatrix, meshToWorldMatrix);                       
    100.                 decalsMeshCutter.CutDecalsPlanes (decalsMesh);
    101.                 decalsMesh.OffsetActiveProjectorVertices ();
    102.                 dsDecals.UpdateDecalsMeshes (decalsMesh);
    103.             }
    104.         }
    105. }
    106.  
     
  3. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Is the window one mesh or do you have one mesh for the inside and one for the outside? I am just guessing, because the code obviously works in general but for some reason not in that special case. I can't find anything obvious in the code.
     
  4. justinl

    justinl

    Joined:
    Aug 27, 2012
    Posts:
    58
    The window is just a thin box (uses a box collider). I notice that any decal I create in the game, if I move the generated decal mesh off of the object that it's attached to, the back of the mesh doesn't render the decal (back face culling is on), so I wouldn't actually expect it to work by default I suppose.

    I also tried creating a thin cube in your Bootcamp Simple.unity scene (I didn't change any of your example code), and if I shoot that thin cube that has a transparent shader on it, you can only see the decal from one side and not the other. So definitely it's not a change in my code. Do you have any thoughts as to how to render the decal from both sides?
     
  5. emandrawkcab

    emandrawkcab

    Joined:
    Feb 28, 2013
    Posts:
    5
    Just in the editor, but I want it automated. All I'm trying to do is cover pre-existing meshes with your decal meshes in certain areas, then later export everything from this project as AssetBundles for another (I can already extract the mesh data and make a complete GameObject with the same mesh data you generate).

    I am calling that. I'm doing everything exactly as shown in the example code. I've tried experimenting with it for a while now, and can't get anything to show up without hitting the Update All Projectors button, and I want this automated.

    This is in a static function so I can run it from the menu, so everything's static and I reinitialize it each time the function is ran. Here's the class members:
    Code (csharp):
    1.     static DS_Decals m_Decals;
    2.     static List<DecalProjector> m_DecalProjectors;
    3.  
    4.     static DecalsMesh m_DecalsMesh;
    5.     static DecalsMeshCutter m_DecalsMeshCutter;
    6.  
    7.     const float k_DecalProjectorOffset = 50f;
    8.     const float k_CullingAngle = 180f;
    9.     const float k_MeshOffset = 0.01f;
    And here's the bit of code that should be making decals:
    Code (csharp):
    1.                 GameObject prefab = Resources.Load("Prefabs/DecalsPrefab") as GameObject;
    2.                 if (!prefab)
    3.                 {
    4.                     Debug.Log("[MakeDecals] Cannot locate prefab! Aborting.");
    5.                     return;
    6.                 }
    7.  
    8.                 GameObject decals_instance = Instantiate(prefab) as GameObject;
    9.  
    10.                 m_Decals = decals_instance.GetComponentInChildren<DS_Decals>();
    11.                 if (!m_Decals)
    12.                 {
    13.                     Debug.Log("[MakeDecals] Prefab had no DS_Decals instance! Aborting.");
    14.                     return;
    15.                 }
    16.  
    17.                 m_DecalsMesh = new DecalsMesh(m_Decals);
    18.                 m_DecalsMeshCutter = new DecalsMeshCutter();
    19.  
    20.                 ////////////////////////////////////////////
    21.  
    22.                 Vector3 local_right = Vector3.right; // (actual calculation removed for now)
    23.  
    24.                 Vector3 projector_position = Vector3.zero; // (actual calculation removed for now)
    25.                 Vector3 projector_rotation = Quaternion.identity; // (actual calculation removed for now)
    26.                 Vector3 projector_scale = new Vector3(100f, 100f, 100f); // (actual calculation removed for now)
    27.  
    28.                 DecalProjector decal_projector = new DecalProjector(projector_position, projector_rotation, projector_scale, k_CullingAngle, k_MeshOffset, 0, 0);
    29.  
    30.                 m_DecalProjectors.Add(decal_projector);
    31.                 m_DecalsMesh.AddProjector(decal_projector);
    32.  
    33.                 // curr_terrain is a valid, complete GameObject, with a height-map mesh where the height is on the y-axis
    34.                 Mesh mesh = curr_terrain.GetComponent<MeshFilter>().sharedMesh;
    35.  
    36.                 m_DecalsMesh.Add(mesh, curr_terrain.transform.worldToLocalMatrix, curr_terrain.transform.localToWorldMatrix);
    37.                 m_DecalsMeshCutter.CutDecalsPlanes(m_DecalsMesh);
    38.                 m_DecalsMesh.OffsetActiveProjectorVertices();
    39.                 m_Decals.UpdateDecalsMeshes(m_DecalsMesh);
    I can't figure out why I have to hit the button for everything to actually update. The way I get it to work without this code working is to access the real GameObject (the child of the decals object) named "Projector", change its transform data in script, and hit the button after the script runs. Help?
     
  6. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    I missed one point. It happens because both materials are transparent. The issue you are encountering is due to a sorting problem of transparent shaders. A workaround would be to have a separate DS_Decals instance inside of the building. So when the window is hit, you need to create projectors in the two different DS_Decals.
     
  7. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @emandrawkcab: The Decal System was not created that it can be manipulated by anything else in the editor than its own editor scripts. Nevertheless, I still try to help you as well as I can. I you want that the changes stay visible in the editor, you can't use DecalProject, but instead DS_DecalProjectors become relevant.
    Do I understand you correctly that you want to compute the mesh and make it visible? And the computation works, while the visualization doesn't?
     
  8. emandrawkcab

    emandrawkcab

    Joined:
    Feb 28, 2013
    Posts:
    5
    Yes, I'd like the mesh to be created and accessible from script without needing to click any buttons in the UI.

    I'm not quite sure what you mean by that. Clicking the Update All Projectors button works, the script code doesn't.

    Thank you for your time.
     
  9. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @emandrawkcab: I though the editor functionality is not public, but some parts can be accessed. You may try to use:

    Code (csharp):
    1. Edelweiss.DecalSystemEditor.EditorDecalsCreator.UpdateAllProjectors (yourDecalsInstance);
    But that means you have to switch your strategy. You don't need a decals mesh anymore because it is handled by the editor decals creator. All you need to do is to add the DS_DecalsProjector instances in child game objects of the DS_Decals instance. After that, you call the mentioned UpdateAllProjectors method.
     
  10. emandrawkcab

    emandrawkcab

    Joined:
    Feb 28, 2013
    Posts:
    5
    Thanks again! Works like a charm.
     
  11. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Great!
     
  12. justinl

    justinl

    Joined:
    Aug 27, 2012
    Posts:
    58
    Hi Dantus, thanks for the suggestion! Instead of creating a new DS_Decals instance I think what I'll do is just generate 2 projectors shooting both inwards towards the glass when I shoot a window. Cheers and Thanks a lot!
     
  13. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    I don't think this works. Even the solution I proposed doesn't work in all cases.
    It's important to understand that transparent objects are sorted relative to the camera, meaning the transparent object with the greatest distance to the camera is rendered first. The distance is always from the camera to the pivot point of the transparent object. So matter how many projectors you place for the window, this doesn't change the pivot of the DS_Decals to which the projectors belong and the rendering order is not changed. That's why another DS_Decals instance is needed which is closer to the camera than the window. Like that the sorting issue can be solved.

    Hope this helps.
     
  14. eskovas

    eskovas

    Joined:
    Dec 2, 2009
    Posts:
    1,373
    Hi dantus,
    I'm having a problem trying to get this tool to work because everytime i try to use it, Unity crashes.
    I also created a new project to see if the problem was with my game's project, but it still crashes.
    It happens in many different areas like rotating/moving the decals, creating new decals, etc.
    I'm using Unity Pro, but when i was using Unity free, it also crashed Unity and between those i switched from Win7 to Win8, so i completely reinstalled Unity.

    I have the editor log if you want to look at it, maybe it will help you find the problem.
    Don't know if anyone else had this problem, but i hope you can find a fix for it.
    Thanks.
     
    Last edited: Apr 8, 2013
  15. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @eskovas: Thanks for reporting the issue. It is indeed the first time I hear that Unity crashed due to the Decal System. Please post the editor log here or send me a pm.
     
  16. eskovas

    eskovas

    Joined:
    Dec 2, 2009
    Posts:
    1,373
    Here it is.
    Hope that helps.

    EDIT:
    Here's the process i made till that crash:
    - Opened Unity,
    - created a new project,
    - went to the asset store, downloaded and imported the decals asset,
    - placed the bridge from the bootcamp in the scene
    - created a new decals object,
    - created a new material with a texture and assigned it to the decal material,
    - created a new projector,
    - tried to move it and it crashed.

    I think thats all the steps i made.
     

    Attached Files:

    Last edited: Apr 8, 2013
  17. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @eskovas, did you create a uv rectangle? There has to be at least one uv rectangle which can be used by the projector.
    Independent of that, it should not crash. I am only getting a warning message in the inspector that there is no uv rectangle and even if there are projectors in the scene, it doesn't crash here.
     
  18. eskovas

    eskovas

    Joined:
    Dec 2, 2009
    Posts:
    1,373
    Yes i did create the uv rectangle after that error appeared, It didn't crashed on that part.
    It's really strange, i have tried everything, even creating a new project to test it and it still crashed...
    I also tried it in windows7 and windows8 with a complete new Unity installation(both Unity free and pro versions).
    I don't really know what else to try.
     
    Last edited: Apr 9, 2013
  19. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @eskovas, after having a closer look at the release log, it seems that you are using a projection with lightmapping support (for the uv2). This calls Unity's internal unwrapping functionality, which is the last call before the crash.
    Is it correct that you are using a projection with lightmapping support? If yes, please try to use default projection mode (Diffuse) to test if there are any issues.
     
  20. atsamy

    atsamy

    Joined:
    Feb 28, 2013
    Posts:
    5
    thanks for the helpful tool, but i have a question, am using the bullet decal script, do i have to use a mesh collider on every object, because its not workin if i added like a box collider on a wall or something??
     
  21. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    The mesh collider is used to detect, whether there is a collision of the bullet with the mesh. If a less precise collider is sufficient, you can also use a different kind of collider, but you have to modify the code to take that into account. Keep in mind that those examples are not meant to be general solutions, but examples to illustrate how it can be used.

    Edit: Welcome to the forums :)
     
  22. eskovas

    eskovas

    Joined:
    Dec 2, 2009
    Posts:
    1,373
    I tested without using the lightmapping support and i tested it for about 15 minutes and never crashed, so i think that was the problem.
    There should maybe be a warning when trying to do something that can't be done, to prevent the crash. Did you try it yourself to see if it crashed?
     
  23. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Yes, I tried it yesterday and had no problem, because I switched from diffuse to lightmap. But today, I was able to reproduce the crash.

    Here is a quick workaround for you:
    - Use the diffuse projection mode
    - Place a projector somewhere, such that at least a few triangles are created
    - Switch to the lightmapping projection you need
    - Add as many projectors as you want

    It is important that at least one triangle exists in the whole DS_Decals instance, before you switch to any lightmapped projection. Meaning one projector that performs some projection. As a consequence, while you are using any lightmapped projection, make sure that there is always at least one projector that performs some projection.

    I am going to integrate a workaround for this issue in the next release and will report the bug to Unity.
    Thanks a lot for reporting the issue!

    PS For anyone who is interested in it: If the uv2 should be computed for the lightmapping and an empty mesh is passed or maybe a mesh with only one vertex, can't remember how it is handled internally, Unity crashes.
     
  24. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Edit: Double post
     
  25. ibyte

    ibyte

    Joined:
    Aug 14, 2009
    Posts:
    1,047
    Hi Dantus, thanks for the nice Decal Package. My first use case is very simple but I am getting a little confused when looking at the bullet example code.

    I am applying a decal to a sphere. I have multiple uv's and I would like to select the one I want activated to be used by a predefined projector at run time. I created a prefab with everything preset. When I run the game I can see a decal but it is not the correct uv.

    Code (csharp):
    1.  
    2.         ballDecal = (GameObject) Instantiate(animalDecalPrefab);
    3.         animalDecal = ballDecal.GetComponentInChildren<DS_Decals>();
    4.         DS_DecalProjector animlaDecalProjector = animalDecal.GetComponentInChildren<DS_DecalProjector>();
    5.         animlaDecalProjector.uv1RectangleIndex = main_Game.curentCourse;
    6.         ballDecal.transform.parent = ballObject.transform;
    7.         ballDecal.transform.localPosition = Vector3.zero;
    8.         ballDecal.transform.localScale = Vector3.one;
    9.  
    How do I tell the projector which uv to use and refresh the projector so that it know's the uv has changed?

    Another edit: I see now that setting uv1RectangleIndex is working I need to equiv of updateprojectors.

    iByte
     
    Last edited: Apr 9, 2013
  26. eskovas

    eskovas

    Joined:
    Dec 2, 2009
    Posts:
    1,373
    @Dantus

    Thanks for the awnser, now i can use this tool in my game :)
     
  27. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @ibyte, the first things you did are correct. You need the DS_Decals and the projector. It is not possible to simply replace the the uv rectangle index. The actual decal mesh, meaning the mesh that is produced by the Decal System, has to be computed.
    I don't have the time to explain it in detail today. I still want to give you a quick explanation with the relevant code part of the examples. Just ask if something is not clear.
    Here are the relevant passages of the example code. What you need is a decals mesh (m_DecalsMesh). Just create one. And you need to have the mesh of the sphere (l_Mesh) with its transform (l_MeshTransform).

    Code (csharp):
    1. m_DecalsMesh.AddProjector (animalDecalProjector);
    2.                            
    3.     // Get the required matrices.
    4. Matrix4x4 l_WorldToMeshMatrix = l_MeshTransform.worldToLocalMatrix;
    5. Matrix4x4 l_MeshToWorldMatrix = l_MeshTransform.localToWorldMatrix;
    6.                        
    7.     // Add the mesh data to the decals mesh, cut and offset it before we pass it
    8.     // to the decals instance to be displayed.
    9. m_DecalsMesh.Add (l_Mesh, l_WorldToMeshMatrix, l_MeshToWorldMatrix);                       
    10. m_DecalsMeshCutter.CutDecalsPlanes (m_DecalsMesh);
    11. m_DecalsMesh.OffsetActiveProjectorVertices ();
    12. animalDecal.UpdateDecalsMeshes (m_DecalsMesh);
    I am pretty sure that I missed something :) . I will take a little more time tomorrow, but maybe this is already sufficient for you.
     
  28. ibyte

    ibyte

    Joined:
    Aug 14, 2009
    Posts:
    1,047
    Hi Dantus, Thanks for the explanation. I am still a little confused. If everything is preset why do i need to make those calls when I only want to swap where the uv points to on the existing mesh that Decals already made? Playing around a little bit more when my game is running an I see that when I change the UV rectangle via the inspector my decal is updated in the game. Clicking update all projectors was not required.

    Thanks

    iByte
     
  29. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @ibyte, when you modify something in the inspector of the Decal System, that kind of code is executed internally. All kinds of decal operations, and changing the uv rectangle index of a projector is one of them, requires you to have a decals mesh. That's one of the central classes of the Decal System. The editor scripts use this one extensively, but it is not stored, meaning you have to reproduce it at runtime. The code I showed you does exactly that.
     
  30. ibyte

    ibyte

    Joined:
    Aug 14, 2009
    Posts:
    1,047
    Hi Dantus, okay ... I figured that was the case for dynamically created decals at run time where the surface to which the decal is going to be applied might change but I didn't figure that was the case for decals that were applied to static objects where the mesh will never change.

    That is where I was getting confused :D

    I thought the "mesh" that was generated and I see stored in the asset path was going to be used from now on and it would not have to be regenerated every time it was used. Does the actual mesh that is being created ("cut") and will always be the same have to be regenerated just because the UV is changing?

    I am just trying to understand why create an asset mesh and store it in the file system if it is to be rebuilt each time?

    EDIT: At the end of it all I guess where i still need help is in order to change the uv on a existing Decals prefab which components may I access and reuse the data already defined and which items have to be recreated on the fly. I tried to incorporate your code above while consulting the BulletExampleDynamicObject code but .... :)

    Thanks

    iByte
     
    Last edited: Apr 9, 2013
  31. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Hi iByte. You are right that the meshes don't have to be regenerated each time. The meshes that were produced are needed, because the Decal System can be used a a design tool to improve the visual quality of assets or scenes in general.
    But as soon as you want to change anything in them with decal operations, you have to produce a decals mesh. A decals mesh contains all the mesh data and additionally lots of other information. As that information is not stored, you have to recreate it. In your case it is indeed not needed to store the mesh, as you need to create it dynamically anyways. But in the case that you use the Decal System only as a design tool, the mesh itself is needed in a prefab.

    If you don't want to create decals meshes at runtime, there are a few alternatives.
    You may think about creating all variants in the editor. Like that, you could just replace them at runtime. If you have only a handful of uv rectangles, that would work out quite well. If you have more, it may not be the best solution.
    In the case that you have a tiled uv layout, like a 9x9 grid, where all parts have the same size, an alternative would be to modify the uv coordinates of the mesh directly.
     
  32. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Have fun :)
     
  33. ibyte

    ibyte

    Joined:
    Aug 14, 2009
    Posts:
    1,047
    Hi Dantus, Thanks for the info

    Okay .. I think I understand now. Would it be possible in the future to add an api to select the UV by name or index on the fly as part of the runtime?

    I had originally though to create all the UV rects and add a projector for each UV to the prefab and then just enable the projector I wanted to use at run time. When i tried toggling the active state for the projector GameObject at run time via the inspector I could not get it to stop showing both Decals. So then I thought using one projector and swapping decals might be easier. :eek: Should just activating and deactivating the projector game object work?

    The texture atlas I am using started out with textures all the same size but it was built (by EZ GUI) and trimmed as they were put on the Atlas. I am trying to reuse the same texture atlas to conserve resources.

    EDIT: Hmm since the UV's are stored as part of the Decals GameObject (or prefab) is there an api to access that info? or would I have to keep track of that info myself?

    Thanks

    iByte
     
    Last edited: Apr 10, 2013
  34. Coks

    Coks

    Joined:
    Jul 23, 2012
    Posts:
    28
    Hello!
    I created decal on skinning mesh runtime by example from Decal System Demos. And run my iPod4 - when generated decal fps drops to zero for a second. How to improve performance? Maybe I can generate decals on start and then show \ hide them. But can a single projector to be hidden?
     
  35. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    It is planned to add that in the future. But it will be part of the decals mesh api (and it will be in a pro version). Afterwards, the code I have shown, will be nearly identical. The improvement will only help the performance for that case.

    You can't activate/deactivate the projector at runtime, because that is also a decal operation. At runtime you can only activate/deactivate the whole DS_Decals game object.

    I expect it may be more complex to perform uv computations in that situtation.

    Yes, this information is available. DS_Decals has a uvRectangles array.
     
  36. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    The decal computation is extremely heavy, especially the one for skinned meshes. I am not surprised by that performance.
    Hiding a single projector is not possible. You can cheat though, by using vertex colors. You are able to find the vertex range for each projector if you create them at runtime and make the ones invisible you don't want to show. You may have a look at the colored bullet example to get an idea how it works. The example just gives each bullet hole a color, but you do do the same and make them invisible.
     
  37. Coks

    Coks

    Joined:
    Jul 23, 2012
    Posts:
    28
    Thanks for quick responce,
    But I cannot recolor vertex. I do same as example Bullet Color. But SkinnedDecalsMeshRenderers is a little different.
    I do like this, but does not work:
    Code (csharp):
    1. Color l_VertexColor = Color.red;
    2. int l_VertexCount = l_DecalProjector.DecalsMeshUpperVertexIndex - l_DecalProjector.DecalsMeshLowerVertexIndex + 1;
    3. for (int i = 0; i < l_VertexCount; i = i + 1) {
    4.        m_VertexColors.Add(l_VertexColor);
    5. }
    6. l_SkinnedMeshRenderer.sharedMesh.colors = m_VertexColors.ToArray ();
     
  38. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @Coks, are you using a shader that takes vertex colors into account? You may try to use "Decal/Transparent Diffuse Colored".
     
  39. Coks

    Coks

    Joined:
    Jul 23, 2012
    Posts:
    28
    I tried shader "Decal/Transparent Diffuse Colored". The result is same: does not respond to changes in vertex color. I tried the same in example Bootcamp Simple Skinning put this code - does not work.
     
    Last edited: Apr 10, 2013
  40. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    It works on my end. Make sure that the prefab that you drag into the decalsPrefab field has the correct material with the shader that supports vertex colors. I am using the ColoredBulletMarks material. Here is the code I am using:

    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 ColoredSkinnedBulletExampleCS : MonoBehaviour {
    14.    
    15.         // The prefab which contains the DS_SkinnedDecals script with already set material and
    16.         // uv rectangles.
    17.     public GameObject decalsPrefab;
    18.    
    19.         // The reference to the instantiated prefab's DS_SkinnedDecals instance.
    20.     private DS_SkinnedDecals m_Decals;
    21.  
    22.         // All the projectors that were created at runtime.
    23.     private List <SkinnedDecalProjector> m_DecalProjectors = new List <SkinnedDecalProjector> ();
    24.    
    25.         // Intermediate mesh data. Mesh data is added to that one for a specific projector
    26.         // in order to perform the cutting.
    27.     private SkinnedDecalsMesh m_DecalsMesh;
    28.     private SkinnedDecalsMeshCutter m_DecalsMeshCutter;
    29.  
    30.         // Vertex colors.
    31.     private List <Color> m_VertexColors = new List <Color> ();
    32.  
    33.         // The raycast hits a collider at a certain position. This value indicated how far we need to
    34.         // go back from that hit point along the ray of the raycast to place the new decal projector. Set
    35.         // this value to 0.0f to see why this is needed.
    36.     public float decalProjectorOffset = 0.5f;
    37.    
    38.         // The size of new decal projectors.
    39.     public Vector3 decalProjectorScale = new Vector3 (0.2f, 2.0f, 0.2f);
    40.     public float cullingAngle = 90.0f;
    41.     public float meshOffset = 0.001f;
    42.    
    43.         // We iterate through all the defined uv rectangles. This one indices which index we are using at
    44.         // the moment.
    45.     private int m_UVRectangleIndex = 0;
    46.  
    47.         // Color iterator.
    48.     private int m_ColorIndex = 0;
    49.  
    50.         // Move on to the next uv rectangle index.
    51.     private void NextUVRectangleIndex () {
    52.         m_UVRectangleIndex = m_UVRectangleIndex + 1;
    53.         if (m_UVRectangleIndex >= m_Decals.uvRectangles.Length) {
    54.             m_UVRectangleIndex = 0;
    55.         }
    56.     }
    57.  
    58.         // Move on to the next vertex color.
    59.     private void NextColorIndex () {
    60.         m_ColorIndex = m_ColorIndex + 1;
    61.         if (m_ColorIndex > 2) {
    62.             m_ColorIndex = 0;
    63.         }
    64.     }
    65.    
    66.     private Color CurrentColor {
    67.         get {
    68.             Color l_Color;
    69.             if (m_ColorIndex == 0) {
    70.                 l_Color = Color.red;
    71.             } else if (m_ColorIndex == 1) {
    72.                 l_Color = Color.green;
    73.             } else {
    74.                 l_Color = Color.blue;
    75.             }
    76.             return (l_Color);
    77.         }
    78.     }
    79.  
    80.     private void Start () {
    81.        
    82.             // Instantiate the prefab and get its decals instance.
    83.         GameObject l_Instance = Instantiate (decalsPrefab) as GameObject;
    84.         m_Decals = l_Instance.GetComponentInChildren <DS_SkinnedDecals> ();
    85.        
    86.         if (m_Decals == null) {
    87.             Debug.LogError ("The 'decalsPrefab' does not contain a 'DS_SkinnedDecals' instance!");
    88.         } else {
    89.            
    90.                 // Create the decals mesh (intermediate mesh data) for our decals instance.
    91.                 // Further we need a decals mesh cutter instance and the world to decals matrix.
    92.             m_DecalsMesh = new SkinnedDecalsMesh (m_Decals);
    93.             m_DecalsMeshCutter = new SkinnedDecalsMeshCutter ();
    94.         }
    95.     }
    96.    
    97.     private void Update () {
    98.         if (Input.GetKeyDown (KeyCode.C)) {
    99.                 // Remove all projectors.
    100.             while (m_DecalProjectors.Count > 0) {
    101.                 SkinnedDecalProjector l_Projector = m_DecalProjectors [m_DecalProjectors.Count - 1];
    102.  
    103.  
    104.                 m_DecalsMesh.RemoveProjector (l_Projector);
    105.                 m_DecalProjectors.RemoveAt (m_DecalProjectors.Count - 1);
    106.             }
    107.             m_VertexColors.Clear ();
    108.             m_Decals.UpdateSkinnedDecalsMeshes (m_DecalsMesh);
    109.         }
    110.  
    111.         if (Input.GetButtonDown ("Fire1")) {
    112.             Ray l_Ray = Camera.main.ViewportPointToRay (new Vector3 (0.5f, 0.5f, 0.0f));
    113.             RaycastHit l_RaycastHit;
    114.             if (Physics.Raycast (l_Ray, out l_RaycastHit, Mathf.Infinity)) {
    115.                
    116.                     // Collider hit.
    117.  
    118.                     // Make sure there are not too many projectors.
    119.                 if (m_DecalProjectors.Count >= 50) {
    120.                    
    121.                         // If there are more than 50 projectors, we remove the first one from
    122.                         // our list and certainly from the decals mesh (the intermediate mesh
    123.                         // format). All the mesh data that belongs to this projector will
    124.                         // be removed.
    125.                     SkinnedDecalProjector l_DecalProjector = m_DecalProjectors [0];
    126.  
    127.                         // The vertex color list has to be updated as well.
    128.                     m_VertexColors.RemoveRange (0, l_DecalProjector.DecalsMeshUpperVertexIndex + 1);
    129.  
    130.                     m_DecalProjectors.RemoveAt (0);
    131.                     m_DecalsMesh.RemoveProjector (l_DecalProjector);
    132.                 }
    133.  
    134.  
    135.                     // Calculate the position and rotation for the new decal projector.
    136.                 Vector3 l_ProjectorPosition = l_RaycastHit.point - (decalProjectorOffset * l_Ray.direction.normalized);
    137.                 Vector3 l_ForwardDirection = Camera.main.transform.up;
    138.                 Vector3 l_UpDirection = - Camera.main.transform.forward;
    139.                 Quaternion l_ProjectorRotation = Quaternion.LookRotation (l_ForwardDirection, l_UpDirection);
    140.                
    141.                     // Randomize the rotation.
    142.                 Quaternion l_RandomRotation = Quaternion.Euler (0.0f, Random.Range (0.0f, 360.0f), 0.0f);
    143.                 l_ProjectorRotation = l_ProjectorRotation * l_RandomRotation;
    144.  
    145.  
    146.                     // We hit a collider. Next we have to find the mesh that belongs to the collider.
    147.                     // That step depends on how you set up your mesh filters and collider relative to
    148.                     // each other in the game objects. It is important to have a consistent way in order
    149.                     // to have a simpler implementation.
    150.                
    151.                 SkinnedMeshRenderer l_SkinnedMeshRenderer = l_RaycastHit.collider.GetComponent <SkinnedMeshRenderer> ();
    152.                
    153.                 if (l_SkinnedMeshRenderer != null) {
    154.                     Mesh l_Mesh = null;
    155.                     l_Mesh = l_SkinnedMeshRenderer.sharedMesh;
    156.                    
    157.                     if (l_Mesh != null) {
    158.                        
    159.                             // Create the decal projector.
    160.                         SkinnedDecalProjector l_DecalProjector = new SkinnedDecalProjector (l_ProjectorPosition, l_ProjectorRotation, decalProjectorScale, cullingAngle, meshOffset, m_UVRectangleIndex, m_UVRectangleIndex);
    161.                        
    162.                             // Add the projector to our list and the decals mesh, such that both are
    163.                             // synchronized. All the mesh data that is now added to the decals mesh
    164.                             // will belong to this projector.
    165.                         m_DecalProjectors.Add (l_DecalProjector);
    166.                         m_DecalsMesh.AddProjector (l_DecalProjector);
    167.                        
    168.                             // Get the required matrices.
    169.                         Matrix4x4 l_WorldToMeshMatrix = l_RaycastHit.collider.renderer.transform.worldToLocalMatrix;
    170.                         Matrix4x4 l_MeshToWorldMatrix = l_RaycastHit.collider.renderer.transform.localToWorldMatrix;
    171.                        
    172.                             // Add the mesh data to the decals mesh, cut and offset it before we pass it
    173.                             // to the decals instance to be displayed.
    174.                         m_DecalsMesh.Add (l_Mesh, l_SkinnedMeshRenderer.bones, l_SkinnedMeshRenderer.quality, l_WorldToMeshMatrix, l_MeshToWorldMatrix);
    175.                         m_DecalsMeshCutter.CutDecalsPlanes (m_DecalsMesh);
    176.                         m_DecalsMesh.OffsetActiveProjectorVertices ();
    177.                         m_Decals.UpdateSkinnedDecalsMeshes (m_DecalsMesh);
    178.  
    179.                             // Update the vertex colors too.
    180.                         Color l_VertexColor = CurrentColor;
    181.                         int l_VertexCount = l_DecalProjector.DecalsMeshUpperVertexIndex - l_DecalProjector.DecalsMeshLowerVertexIndex + 1;
    182.                         for (int i = 0; i < l_VertexCount; i = i + 1) {
    183.                             m_VertexColors.Add (l_VertexColor);
    184.                         }
    185.                         m_Decals.SkinnedDecalsMeshRenderers [0].SkinnedMeshRenderer.sharedMesh.colors = m_VertexColors.ToArray ();
    186.                        
    187.                             // For the next hit, use a new uv rectangle. Usually, you would select the uv rectangle
    188.                             // based on the surface you have hit.
    189.                         NextUVRectangleIndex ();
    190.                         NextColorIndex ();
    191.                     }
    192.                 }
    193.             }
    194.         }
    195.     }
    196. }
     
  41. Coks

    Coks

    Joined:
    Jul 23, 2012
    Posts:
    28
    Ok. Thank you very much! It don't work for me because the option rendering path in camara was set Vertex Lit.
     
  42. MaVCArt

    MaVCArt

    Joined:
    Jan 27, 2013
    Posts:
    8
    Hi, me again :)

    I've been working with the decal system, and for one of our next implementations, we would like to be able to access a projector's material slot through a script, so that we can lerp the material's color information to achieve a pulsating glow effect. However, when writing the script we noticed that there is no variable available for the material slot under the DecalProjector Class.

    Is what we are trying to do possible? If not, do you suggest a workaround, or something else?

    any help would be greatly appreciated, and again, this is a fantastic system that has saved us a number of times already :)

    with kind regards
    mattias
     
  43. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Hello again :)

    You are right, the projectors don't have a material. All projectors of a DS_Decals instance share the same material with the goal maximize the performance, even if the mesh is computed at runtime. You would need to create a DS_Decals instance for each projector if you want to modify the material.
    An alternative approach would be to use vertex colors. Like that you can have unique colors per projector. Such an example comes with the Decal System. With that example code you can easily implement the color interpolation per projector.

     
  44. MaVCArt

    MaVCArt

    Joined:
    Jan 27, 2013
    Posts:
    8
    the problem isn't really that all the decals share the same material within the same projector - the problem is that we can't access that material from inside a script; we would like to change the material's parameters depending on a condition that is enabled when the player comes within a certain radius of the decal, for which we need the "this" of course.
    however, since "this" in this case is the DecalProjector Class (since you can't add a script component to the decal's mesh renderer), and since there is no variable available to edit the material it contains directly, that's where we hit a wall.

    as for the vertex colors, we are looking into that, thanks for the suggestion, we hadn't thought of it :)

    with kind regards
    mattias
     
  45. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @MaVCArt: DS_Decals has the property CurrentMaterial. DS_Decals instances are a container for several projectors. If you are looking for something that influences all projectors as in your case, it is very likely that you find it in DS_Decals.
     
  46. MaVCArt

    MaVCArt

    Joined:
    Jan 27, 2013
    Posts:
    8
    update: we got it working; apparently we simply needed to access the decals mesh renderers list that is provided in the decal class - we indeed found how to do it in the demo scenes that were provided with the decal system :)

    thanks for the help, this is a fantastic system!
     
  47. sburgoon

    sburgoon

    Joined:
    Jan 29, 2012
    Posts:
    38
    I couldn't find this anywhere in the thread, so my apologies if I missed it. Is there a way to get run-time decals (bullet example) working with static geometry that doesn't use a mesh collider? I tend to use primitive colliders wherenever possible (floors/walls), but that seems to break the decal system if static batching is used.

    When trying to add the decal like in the bullet example, I get errors like: Not allowed to access normals on mesh 'Combined Mesh (root: scene) 2 Instance'. I know Unity somewhat recently blocked read-access to the static mesh filter (I have no clue why, but it messed up aaron granbergs pathfinding raycast system for a bit too), so this might be a new-ish issue. I can't really think of a way around it, but figured I'd see if you had any clue. I have a sinking feeling that the unity projectors get around that by being internal (they are likely allowed access), since they still work. Any suggestions would be great, I love the decal system, and have it all setup and working, but ditching static batching just isn't feasible for my game, so I may have to revert to a projector based system and deal with the drawcall pain (shudder).

    UPDATE:

    so, my current best solution is to rasterize the colliders myself for static objects, and never fall back to the render mesh. This seems to work (I'll obviously want to cache this somehow so I'm not constantly recreating and destroying mesh objects for each decal), so I at least have a solution, but if you have any better ideas, feel free to shoot them my way. Love the decal system btw, really a time saver.

    -Sean
     
    Last edited: Apr 14, 2013
  48. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    @sburgoon: Just for your information, I will check that tomorrow and let you know.
     
  49. sburgoon

    sburgoon

    Joined:
    Jan 29, 2012
    Posts:
    38
    Thanks. I'm 90% sure that there's no way to get at the static batched mesh, so don't spend too much time. My solution of manually building and caching a mesh for the collider primitive is actually working just fine now, so it's not a big deal either way. Thanks again for the awesome decal system.
     
  50. Dantus

    Dantus

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

    Did you check that the mesh you are dealing with can be read in Unity? In the import settings for meshes you find a "Read/Write Enabled" option. If this one is not enabled, you get a "Not allowed to access ..." exception. It seems you are using some kind of mesh combine utility. There is hopefully an option to make the combined mesh readable.

    Hope this resolves your issue