/// <summary> /// Creates dummy texture /// </summary> /// <param name="Material">Material to use for texture parameters</param> private void SetInvalidTexture(ObjFile.Material Material) { Format = GBI.G_IM_FMT_RGBA; Size = GBI.G_IM_SIZ_16b; Data = new byte[Material.Width * Material.Height * 2]; Palette = null; }
public void ConvertScene(bool ConsecutiveRoomInject, bool ForceRGBATextures) { /* Check if collision model is valid */ if (ColModel == null) { throw new Exception("No collision model defined"); } /* Process rooms... */ for (int i = 0; i < _Rooms.Count; i++) { /* Get current room from list */ ZRoom Room = _Rooms[i]; /* Create new room file, DList offset list and texture list */ Room.RoomData = new List <byte>(); Room.DLists = new List <NDisplayList>(); Textures = new List <NTexture>(); /* Create room header */ WriteRoomHeader(Room); /* Write objects */ if (Room.ZObjects.Count != 0) { ObjectOffset = Room.RoomData.Count; foreach (ZUShort Obj in Room.ZObjects) { Helpers.Append16(ref Room.RoomData, Obj.Value); } AddPadding(ref Room.RoomData, 8); } /* Write actors */ if (Room.ZActors.Count != 0) { ActorOffset = Room.RoomData.Count; foreach (ZActor Actor in Room.ZActors) { Helpers.Append16(ref Room.RoomData, Actor.Number); Helpers.Append16(ref Room.RoomData, (ushort)Actor.XPos); Helpers.Append16(ref Room.RoomData, (ushort)Actor.YPos); Helpers.Append16(ref Room.RoomData, (ushort)Actor.ZPos); Helpers.Append16(ref Room.RoomData, (ushort)Actor.XRot); Helpers.Append16(ref Room.RoomData, (ushort)Actor.YRot); Helpers.Append16(ref Room.RoomData, (ushort)Actor.ZRot); Helpers.Append16(ref Room.RoomData, Actor.Variable); } AddPadding(ref Room.RoomData, 8); } /* Prepare dummy mesh header */ MeshHeaderOffset = Room.MeshHeaderOffset = Room.RoomData.Count; Helpers.Append32(ref Room.RoomData, 0); /* Mesh type X, Y meshes */ Helpers.Append32(ref Room.RoomData, 0); /* Start address */ Helpers.Append32(ref Room.RoomData, 0); /* End address */ for (int j = 0; j < Room.ObjModel.Groups.Count; j++) { Helpers.Append64(ref Room.RoomData, 0); Helpers.Append32(ref Room.RoomData, 0); /* Display List offset 1 */ Helpers.Append32(ref Room.RoomData, 0); /* Display List offset 2 */ } AddPadding(ref Room.RoomData, 8); /* Create textures */ for (int M = 0; M < Room.ObjModel.Materials.Count; M++) { /* Get material & force RGBA default */ ObjFile.Material Mat = Room.ObjModel.Materials[M]; Mat.ForceRGBA = ForceRGBATextures; /* ---VERY kludgy RGBA forcing code--- */ for (int x = 0; x < Room.ObjModel.Groups.Count; x++) { /* If group has multitex material number... */ if (Room.ObjModel.Groups[x].MultiTexMaterial != -1) { /* Turn force RGBA ON for multitex material */ Room.ObjModel.Materials[Room.ObjModel.Groups[x].MultiTexMaterial].ForceRGBA = true; /* Scan group's triangles for current material name... */ for (int y = 0; y < Room.ObjModel.Groups[x].Triangles.Count; y++) { if (Room.ObjModel.Groups[x].Triangles[y].MaterialName == Mat.Name) { /* Turn force RGBA ON for current material */ Mat.ForceRGBA = true; goto Cont; } } } } /* Continue here... */ Cont: if (Mat.TexImage == null) { continue; } /* Create new texture, convert current material */ NTexture Texture = new NTexture(); Texture.Convert(Mat); /* Add current offset to texture offset list */ Texture.TexOffset = ((uint)Room.RoomData.Count); /* Write converted data to room file */ Room.RoomData.AddRange(Texture.Data); /* See if we've got a CI-format texture... */ int Format = ((Texture.Type & 0xE0) >> 5); #if DEBUG Console.WriteLine("Texture format N64: " + Format.ToString("X2")); #endif if (Format == GBI.G_IM_FMT_CI) { /* If it's CI, add current offset to palette offset list */ Texture.PalOffset = ((uint)Room.RoomData.Count); /* Write palette data to room file */ Room.RoomData.AddRange(Texture.Palette); } else { /* Add dummy entry to palette offset list */ Texture.PalOffset = Dummy; } Textures.Add(Texture); } /* Create Display Lists */ for (int j = 0; j < Room.ObjModel.Groups.Count; j++) { NDisplayList DList = new NDisplayList(Scale, Room.ObjModel.Groups[j].TintAlpha, 1.0f, IsOutdoors, Room.ObjModel.Groups[j].BackfaceCulling); DList.Convert(Room.ObjModel, j, Textures, (uint)Room.RoomData.Count); if (DList.Data != null) { Room.RoomData.AddRange(DList.Data); } Room.DLists.Add(DList); } /* Fix room header and add missing data */ FixRoomHeader(Room); /* Add some padding for good measure */ AddPadding(ref Room.RoomData, 0x1000); /* Store room data length */ Room.FullDataLength = Room.RoomData.ToArray().Length; /* Put modified room info back into list */ _Rooms[i] = Room; } /* Create new scene file */ SceneData = new List <byte>(); /* Write scene header */ Helpers.Append64(ref SceneData, (ulong)(0x1502000000000000 | Music)); /* Sound settings */ CmdMapListOffset = SceneData.Count; Helpers.Append64(ref SceneData, 0x0400000000000000); /* Map list */ CmdTransitionsOffset = SceneData.Count; Helpers.Append64(ref SceneData, 0x0E00000000000000); /* Transition list */ Helpers.Append64(ref SceneData, 0x1900000000000003); /* Cutscenes */ CmdCollisionOffset = SceneData.Count; Helpers.Append64(ref SceneData, 0x0300000000000000); /* Collision header */ CmdEntranceListOffset = SceneData.Count; Helpers.Append64(ref SceneData, 0x0600000000000000); /* Entrance index */ if (IsOutdoors == true) /* Special objects */ { Helpers.Append64(ref SceneData, 0x0701000000000002); } else { Helpers.Append64(ref SceneData, 0x0702000000000003); } CmdSpawnPointOffset = SceneData.Count; Helpers.Append64(ref SceneData, 0x0000000000000000); /* Spawn point list */ if (IsOutdoors == true) /* Skybox / lighting settings */ { Helpers.Append64(ref SceneData, 0x1100000001000000); } else { Helpers.Append64(ref SceneData, 0x1100000000000100); } CmdExitListOffset = SceneData.Count; Helpers.Append64(ref SceneData, 0x1300000000000000); /* Exit list */ CmdEnvironmentsOffset = SceneData.Count; Helpers.Append64(ref SceneData, 0x0F00000000000000); /* Environments */ Helpers.Append64(ref SceneData, 0x1400000000000000); /* End marker */ /* Fix scene header; map list ... */ Helpers.Overwrite32(ref SceneData, CmdMapListOffset, (uint)(0x04000000 | (_Rooms.Count << 16))); Helpers.Overwrite32(ref SceneData, CmdMapListOffset + 4, (uint)(0x02000000 | SceneData.Count)); if (ConsecutiveRoomInject == true) { int RoomInjectOffset = _Rooms[0].InjectOffset; foreach (ZRoom Room in _Rooms) { Helpers.Append32(ref SceneData, (uint)RoomInjectOffset); Helpers.Append32(ref SceneData, (uint)(RoomInjectOffset + Room.FullDataLength)); RoomInjectOffset += Room.FullDataLength; } } else { foreach (ZRoom Room in _Rooms) { Helpers.Append32(ref SceneData, (uint)Room.InjectOffset); Helpers.Append32(ref SceneData, (uint)(Room.InjectOffset + Room.FullDataLength)); } } AddPadding(ref SceneData, 8); /* ... transition list ... */ Helpers.Overwrite32(ref SceneData, CmdTransitionsOffset, (uint)(0x0E000000 | (Transitions.Count << 16))); Helpers.Overwrite32(ref SceneData, CmdTransitionsOffset + 4, (uint)(0x02000000 | SceneData.Count)); foreach (ZActor Trans in Transitions) { SceneData.Add(Trans.FrontSwitchTo); SceneData.Add(Trans.FrontCamera); SceneData.Add(Trans.BackSwitchTo); SceneData.Add(Trans.BackCamera); Helpers.Append16(ref SceneData, Trans.Number); Helpers.Append16(ref SceneData, (ushort)Trans.XPos); Helpers.Append16(ref SceneData, (ushort)Trans.YPos); Helpers.Append16(ref SceneData, (ushort)Trans.ZPos); Helpers.Append16(ref SceneData, (ushort)Trans.YRot); Helpers.Append16(ref SceneData, Trans.Variable); } AddPadding(ref SceneData, 8); /* ... exit list ... */ Helpers.Overwrite32(ref SceneData, CmdExitListOffset + 4, (uint)(0x02000000 | SceneData.Count)); foreach (ZUShort Exit in ExitList) { Helpers.Append16(ref SceneData, Exit.Value); } AddPadding(ref SceneData, 8); /* ... spawn point list ... */ Helpers.Overwrite32(ref SceneData, CmdSpawnPointOffset, (uint)(0x00000000 | (SpawnPoints.Count << 16))); Helpers.Overwrite32(ref SceneData, CmdSpawnPointOffset + 4, (uint)(0x02000000 | SceneData.Count)); foreach (ZActor Spawn in SpawnPoints) { Helpers.Append16(ref SceneData, Spawn.Number); Helpers.Append16(ref SceneData, (ushort)Spawn.XPos); Helpers.Append16(ref SceneData, (ushort)Spawn.YPos); Helpers.Append16(ref SceneData, (ushort)Spawn.ZPos); Helpers.Append16(ref SceneData, (ushort)Spawn.XRot); Helpers.Append16(ref SceneData, (ushort)Spawn.YRot); Helpers.Append16(ref SceneData, (ushort)Spawn.ZRot); Helpers.Append16(ref SceneData, Spawn.Variable); } AddPadding(ref SceneData, 8); /* ... environments ... */ Helpers.Overwrite32(ref SceneData, CmdEnvironmentsOffset, (uint)(0x0F000000 | (Environments.Count << 16))); Helpers.Overwrite32(ref SceneData, CmdEnvironmentsOffset + 4, (uint)(0x02000000 | SceneData.Count)); foreach (ZEnvironment Env in Environments) { Helpers.Append48(ref SceneData, (ulong)(Env.C1C.ToArgb() & 0xFFFFFF)); Helpers.Append48(ref SceneData, (ulong)(Env.C2C.ToArgb() & 0xFFFFFF)); Helpers.Append48(ref SceneData, (ulong)(Env.C3C.ToArgb() & 0xFFFFFF)); Helpers.Append48(ref SceneData, (ulong)(Env.C4C.ToArgb() & 0xFFFFFF)); Helpers.Append48(ref SceneData, (ulong)(Env.C5C.ToArgb() & 0xFFFFFF)); Helpers.Append48(ref SceneData, (ulong)(Env.FogColorC.ToArgb() & 0xFFFFFF)); Helpers.Append16(ref SceneData, Env.FogDistance); Helpers.Append16(ref SceneData, Env.DrawDistance); } AddPadding(ref SceneData, 8); /* ... entrance list ... */ Helpers.Overwrite32(ref SceneData, CmdEntranceListOffset + 4, (uint)(0x02000000 | SceneData.Count)); Helpers.Append16(ref SceneData, 0x0000); /* Map 0, spawn point 0 */ AddPadding(ref SceneData, 8); /* ... collision */ WriteSceneCollision(); }
/// <summary> /// Converts given ObjFile.Material into N64 texture /// </summary> /// <param name="Material">Material to convert</param> public void Convert(ObjFile.Material Material) { try { BitmapData RawBmp = null; byte[] Raw = null; IsGrayscale = false; HasAlpha = false; CheckImageProperties(Material.TexImage); if (IsSizeValid() == false) { throw new Exception(string.Format("Invalid texture size {0}x{1}", Width, Height)); } try { RawBmp = Material.TexImage.LockBits( new Rectangle(0, 0, (int)Material.Width, (int)Material.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb ); int TotalSize = RawBmp.Height * RawBmp.Stride; Raw = new byte[TotalSize]; System.Runtime.InteropServices.Marshal.Copy(RawBmp.Scan0, Raw, 0, TotalSize); } finally { if (RawBmp != null) { Material.TexImage.UnlockBits(RawBmp); } } //throw new Exception("Too many grayshades in texture OR invalid size"); List <Color> UniqueColors = GetUniqueColors(Material.TexImage); if (IsGrayscale == true && Material.ForceRGBA == false) { if (HasAlpha == true) { /* Convert to IA */ if (UniqueColors.Count <= 16) { #if DEBUG Console.WriteLine("IA 8-bit <- " + Material.Name + ", " + Material.Width.ToString() + "*" + Material.Height.ToString() + ", " + UniqueColors.Count.ToString() + " grayshades"); #endif /* Set type, IA 8-bit */ Format = GBI.G_IM_FMT_IA; Size = GBI.G_IM_SIZ_8b; /* Generate texture buffer */ Data = new byte[Material.Width * Material.Height]; Palette = null; /* Loop through pixels, convert to IA 8-bit, write to texture buffer */ for (int i = 0, j = 0; i < Raw.Length; i += 4, j++) { Data[j] = (byte)(((Raw[i] / 16) << 4) | ((Raw[i + 3] / 16) & 0xF)); } } else if (UniqueColors.Count <= 256 && Material.Width * Material.Height <= 2048) { #if DEBUG Console.WriteLine("IA 16-bit <- " + Material.Name + ", " + Material.Width.ToString() + "*" + Material.Height.ToString() + ", " + UniqueColors.Count.ToString() + " grayshades"); #endif /* Set type, IA 16-bit */ Format = GBI.G_IM_FMT_IA; Size = GBI.G_IM_SIZ_16b; /* Generate texture buffer */ Data = new byte[Material.Width * Material.Height * 2]; Palette = null; /* Loop through pixels, convert to IA 16-bit, write to texture buffer */ for (int i = 0, j = 0; i < Raw.Length; i += 4, j += 2) { Data[j] = Raw[i + 2]; Data[j + 1] = Raw[i + 3]; } } else { /* Uh-oh, too many grayshades OR invalid size! */ throw new Exception("Too many grayshades in texture OR invalid size"); } } else { /* Convert to I */ if (UniqueColors.Count <= 16) { #if DEBUG Console.WriteLine("I 4-bit <- " + Material.Name + ", " + Material.Width.ToString() + "*" + Material.Height.ToString() + ", " + UniqueColors.Count.ToString() + " grayshades"); #endif /* Set type, I 4-bit */ Format = GBI.G_IM_FMT_I; Size = GBI.G_IM_SIZ_4b; /* Generate texture buffer */ Data = new byte[(Material.Width * Material.Height) / 2]; Palette = null; /* Loop through pixels, convert to I 4-bit, write to texture buffer */ for (int i = 0, j = 0; i < Raw.Length; i += 8, j++) { Data[j] = (byte)(((Raw[i] / 16) << 4) | ((Raw[i + 4] / 16) & 0xF)); } } else if (UniqueColors.Count <= 256 && Material.Width * Material.Height <= 4096) { #if DEBUG Console.WriteLine("I 8-bit <- " + Material.Name + ", " + Material.Width.ToString() + "*" + Material.Height.ToString() + ", " + UniqueColors.Count.ToString() + " grayshades"); #endif /* Set type, I 8-bit */ Format = GBI.G_IM_FMT_I; Size = GBI.G_IM_SIZ_8b; /* Generate texture buffer */ Data = new byte[Material.Width * Material.Height]; Palette = null; /* Loop through pixels, convert to I 8-bit, write to texture buffer */ for (int i = 0, j = 0; i < Raw.Length; i += 4, j++) { Data[j] = Raw[i]; } } else { /* Uh-oh, too many grayshades OR invalid size! */ throw new Exception("Too many grayshades in texture OR invalid size"); } } } else { /* Convert to CI */ if (UniqueColors.Count <= 16 && Material.ForceRGBA == false) { #if DEBUG Console.WriteLine("CI 4-bit <- " + Material.Name + ", " + Material.Width.ToString() + "*" + Material.Height.ToString() + ", " + UniqueColors.Count.ToString() + " unique colors"); #endif /* Set type, CI 4-bit */ Format = GBI.G_IM_FMT_CI; Size = GBI.G_IM_SIZ_4b; /* Generate texture buffer */ Data = new byte[(Material.Width * Material.Height) / 2]; /* Generate 16-color RGBA5551 palette */ Palette = GeneratePalette(UniqueColors, 16); /* Loop through pixels, get palette indexes, write to texture buffer */ for (int i = 0, j = 0; i < Raw.Length; i += 8, j++) { ushort RGBA5551_1 = ToRGBA5551(Raw[i + 2], Raw[i + 1], Raw[i], Raw[i + 3]); ushort RGBA5551_2 = ToRGBA5551(Raw[i + 6], Raw[i + 5], Raw[i + 4], Raw[i + 7]); byte Value = (byte)( ((GetPaletteIndex(Palette, RGBA5551_1)) << 4) | ((GetPaletteIndex(Palette, RGBA5551_2) & 0xF))); Data[j] = Value; } } else if (UniqueColors.Count <= 256 && Material.Width * Material.Height <= 2048 && Material.ForceRGBA == false) { #if DEBUG Console.WriteLine("CI 8-bit <- " + Material.Name + ", " + Material.Width.ToString() + "*" + Material.Height.ToString() + ", " + UniqueColors.Count.ToString() + " unique colors"); #endif /* Set type, CI 8-bit */ Format = GBI.G_IM_FMT_CI; Size = GBI.G_IM_SIZ_8b; /* Generate texture buffer */ Data = new byte[Material.Width * Material.Height]; /* Generate 256-color RGBA5551 palette */ Palette = GeneratePalette(UniqueColors, 256); /* Loop through pixels, get palette indexes, write to texture buffer */ for (int i = 0, j = 0; i < Raw.Length; i += 4, j++) { ushort RGBA5551 = ToRGBA5551(Raw[i + 2], Raw[i + 1], Raw[i], Raw[i + 3]); Data[j] = (byte)GetPaletteIndex(Palette, RGBA5551); } } else { /* Convert to RGBA */ #if DEBUG Console.WriteLine("RGBA 16-bit <- " + Material.Name + ", " + Material.Width.ToString() + "*" + Material.Height.ToString()); #endif /* Set type, RGBA 16-bit */ Format = GBI.G_IM_FMT_RGBA; Size = GBI.G_IM_SIZ_16b; /* Generate texture buffer */ Data = new byte[Material.Width * Material.Height * 2]; Palette = null; /* Loop through pixels, convert to RGBA5551, write to texture buffer */ for (int i = 0, j = 0; i < Raw.Length; i += 4, j += 2) { ushort RGBA5551 = ToRGBA5551(Raw[i + 2], Raw[i + 1], Raw[i], Raw[i + 3]); Data[j] = (byte)(RGBA5551 >> 8); Data[j + 1] = (byte)(RGBA5551 & 0xFF); } } } } catch (Exception ex) { System.Windows.Forms.MessageBox.Show("Material '" + Material.DisplayName + "': " + ex.Message, "Exception", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation); SetInvalidTexture(Material); } /* Pack texture type */ Type = (byte)((Format << 5) | (Size << 3)); }