Skip to content

Instantly share code, notes, and snippets.

@0xF6
Forked from RemyUnity/CubemapTextureBuilder.cs
Created May 27, 2022 06:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0xF6/e51a783096da9d929a51e6dc268058fa to your computer and use it in GitHub Desktop.
Save 0xF6/e51a783096da9d929a51e6dc268058fa to your computer and use it in GitHub Desktop.
Unity utility to convert 6 texture to a single cubemap texture
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.IO;
public class CubemapTextureBuilder : EditorWindow
{
[MenuItem("Tools/Cubemap Builder")]
public static void OpenWindow()
{
GetWindow<CubemapTextureBuilder>();
}
Texture2D[] textures = new Texture2D[6];
string[] labels = new string[] {
"Right", "Left",
"Top", "Bottom",
"Front", "Back"
};
TextureFormat[] HDRFormats = new TextureFormat[] {
TextureFormat.ASTC_HDR_10x10 ,
TextureFormat.ASTC_HDR_12x12 ,
TextureFormat.ASTC_HDR_4x4 ,
TextureFormat.ASTC_HDR_5x5 ,
TextureFormat.ASTC_HDR_6x6 ,
TextureFormat.ASTC_HDR_8x8 ,
TextureFormat.BC6H ,
TextureFormat.RGBAFloat ,
TextureFormat.RGBAHalf
};
Vector2Int[] placementRects = new Vector2Int[]
{
new Vector2Int(2, 1),
new Vector2Int(0, 1),
new Vector2Int(1, 2),
new Vector2Int(1, 0),
new Vector2Int(1, 1),
new Vector2Int(3, 1),
};
private void OnGUI()
{
for (int i=0; i<6; i++)
{
textures[i] = EditorGUILayout.ObjectField(labels[i], textures[i], typeof(Texture2D), false) as Texture2D;
}
if (GUILayout.Button("Build Cubemap"))
{
// Missing Texture
if (textures.Any( t => t == null ) )
{
EditorUtility.DisplayDialog("Cubemap Builder Error", "One or more texture is missing.", "Ok");
return;
}
// Get size
var size = textures[0].width;
// Not all of the same size or square
if ( textures.Any( t => (t.width!=size) || (t.height!=size) ) )
{
EditorUtility.DisplayDialog("Cubemap Builder Error", "All the textures need to be the same size and square.", "Ok");
return;
}
var isHDR = HDRFormats.Any(f => f == textures[0].format);
var texturePaths = textures.Select(t => AssetDatabase.GetAssetPath(t)).ToArray();
// Should be ok, ask for the file path.
var path = EditorUtility.SaveFilePanel("Save Cubemap", Path.GetDirectoryName(texturePaths[0]), "Cubemap", isHDR ? "exr" : "png");
if (string.IsNullOrEmpty(path)) return;
// Save the readable flag to restore it afterwards
var readableFlags = textures.Select(t => t.isReadable).ToArray();
// Get the importer and mark the textures as readable
var importers = texturePaths.Select(p => TextureImporter.GetAtPath(p) as TextureImporter).ToArray();
foreach (var importer in importers)
{
importer.isReadable = true;
}
AssetDatabase.Refresh();
foreach( var p in texturePaths)
{
AssetDatabase.ImportAsset(p);
}
// Build the cubemap texture
var cubeTexture = new Texture2D(size * 4, size * 3, isHDR ? TextureFormat.RGBAFloat : TextureFormat.RGBA32, false);
for (int i=0; i<6; i++)
{
cubeTexture.SetPixels(placementRects[i].x * size, placementRects[i].y * size, size, size, textures[i].GetPixels(0));
}
cubeTexture.Apply(false);
// Save the texture to the specified path, and destroy the temporary object
var bytes = isHDR ? cubeTexture.EncodeToEXR() : cubeTexture.EncodeToPNG();
File.WriteAllBytes(path, bytes);
DestroyImmediate(cubeTexture);
// Reset the read flags, and reimport everything
for (var i = 0; i<6; i++)
{
importers[i].isReadable = readableFlags[i];
}
path = path.Remove(0, Application.dataPath.Length-6);
AssetDatabase.ImportAsset(path);
var cubeImporter = AssetImporter.GetAtPath(path) as TextureImporter;
cubeImporter.textureShape = TextureImporterShape.TextureCube;
cubeImporter.sRGBTexture = false;
cubeImporter.generateCubemap = TextureImporterGenerateCubemap.FullCubemap;
foreach (var p in texturePaths)
{
AssetDatabase.ImportAsset(p);
}
AssetDatabase.ImportAsset(path);
AssetDatabase.Refresh();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment