Load Texture to dynamic Mesh in C#

Sally Whitemane

Well-known member
Featured Contributor
Messages
272
Reactions
1,047
Points
93
Patreon
sallyw
I made a tiled dancefloor mesh with a dynamic size at runtime in C#. No prefabs. Everything is working but I'm loading a 128x128 tile texture using:
https://docs.unity3d.com/530/Documentation/ScriptReference/Texture2D.LoadRawTextureData.html
Code:
Texture2D tex = new Texture2D(128, 128, TextureFormat.RGB24, false);
tex.LoadRawTextureData(rawrgb24Texture);
... and the data is coming from a huge hardcoded byte[] array. It's 128x128x3 => 49.152 bytes.

  1. open image in Gimp -> export by changing the extension to .raw and a popup will ask you about the format
  2. open your browser and go to https://hexed.it/ - that's an online hexeditor
  3. open file -> pick your exported .raw
  4. right click -> select all
  5. right click -> Export selected bytes as code snipped ...
  6. copy the Code Snippet to C# and fix the byte-array declaration to match the C# syntax
It works, but it's just an ugly workaround.
I cannot figure out how to load from a png-file to a texture in C# properly.
SallySoundFX experimental.jpg


Is it even possible to load png-texture directly? Or is it blocked too for security reasons?
Where do have to put the png? All Unity guides seem to assume there is an Assets/Resource-folder - VAM has no such thing?! Maybe VaM\Custom\Assets ?
Or do I have to create an AssetBundle? I've create one with a single .mat but don't know how to load it from C# in VAM. Also I'm not sure the .assetbundle was created correctly, because the file had no extension - so I added it. Tried to load on a CUA-atom - did not show any content. But that seems to be build for other content anyway.

It's super easy to get things working in the UnityEditor, but VAM is a blackbox.

A path- and name-list of all texture, materials and shaders from a clean VAM install would help too. There is probably already a good texture somewhere that I could use ... if I figure out how to set it to my Material. I don't want the plugin that I'm working on to be bloated with unnecessary resources.

Btw. I also tried to load a raw DXT1 compressed texture to reduce the size of the byte[]-array. It does work, but has textures glitches. Probably because I used a sketchy online converter to get the DXT1 data from a PNG. That thing returned a DDS file. So I guess it is not raw DXT, it's still inside a container maybe.

EDIT:
Manages to load raw DXT1 compressed data without glitches now. This time I exported the image with GIMP to DDS without mipmaps. The I removed the first 4 bytes - it is the ".dds" containers magic number to identify the fileformat. Then I removed another 124 bytes - that is the size of the DDS header. Size is down to 8192 bytes. The compression is noticeable though. Quality is worse.
C#:
public static byte[] dancefloorTileDXT1 = {
    // MAGIC NUMBER ".dds" 4 bytes https://en.wikipedia.org/wiki/DirectDraw_Surface
    //0x44, 0x44, 0x53, 0x20,
    // DDS HEADER 124 bytes https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
    //0x7C, 0x00, 0x00, 0x00, 0x07, 0x10, 0x08, 0x00, 0x80, 0x00, 0x00, 0x00,
    //0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    //0x01, 0x00, 0x00, 0x00, 0x47, 0x49, 0x4D, 0x50, 0x2D, 0x44, 0x44, 0x53,
    //0x5B, 0x09, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    //0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    //0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    //0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x44, 0x58, 0x54, 0x31,
    //0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    //0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
    //0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    //0x00, 0x00, 0x00, 0x00,
    // HEADER END - RAW DXT1 DATA START
                            0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA,
    0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00,
    // had to remove data to be able to post this (20000 characters limit)
};
 
Last edited:
Here is an example I made a while back for loading in an external .png as an animation texture. Should also handle .dds/.tga but haven't tested.

Hopefully helps in some way.
 

Attachments

  • AnimationTest.zip
    14.1 KB · Views: 0
That is very helpful! Thanks @VRStudy !

Reference for other readers and me: ;)
The path would be something like this:
C#:
string path = @"Custom\\yourpath\\myTexture.png";

Then use it with VAMs TextureLoader-class (the missing link I did not know about):
C#:
Texture2D myTexture = TextureLoader.LoadTexture(path);
// assign to your material ...
material.SetTexture("_MainTex", tex);

C#:
public class TextureLoader : MonoBehaviour
{
    public static Texture2D LoadTGA(string fileName)
    {
        using (FileStream tGAStream = File.OpenRead(fileName))
        {
            return LoadTGA(tGAStream);
        }
    }

    public static Texture2D LoadDDSManual(string ddsPath)
    {
        try
        {
            byte[] array = File.ReadAllBytes(ddsPath);
            byte b = array[4];
            if (b != 124)
            {
                throw new Exception("Invalid DDS DXTn texture. Unable to read");
            }

            int height = array[13] * 256 + array[12];
            int width = array[17] * 256 + array[16];
            byte b2 = array[87];
            TextureFormat format = TextureFormat.DXT5;
            if (b2 == 49)
            {
                format = TextureFormat.DXT1;
            }

            if (b2 == 53)
            {
                format = TextureFormat.DXT5;
            }

            int num = 128;
            byte[] array2 = new byte[array.Length - num];
            Buffer.BlockCopy(array, num, array2, 0, array.Length - num);
            FileInfo fileInfo = new FileInfo(ddsPath);
            Texture2D texture2D = new Texture2D(width, height, format, mipmap: false);
            texture2D.LoadRawTextureData(array2);
            texture2D.Apply();
            texture2D.name = fileInfo.Name;
            return texture2D;
        }
        catch (Exception arg)
        {
            Debug.LogError("Error: Could not load DDS " + arg);
            return new Texture2D(8, 8);
        }
    }

    public static void SetNormalMap(ref Texture2D tex)
    {
        Color[] pixels = tex.GetPixels();
        for (int i = 0; i < pixels.Length; i++)
        {
            Color color = pixels[i];
            color.r = 1f;
            color.a = pixels[i].r;
            pixels[i] = color;
        }

        tex.SetPixels(pixels);
        tex.Apply(updateMipmaps: true);
    }

    public static Texture2D LoadTexture(string fn, bool normalMap = false)
    {
        if (!File.Exists(fn))
        {
            return null;
        }

        switch (Path.GetExtension(fn).ToLower())
        {
            case ".png":
            case ".jpg":
                {
                    Texture2D tex2 = new Texture2D(1, 1);
                    ImageConversion.LoadImage(tex2, File.ReadAllBytes(fn));
                    if (normalMap)
                    {
                        SetNormalMap(ref tex2);
                    }

                    return tex2;
                }
            case ".dds":
                {
                    Texture2D tex3 = LoadDDSManual(fn);
                    if (normalMap)
                    {
                        SetNormalMap(ref tex3);
                    }

                    return tex3;
                }
            case ".tga":
                {
                    Texture2D tex = LoadTGA(fn);
                    if (normalMap)
                    {
                        SetNormalMap(ref tex);
                    }

                    return tex;
                }
            default:
                Debug.Log("texture not supported : " + fn);
                return null;
        }
    }

    public static Texture2D LoadTGA(Stream TGAStream, TextureFormat tf = TextureFormat.RGBA32, bool useMipMap = true, bool linear = false)
    {
        using (BinaryReader binaryReader = new BinaryReader(TGAStream))
        {
            binaryReader.BaseStream.Seek(12L, SeekOrigin.Begin);
            short num = binaryReader.ReadInt16();
            short num2 = binaryReader.ReadInt16();
            int num3 = binaryReader.ReadByte();
            binaryReader.BaseStream.Seek(1L, SeekOrigin.Current);
            Texture2D texture2D = new Texture2D(num, num2, tf, useMipMap, linear);
            Color32[] array = new Color32[num * num2];
            switch (num3)
            {
                case 32:
                    {
                        for (int j = 0; j < num * num2; j++)
                        {
                            byte b2 = binaryReader.ReadByte();
                            byte g2 = binaryReader.ReadByte();
                            byte r2 = binaryReader.ReadByte();
                            byte a = binaryReader.ReadByte();
                            ref Color32 reference2 = ref array[j];
                            reference2 = new Color32(r2, g2, b2, a);
                        }

                        break;
                    }
                case 24:
                    {
                        for (int i = 0; i < num * num2; i++)
                        {
                            byte b = binaryReader.ReadByte();
                            byte g = binaryReader.ReadByte();
                            byte r = binaryReader.ReadByte();
                            ref Color32 reference = ref array[i];
                            reference = new Color32(r, g, b, 1);
                        }

                        break;
                    }
                default:
                    throw new Exception("TGA texture had non 32/24 bit depth.");
            }

            texture2D.SetPixels32(array);
            texture2D.Apply();
            return texture2D;
        }
    }
}
 
Back
Top Bottom