Decalery (aka Bakery Decals)

Hey there! I got a question: I just got decalery but I can’t get it to work the way I want to. Here’s what I want to use it for:

I have a procedural level generation that builds the level out of different blocks, each block being a prefab.

The “minimal feature” I want is to be able to use the decal system within the prefabs to decorate each block with decals nicely. I can put in the decals and set the parent mode to source to have the meshes be a part of the prefab. However once I instantiate the prefabs the meshes are gone again, even if the game objects that contained them remain - it’s simply that the reference to the mesh is gone because I guess the mesh isn’t saved. So my “minimum requirement” for a functional decal system is to bake the decals into the prefabs so that they are present on instantiation.

The “better feature” I would like to do is to bake all the decals AFTER all the block prefabs have been instantiated. This would let me create decal setups that cross the boundaries between blocks, blurring the line between the individual blocks a little reducing the tile-based look of the approach even more. So I want to generate the Decals at runtime, but only once from all setup DecalGroups, rather than using the dynamic projector setup.

So here’s the question:

A) Can I bake decals into prefabs? If so, how?
B) Can I call a method (one for all, or one on each DecalGroup) at runtime, that generates the mesh? Basically a method that presses the “update” button. If so, which one?

Also, a random question out of curiosity: Why is it called a DecalGroup and not a DecalProjector or something like that?

Cheers!

EDIT: I guess “create mesh asset” solves the problem of A), but still doesn’t let me solve B). Plus the naming of the created mesh seems dependent on the scene I had opened, not related to the prefab at all, which would make more sense.

Awesome! Thanks for this. Keen for the URP graphs :smiley:

1 Like

Make sure to enable the “Create mesh asset” option - this way decal meshes will be stored as their own files, not inside the scene, and prefabs can properly reference them. I added that option in v1.1, so if you don’t see it, try updating from the store (or github) :slight_smile:

This is also doable, as all DecalGroup logic is available at runtime. I have a fast API for runtime decals in the docs, but in your case perhaps it’s enough and more flexible to just use the same call that the Decal Group inspector uses, which is:


            if (decalGroup.useBurst)
            {
                CPUBurstDecalUtils.UpdateDecal(decalGroup);
            }
            else
            {
                CPUDecalUtils.UpdateDecal(decalGroup);
            }

Good point :sweat_smile:
Originally because of the fact you can have a single mesh with many decals projecting at once. After I realized it wasn’t super intuitive… it was a bit too late, since I was using them all over my game, and renaming them or keeping two different branches would be annoying…

Yeah, maybe it needs different naming rules… I’ve changed them to work with Bakery Lightmapped Prefabs with no name conflicts (which are special prefabs storing baked lighting on their objects), but didn’t do it for regular prefabs, maybe I should.

This is also doable, as all DecalGroup logic is available at runtime. I have a fast API for runtime decals in the docs, but in your case perhaps it’s enough and more flexible to just use the same call that the Decal Group inspector uses

Yeah, I did find that part of the code but I would need to make sure to remove the mesh first, which is also part of the Decal Group Inspector, a little up. I was just curious if there was a nice helper method somewhere I couldn’t find. It felt a little dirty to just copy and paste that code into my own thing rather than to call a method on the DecalGroup like “UpdateDecal()” or something.

But I’ll see if that works, like so:

        public void BuildPrefabs(FloorLayoutData layoutData) {
            ClearPrefabs();

            for (int i = 0; i < layoutData.visuals.Count; i++) {
                BuildPrefabs(layoutData.visuals[i], layoutData.blockNames[i], 
                    layoutData.visualPos[i], layoutData.visualMirrorX[i], layoutData.visualMirrorY[i]);
            }

            var decals = root.GetComponentsInChildren<DecalGroup>();
            Regenerate(decals);
        }

        #region -- Decalery Interaction ------------------------------------

        void Regenerate(DecalGroup[] decals) {
            foreach (var d in decals) {
                ClearDecal(d);
                bool origOpt = d.optimize;
                d.optimize = false;
                d.linkToDecalGroup = false;
                d.linkToParent = true;

                if (d.useBurst) {
                    CPUBurstDecalUtils.UpdateDecal(d);
                }
                else {
                    CPUDecalUtils.UpdateDecal(d);
                }

                d.optimize = origOpt;
            }
        }

        void ClearDecal(DecalGroup d) {
            if (d.originalName == d.name) {
                var objs = d.sceneObjects;
                if (objs == null) return;
                for (int i = 0; i < objs.Count; i++) {
                    if (objs[i] == null) continue;

                    var prt = objs[i].transform.parent;
                    if (prt != null && prt.name == "NEW_DECAL#parent") {
                        DestroyImmediate(prt.gameObject);
                    }
                    DestroyImmediate(objs[i]);
                }
            }
            d.sceneObjects = new List<GameObject>();
            d.originalName = d.name;
        }

        #endregion

EDIT: Okay, that doesn’t work without the receiver mesh being read/write accessible, which is not really something I want to enable on all my meshes. Is there a way around that? Probably not. Then again, the dynamic Decal spawning does work at runtime.

For reference, the error messages:

  • Not allowed to access vertices on mesh ‘mainFloor’ (isReadable is false; Read/Write must be enabled in import settings)
  • Not allowed to access normals on mesh ‘mainFloor’ (isReadable is false; Read/Write must be enabled in import settings)
  • Not allowed to access triangles/indices on mesh ‘mainFloor’ (isReadable is false; Read/Write must be enabled in import settings)
  • Not allowed to access uv2 on mesh ‘mainFloor’ (isReadable is false; Read/Write must be enabled in import settings)
  • Not allowed to access uv on mesh ‘mainFloor’ (isReadable is false; Read/Write must be enabled in import settings)
1 Like

Added a quick fix to get the proper name of the Prefab into the created mesh asset. In CPUDecalUtils at line 1110 and on:

#if UNITY_EDITOR
            if (decal.makeAsset)
            {
                var activeScene = EditorSceneManager.GetActiveScene();
                var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
                var sceneName = prefabStage != null ? prefabStage.prefabContentsRoot.name : activeScene.name;

                newMesh.name = sceneName + "_" + newGO.name;
    #if BAKERY_INCLUDED
1 Like

Added some little scripts to convert from URP/HDRP decal projectors to Decalery today.

Those aren’t perfect (yet) and only convert main decal parameters, but it’s a start.

It’s the main limitation of all CPU techniques, I mentioned it in the docs:

GPU decal mode avoids this issue, but it’s also more simplified (quad/trail projections, not complex geometry). If your decals are mostly quad-like, you can use it, but you’ll need the runtime API (as described in the docs).

Nice :slight_smile:
PrefabStageUtility was only added in 2021 it seems… prefab API must be the most confusing and over-engineered one in Unity.
I’ve added your fix, but under UNITY_2021_2_OR_NEWER. Older Unity versions will still use the active scene name.

Yeah, I figured that out so it might just be hand made quads since the geometry can handle that in my case. That said, I am also getting a bunch of shader warnings when builing in Unity 2022.

Shader warning in 'Decalery/DecaleryStandardPBR': 'UNITY_EXTRACT_FOG': macro redefinition. Previous definition found at C:/Program Files/Unity/Hub/Editor/2022.3.20f1-x86_64/Editor/Data/CGIncludes/UnityCG.cginc:1093. at line 545

Shader warning in 'Decalery/DecaleryStandardPBR': 'UNITY_EXTRACT_FOG_FROM_TSPACE': macro redefinition. Previous definition found at C:/Program Files/Unity/Hub/Editor/2022.3.20f1-x86_64/Editor/Data/CGIncludes/UnityCG.cginc:1094. at line 546

Shader warning in 'Decalery/DecaleryStandardPBR': 'UNITY_EXTRACT_FOG_FROM_WORLD_POS': macro redefinition. Previous definition found at C:/Program Files/Unity/Hub/Editor/2022.3.20f1-x86_64/Editor/Data/CGIncludes/UnityCG.cginc:1095. at line 547

Shader warning in 'Decalery/DecaleryStandardPBR': 'UNITY_EXTRACT_FOG_FROM_EYE_VEC': macro redefinition. Previous definition found at C:/Program Files/Unity/Hub/Editor/2022.3.20f1-x86_64/Editor/Data/CGIncludes/UnityCG.cginc:1096. at line 548

Shader warning in 'Decalery/DecaleryStandardPBR': semantics in type overridden by variable/function or enclosing type at line 708 (on d3d11)

Compiling Subshader: 0, Pass: Deferred, Fragment program with DIRECTIONAL
Platform defines: SHADER_API_DESKTOP UNITY_ENABLE_DETAIL_NORMALMAP UNITY_ENABLE_REFLECTION_BUFFERS UNITY_LIGHTMAP_FULL_HDR UNITY_LIGHT_PROBE_PROXY_VOLUME UNITY_PASS_DEFERRED UNITY_PBS_USE_BRDF1 UNITY_SPECCUBE_BLENDING UNITY_SPECCUBE_BOX_PROJECTION UNITY_USE_DITHER_MASK_FOR_ALPHABLENDED_SHADOWS
Disabled keywords: BAKERY_COMPRESSED_VOLUME BAKERY_MONOSH BAKERY_SH BAKERY_VOLUME DIRLIGHTMAP_COMBINED DYNAMICLIGHTMAP_ON INDIRECT_DRAW INSTANCING_ON LIGHTMAP_ON LIGHTMAP_SHADOW_MIXING LIGHTPROBE_SH MODE_NORMALONLY MODE_NORMAL_AO_ONLY NORMALMAP SHADER_API_GLES30 SHADOWS_SCREEN SHADOWS_SHADOWMASK TEXARRAY UNITY_ASTC_NORMALMAP_ENCODING UNITY_COLORSPACE_GAMMA UNITY_FRAMEBUFFER_FETCH_AVAILABLE UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS UNITY_HARDWARE_TIER1 UNITY_HARDWARE_TIER2 UNITY_HARDWARE_TIER3 UNITY_LIGHTMAP_DLDR_ENCODING UNITY_LIGHTMAP_RGBM_ENCODING UNITY_METAL_SHADOWS_USE_POINT_FILTERING UNITY_NO_DXT5nm UNITY_NO_FULL_STANDARD_SHADER UNITY_NO_SCREENSPACE_SHADOWS UNITY_PBS_USE_BRDF2 UNITY_PBS_USE_BRDF3 UNITY_PRETRANSFORM_TO_DISPLAY_ORIENTATION UNITY_UNIFIED_SHADER_PRECISION_MODEL UNITY_VIRTUAL_TEXTURING
Shader warning in 'Hidden/fGPUDecalWriteShader': floating point division by zero at Assets/Decalery/fGPUDecalWriteShaderMain.cginc(361) (on d3d11)

Compiling Subshader: 0, Pass: ClearPartialDecal, Fragment program with <no keywords>
Platform defines: SHADER_API_DESKTOP UNITY_ENABLE_DETAIL_NORMALMAP UNITY_ENABLE_REFLECTION_BUFFERS UNITY_LIGHTMAP_FULL_HDR UNITY_LIGHT_PROBE_PROXY_VOLUME UNITY_PBS_USE_BRDF1 UNITY_SPECCUBE_BLENDING UNITY_SPECCUBE_BOX_PROJECTION UNITY_USE_DITHER_MASK_FOR_ALPHABLENDED_SHADOWS
Disabled keywords: SHADER_API_GLES30 UNITY_ASTC_NORMALMAP_ENCODING UNITY_COLORSPACE_GAMMA UNITY_FRAMEBUFFER_FETCH_AVAILABLE UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS UNITY_HARDWARE_TIER1 UNITY_HARDWARE_TIER2 UNITY_HARDWARE_TIER3 UNITY_LIGHTMAP_DLDR_ENCODING UNITY_LIGHTMAP_RGBM_ENCODING UNITY_METAL_SHADOWS_USE_POINT_FILTERING UNITY_NO_DXT5nm UNITY_NO_FULL_STANDARD_SHADER UNITY_NO_SCREENSPACE_SHADOWS UNITY_PBS_USE_BRDF2 UNITY_PBS_USE_BRDF3 UNITY_PRETRANSFORM_TO_DISPLAY_ORIENTATION UNITY_UNIFIED_SHADER_PRECISION_MODEL UNITY_VIRTUAL_TEXTURING

Also this, which is unrelated to the shaders:

Assets\Decalery\CPUDecalUtils.cs(1063,24): warning CS0219: The variable 'existingPrefab' is assigned but its value is never used

1 Like

Thanks, I’ll fix these.

It’s actually used, but only if UNITY_EDITOR. At runtime the related code is stripped, but the variable remains.

EDIT: Fixed.

Added a little option to project more UV channels (apart from UV2) from receivers to the decal mesh:

I already buy, thanks for this.
And Happy new year

1 Like

Happy new year! :slight_smile:

1 Like

@guycalledfrank One small note about the new UV copy feature: the numbering starts from 1 to 8, while the naming convention in shaders and code uses 0 to 7.

It’s a minor detail, but maybe we could add an option in the project settings for this plugin to toggle between the two numbering styles? :slight_smile:

@guycalledfrank Is there a chance to copy all UVs from the Mesh Decal we’re projecting? Currently, it only copies UV0 and adds UV1 from the Lightmap UVs. I understand this somewhat contradicts the concept of projected decals, but I have an edge case where I’ve prepared mesh decals in Blender that use multiple UVs in the Decal Shader to achieve unique effects.

I would suggest that decalery copies all UVs from the mesh decal, not just UV0 (except UV1, as it is transferred from receivers), unless you specify in the component that additional UVs should be copied from receivers.

What do you think about this approach?

It would probably confuse me even more…

Current numbering is Unity-Mesh-style:

image

It should correspond to this numbering, and I chose it because it’s the most “user-facing” one (especially when you check the mesh in Inspector to make totally sure it IS the same UV channel).

I think it’s better to be able to manually select additional UV channels to get from the decal, similar to how you choose the channels to get from the receivers now.

Just added:

My bad! Thank you for the explanation, I wish Unity wouldn’t be so confusing. Nevertheless, thank you very much! I’m testing it right now :slight_smile:

I tested my mesh, and it seems that the copied UV from the Decal mesh is distorted, and I don’t know what to do with it.

Maybe it is because the meshes are very different, I tried to change both meshes but the results are always broken somewhere. Do you have maybe some tips for me?

I’m sending the comparison of My Decal Mesh and one generated Mesh:

Also if you are willing to try something on meshes itself I’m sending the meshes I’m working on:
DecalAndReceiberMeshes.rar (30.3 KB)

Hello! I finally bought Decalery and I’m excited to try it in a new project.

I’ve imported and I’m checking out the runtime examples but the decals don’t seem to be working right. I’m on the Built-In Render Pipeline with Deferred rendering and I have the “DecalRuntimeSettings_Deferred” game object active too.
I’m on Unity 6000.0.23f


The decals seem to either not show up or show up in the wrong place

I must be missing something, any help is appreciated!

Yeah, that implementation kinda sucked. I’ve just rewrote it differently. Can you try the latest patch?

What GAPI are you on (i.e. DX11/12/Vulkan/Metal)? It should be visible in the Editor window header.
DecalRuntimeSettings_Deferred uses the Full GPU mode, but, as mentioned in the docs:

The bug I see looks like what I saw when running it on Vulkan with GPU modes. Banged my head against the wall for a few days but couldn’t figure it out. There are no errors/warnings anywhere. Can even be some compiler issue.
I wanna try rewriting it using compute shaders, it should have more predictable behaviour. On 2021.2+ Unity is finally able to feed vertex buffers to CS, and the only reason I didn’t go for this approach was to make it work on older versions…

Oops, that was it!
I was testing Vulkan with Unity 6 and forgot I left it at that one so I switched to DX11 yesterday but the decals still didn’t show up. Now as I saw your reply I tried reinstalling the whole package again and that was the trick, decals work!

EDIT: What would be the best way to spawn decals from the player camera at whatever distance? Like a shooting mechanic to spawn decals wherever the raycast hit? Is it necessary to create ‘bullet’ transforms?

Hi,

Thank you for the changes. The UVs copy correctly, but my Mesh Decal and Receiver are very complex, and the generated mesh has some holes and issues. But don’t worry about it—it’s simply too challenging for the algorithm.

Instead, I copied the UVs (including the Lightmap UVs, which I created manually for the Receivers) directly in Blender, wrote a script to steal the lightmap from the Receiver, and used your shaders. It works the same way but without generating meshes. :slightly_smiling_face:

For cases like this, I’ll continue working with this method, but I’ll use Decalery for everything else. :slightly_smiling_face: