Footstep material detection with Voxeland terrain object non-fatal errors

Syscrusher

New member
I've been testing UCC (latest version, as of 2018-10-11) in Unity 2018.2.11f1. Things are working quite well overall, but I'm getting a couple of instances per test run of the following error:

Code:
Material doesn't have a texture property '_MainTex'
UnityEngine.Material:get_mainTexture()
Opsive.UltimateCharacterController.SurfaceSystem.SurfaceManager:GetComplexSurfaceType(Material, RaycastHit) (at Assets/Opsive/UltimateCharacterController/Scripts/SurfaceSystem/SurfaceManager.cs:640)
Opsive.UltimateCharacterController.SurfaceSystem.SurfaceManager:GetComplexSurfaceType(RaycastHit) (at Assets/Opsive/UltimateCharacterController/Scripts/SurfaceSystem/SurfaceManager.cs:511)
Opsive.UltimateCharacterController.SurfaceSystem.SurfaceManager:GetSurfaceType(RaycastHit) (at Assets/Opsive/UltimateCharacterController/Scripts/SurfaceSystem/SurfaceManager.cs:270)
Opsive.UltimateCharacterController.SurfaceSystem.SurfaceManager:GetSurfaceEffect(RaycastHit, SurfaceImpact, SurfaceType&, Boolean&) (at Assets/Opsive/UltimateCharacterController/Scripts/SurfaceSystem/SurfaceManager.cs:248)
Opsive.UltimateCharacterController.SurfaceSystem.SurfaceManager:SpawnEffectInternal(RaycastHit, SurfaceImpact, Vector3, Single, GameObject, Vector3, Boolean) (at Assets/Opsive/UltimateCharacterController/Scripts/SurfaceSystem/SurfaceManager.cs:223)
Opsive.UltimateCharacterController.SurfaceSystem.SurfaceManager:SpawnEffect(RaycastHit, SurfaceImpact, Vector3, Single, GameObject, Vector3, Boolean) (at Assets/Opsive/UltimateCharacterController/Scripts/SurfaceSystem/SurfaceManager.cs:205)
Opsive.UltimateCharacterController.Character.CharacterFootEffects:FootStep(Transform, Boolean) (at Assets/Opsive/UltimateCharacterController/Scripts/Character/CharacterFootEffects.cs:327)
Opsive.UltimateCharacterController.Character.CharacterFootEffects:DetectBodyStep() (at Assets/Opsive/UltimateCharacterController/Scripts/Character/CharacterFootEffects.cs:252)
Opsive.UltimateCharacterController.Character.CharacterFootEffects:FixedUpdate() (at Assets/Opsive/UltimateCharacterController/Scripts/Character/CharacterFootEffects.cs:230)
These happen with each footstep, when I walk on a Voxeland static object (that is, a finite-sized voxel terrain). My test scene starts the character on a standard Unity cube with a box collider, then I walk off the cube onto the Voxeland object. The errors don't occur on the Unity cube. There's a very similar error that happens just 2 instances per run when you first step off the cube and onto the Voxeland terrain.

I think it's related to the fact that the Voxeland texture handling is unusual. This is not causing any problems in my testing, other than lack of footstep sounds. In the cinematic scene I'm making, I will be handling foley sounds independently, so this is absolutely not a problem for my project. I just wanted to let you know.

I am also extremely happy to report that locomotion and foot IK work absolutely flawlessly between UCC and Voxeland terrains. Opsive can be really proud of this, because I am testing on an exact copy of the voxel object from my cinematic scene, and the standard Unity character controller (the one in the Standard Assets) is utterly unable to reliably move on this object. I tried out the foot IK on all kinds of crazy surface contours and Could. Not. Break. It. Color me impressed!
 

Justin

Administrator
Staff member
Thanks for posting it here - what does the material look like of the surface that the character is trying to walk on? It doesn't look like the shader uses a MainTexture? I can do a check to prevent that error from occurring, but I'd like to use a fallback texture so the surface manager will play the correct effects.
 

Syscrusher

New member
Here is a code snippet from the shader, which is called Voxeland/Land. This is the part that defines the exposed shader parameters.


Code:
        _MainTexArr("Tex2D Array: Albedo (RGB), Height (A)", 2DArray) = "" {}
        _BumpMapArr("Tex 2D Array: Normals", 2DArray) = "" {}
        [HideInInspector] _MainTex0("Albedo (RGB)", 2D) = "black" {}        [HideInInspector] _BumpMap0("Normals", 2D) = "bump" {}    [HideInInspector] _Height0("Height", Float) = 1        [HideInInspector] _SpecParams0("SpecParams", Vector) = (-0.75,0.25,-0.35,1.5)
        [HideInInspector] _MainTex1("Albedo (RGB)", 2D) = "black" {}        [HideInInspector] _BumpMap1("Normals", 2D) = "bump" {}    [HideInInspector] _Height1("Height", Float) = 1        [HideInInspector] _SpecParams1("SpecParams", Vector) = (-0.75,0.25,-0.35,1.5)
 
# Similar lines clipped for brevity

         [HideInInspector] _MainTex19("Albedo (RGB)", 2D) = "black" {}        [HideInInspector] _BumpMap19("Normals", 2D) = "bump" {}          [HideInInspector] _Height19("Height", Float) = 1    [HideInInspector] _SpecParams19("SpecParams", Vector) = (-0.75,0.25,-0.35,1.5)
Based on a cursory code inspection of the Voxeland custom inspector, it appears their "texture array" is the standard Unity class, Texture2DArray.

Let me know if you need any other details. If you want a snapshot of my project, I can contact Denis (the dev of Voxeland) and ask his permission to send that to you. Given that you're working to make UCC compatible with his asset, I'm fairly confident he wouldn't object.

Scott
 

Justin

Administrator
Staff member
You can prevent the error by adding the following just before line 640 of SurfaceManager:
Code:
            if (material.HasProperty("_MainTexture")) {
                return null;
            }
If I add this code to the Asset Store version I'll use the int version rather than the string of HasProperty but for now this will prevent the error. From here it's adding support for the Voxeland shader. Out of the array of 20 textures, which would you would use for detecting what surface the character hit? I'm trying to determine the best way to handle this if a shader contains multiple textures and I only can use one.
 

zroc

New member
Hmm.. Having same issues with latest UCC.

Fixed it with slight modification to above code, but in two separate functions. Look at area commented with //zrocweb. Add the "!" qualifier to beginning of code(same as above Justin posted).

C#:
private Texture GetMainTexture(Collider collider)
        {
            if (collider == null) {
                return null;
            }

            Texture texture;
            if (!m_ColliderMainTextureMap.TryGetValue(collider, out texture)) {

                // The texture is retrieved from the renderer.
                var renderer = GetRenderer(collider);

                // zrocweb
                if (!renderer.sharedMaterial.HasProperty("_MainTexture")) {
                    return null;
                }

                if (renderer != null && renderer.sharedMaterial != null && renderer.sharedMaterial.mainTexture != null) {
                    texture = renderer.sharedMaterial.mainTexture;
                }

                m_ColliderMainTextureMap.Add(collider, texture);
            }

            return texture;
        }

And then again here.

C#:
private SurfaceType GetComplexSurfaceType(Material material, RaycastHit hit, Collider collider)
        {
            if (material == null) {
                return null;
            }

            if (!(collider is MeshCollider)) {
                Debug.LogWarning("Warning: Surface UV regions on " + collider.name + " only support MeshColliders.");
                return null;
            }

            // zrocweb
            if (!material.HasProperty("_MainTexture"))             {
                return null;
            }

            // The location may be on the secondary map.
            var texture = material.mainTexture;
            if (material.HasProperty(s_MaskID) && material.HasProperty(s_SecondaryTextureID)) {
                var maskTexture = material.GetTexture(s_MaskID) as Texture2D;
                if (maskTexture != null) {
                    var color = maskTexture.GetPixel((int)(hit.textureCoord.x * maskTexture.width), (int)(hit.textureCoord.y * maskTexture.height));
                    // A mask exists. If the alpha color is greater than 0.5f then the secondary texture should be used.
                    if (color.a > 0.5f) {
                        var secondaryTexture = material.GetTexture(s_SecondaryTextureID);
                        if (secondaryTexture != null) {
                            texture = secondaryTexture;
                        }
                    }
                }
            }

            if (texture == null) {
                return null;
            }
 
Top