/// <summary> /// Make texture for model. /// </summary> /// <param name="mod">NSBMD Model</param> private void MakeTexture(NsbmdModel mod) { Console.WriteLine("DEBUG: making texture for model '{0}'...", mod.Name); for (int i = 0; i < mod.Materials.Count; i++) { if (mod.Materials[i].format == 0) // format 0 is no texture continue; var mat = mod.Materials[i]; if (mat == null || mat.paldata == null) continue; int pixelnum = mat.width*mat.height; var image = new RGBA[pixelnum]; switch (mat.format) { // No Texture case 0: //puts( "ERROR: format 0" ); continue; break; // A3I5 Translucent Texture (3bit Alpha, 5bit Color Index) case 1: for (int j = 0; j < pixelnum; j++) { int index = mat.texdata[j] & 0x1f; int alpha = (mat.texdata[j] >> 5) & 7; alpha = ((alpha*4) + (alpha/2)) << 3; image[j] = mat.paldata[index]; image[j].A = (byte) alpha; } break; // 4-Color Palette Texture case 2: for (int j = 0; j < pixelnum; j++) { uint index = mat.texdata[j/4]; index = (index >> ((j%4) << 1)) & 3; image[j] = mat.paldata[index]; } break; // 16-Color Palette Texture case 3: if (mat.color0 != 0) mat.paldata[0] = RGBA.Transparent; // made palette entry 0 transparent for (int j = 0; j < pixelnum; j++) { var matindex = j/2; if (mat.texdata.Length < matindex) continue; int index = mat.texdata[matindex]; index = (index >> ((j%2) << 2)) & 0x0f; if (mat.paldata == null) continue; if (index < 0 || index >= mat.paldata.Length) continue; if (j < 0 || j >= pixelnum) continue; image[j] = mat.paldata[index]; } break; // 256-Color Palette Texture case 4: if (mat.color0 != 0) mat.paldata[0] = RGBA.Transparent; // made palette entry 0 transparent // made palette entry 0 transparent for (int j = 0; j < pixelnum; j++) { image[j] = mat.paldata[mat.texdata[j]]; } break; // 4x4-Texel Compressed Texture case 5: convert_4x4texel_b(mat.texdata, mat.width, mat.height, mat.spdata, mat.paldata, image); break; // A5I3 Translucent Texture (5bit Alpha, 3bit Color Index) case 6: for (int j = 0; j < pixelnum; j++) { int index = mat.texdata[j] & 0x7; int alpha = (mat.texdata[j] >> 3) & 0x1f; alpha = ((alpha*4) + (alpha/2)) << 3; image[j] = mat.paldata[index]; image[j].A = (byte) alpha; } break; // Direct Color Texture case 7: for (int j = 0; j < pixelnum; j++) { UInt16 p = (ushort) (mat.texdata[j*2] + (mat.texdata[j*2 + 1] << 8)); image[j].R = (byte) (((p >> 0) & 0x1f) << 3); image[j].G = (byte) (((p >> 5) & 0x1f) << 3); image[j].B = (byte) (((p >> 10) & 0x1f) << 3); image[j].A = (byte) (((p & 0x8000) != 0) ? 0xff : 0); } break; } ///////////////////////////////////////////////////// // The trick to handle texture repetition in OpenGL // Flip is not supported in Win32 version of OpenGL // We have to manually resize the texture, and apply mirror/flip effect if (mat.repeat == 0x07) { // repeat in s & t direction, flip in s direction // double the width, add a mirror image along the right edge of the texture (s direction) var newimage = new RGBA[pixelnum*2]; int newwidth = mat.width*2; int newwidth_1 = newwidth - 1; for (int y = 0; y < mat.height; y++) { int tbase = y*mat.width; // base in original texture int newbase = y*newwidth; // base in new texture for (int x = 0; x < mat.width; x++) { var pixel = image[tbase + x]; newimage[newbase + x] = pixel; newimage[newbase + newwidth_1 - x] = pixel; } } mat.width = newwidth; image = newimage; } else if (mat.repeat == 0x0b) { // repeat in s & t direction, flip in t direction // double the height, add a mirror image along the bottom edge of the texture (t direction) var newimage = new RGBA[pixelnum*2]; int newheight = mat.height*2; int newheight_1 = mat.height - 1; for (int y = 0; y < mat.height; y++) { int tbase = y*mat.width; int newbase = (newheight_1 - y)*mat.width; for (int x = 0; x < mat.width; x++) newimage[newbase + x] = image[tbase + x]; } mat.height = newheight; image = newimage; } else if (mat.repeat == 0x0f) { // repeat in s & t direction, flip in s & t direction // double both width and height, add mirror images along both right and bottom edges var newimage = new RGBA[pixelnum*4]; int newwidth = mat.width*2; int newwidth_1 = newwidth - 1; int newheight = mat.height*2; int newheight_1 = newheight - 1; for (int y = 0; y < mat.height; y++) { int tbase = y*mat.width; // base in original texture int topbase = y*newwidth; // top base in new texture int bottombase = (newheight_1 - y)*newwidth; // bottom base in new texture for (int x = 0; x < mat.width; x++) { var pixel = image[tbase + x]; newimage[topbase + x] = pixel; newimage[topbase + newwidth_1 - x] = pixel; newimage[bottombase + x] = pixel; newimage[bottombase + newwidth_1 - x] = pixel; } } mat.width = newwidth; mat.height = newheight; image = newimage; } Console.WriteLine("convert matid = {0}", i); Console.WriteLine("\ttex '{0}': {1} [{2},{3}] texsize = {4}", mat.texname, TEXTURE_FORMATS[mat.format], mat.width, mat.height, mat.texsize); Console.WriteLine("\tpal '{0}': pixelnum = {1}, repeat = {2}", mat.palname, pixelnum, mat.repeat); var imageBytesList = new List<byte>(); for (int k = 0; k < image.Length; ++k) { imageBytesList.Add(image[k].R); imageBytesList.Add(image[k].G); imageBytesList.Add(image[k].B); imageBytesList.Add(image[k].A); } var imageBytes = imageBytesList.ToArray(); Gl.glBindTexture(Gl.GL_TEXTURE_2D, i + 1); Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, mat.width, mat.height, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, imageBytes); Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST); Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST); } }
/// <summary> /// Load materials in stream. /// </summary> /// <param name="stream">Stream to use.</param> /// <returns>Material definitions.</returns> public static IEnumerable<NsbmdMaterial> ReadTex0(Stream stream, int blockoffset, out int ptexnum, out int ppalnum, out List<NSBMDTexture> texs, out List<NSBMDPalette> pals) { EndianBinaryReader reader = new EndianBinaryReader(stream, Endianness.LittleEndian); UInt32 blocksize, blockptr, blocklimit; int texnum; UInt32 texdataoffset; int texdatasize; UInt32 sptexoffset; // for 4x4 compressed texels only int sptexsize; // for 4x4 compressed texels only UInt32 spdataoffset; // for 4x4 compressed texels only int palnum; UInt32 paldefoffset; UInt32 paldataoffset; int paldatasize; NsbmdMaterial[] material = null; int i, j; texs = new List<NSBMDTexture>(); pals = new List<NSBMDPalette>(); blockptr = (uint)(blockoffset + 4); // already read the block ID, so skip 4 bytes blocksize = reader.ReadUInt32(); // block size blocklimit = (uint)(blocksize + blockoffset); Console.WriteLine("DEBUG: blockoffset = {0}, blocksize = {1}", blockoffset, blocksize); stream.Skip(4); // skip 4 padding 0s texdatasize = reader.ReadUInt16() << 3; // total texture data size div8 stream.Skip(6); // skip 6 bytes texdataoffset = (uint)(reader.ReadUInt32() + blockoffset); stream.Skip(4); // skip 4 padding 0s sptexsize = reader.ReadUInt16() << 3; // for format 5 4x4-texel, data size div8 stream.Skip(6); // skip 6 bytesmhm sptexoffset = (uint)(reader.ReadUInt32() + blockoffset); // for format 5 4x4-texel, data offset spdataoffset = (uint)(reader.ReadUInt32() + blockoffset); // for format 5 4x4-texel, palette info stream.Skip(4); // skip 4 bytes paldatasize = reader.ReadUInt16() << 3; // total palette data size div8 stream.Skip(2); // skip 2 bytes paldefoffset = (uint)(reader.ReadUInt32() + blockoffset); paldataoffset = (uint)(reader.ReadUInt32() + blockoffset); // printf( "texdataoffset = %08x texdatasize = %08x\n", texdataoffset, texdatasize ); // printf( "sptexoffset = %08x sptexsize = %08x spdataoffset = %08x\n", sptexoffset, sptexsize, spdataoffset ); // printf( "paldataoffset = %08x paldatasize = %08x\n", paldataoffset, paldatasize ); //////////////////////////////////////////////// // texture definition stream.Skip(1); // skip dummy '0' texnum = reader.ReadByte(); // no of texture blockptr = (uint)stream.Position; stream.Seek(paldefoffset, SeekOrigin.Begin); stream.Skip(1); // skip dummy '0' palnum = reader.ReadByte(); // no of palette stream.Seek(blockptr, SeekOrigin.Begin); Console.WriteLine("texnum = {0}, palnum = {1}", texnum, palnum); // allocate memory for material, great enough to hold all texture and palette material = new NsbmdMaterial[(texnum > palnum ? texnum : palnum)]; for (i = 0; i < material.Length; i++) material[i] = new NsbmdMaterial(); stream.Skip(14 + (texnum * 4)); // go straight to texture info for (i = 0; i < texnum; i++) { UInt32 offset; int param; int format; int width; int height; var mat = material[i]; offset = (uint)(reader.ReadUInt16() << 3); param = reader.ReadUInt16(); // texture parameter stream.Skip(4); // skip 4 bytes format = (param >> 10) & 7; // format 0..7, see DSTek width = 8 << ((param >> 4) & 7); height = 8 << ((param >> 7) & 7); mat.color0 = (param >> 13) & 1; if (format == 5) mat.texoffset = offset + sptexoffset; // 4x4-Texel Compressed Texture else mat.texoffset = offset + texdataoffset; mat.format = format; mat.width = width; mat.height = height; NSBMDTexture t = new NSBMDTexture(); t.format = format; t.width = width; t.height = height; t.color0 = (param >> 13) & 1; texs.Add(t); } //////////////////////////////////////////// // copy texture names for (i = 0; i < texnum; i++) { material[i].texname = Utils.ReadNSBMDString(reader); reader.BaseStream.Position -= 16; texs[i].texname = Utils.ReadNSBMDString(reader); } //////////////////////////////////////////////// // calculate each texture's size for (i = 0; i < texnum; i++) { int[] bpp = { 0, 8, 2, 4, 8, 2, 8, 16 }; var mat = material[i]; mat.texsize = (uint)(mat.width * mat.height * bpp[mat.format] / 8); Console.WriteLine("tex {0} '{1}': offset = {2} size = {3} [W,H] = [{4}, {5}]", i, mat.texname, mat.texoffset, mat.texsize, mat.width, mat.height); texs[i].texsize = (uint)(mat.width * mat.height * bpp[mat.format] / 8); } //////////////////////////////////////////////// // palette definition stream.Seek(paldefoffset + 2, SeekOrigin.Begin); // skip palnum, already read stream.Seek(14 + (palnum * 4), SeekOrigin.Current); // go straight to palette info for (i = 0; i < palnum; i++) { uint curOffset = (uint)((reader.ReadUInt16() << 3) + paldataoffset); stream.Seek(2, SeekOrigin.Current); // skip 2 bytes material[i].paloffset = curOffset; NSBMDPalette t = new NSBMDPalette(); t.paloffset = curOffset; pals.Add(t); } //////////////////////////////////////////////// // copy palette names for (i = 0; i < palnum; i++) { var mat = material[i]; mat.palname = Utils.ReadNSBMDString(reader); reader.BaseStream.Position -= 16; pals[i].palname = Utils.ReadNSBMDString(reader); } //////////////////////////////////////////////// // calculate each palette's size // assume the palettes are stored sequentially /*for (i = 0; i < palnum - 1; i++) { int r; var mat = material[i]; r = i; try { while (material[r].paloffset == mat.paloffset) r++; } catch { } // below is RotA stupid way to calculate the size of palette: next's offset - current's offset // it works most of the time if (r != palnum) { mat.palsize = material[r].paloffset - mat.paloffset; pals[i].palsize = material[r].paloffset - mat.paloffset; } else { mat.palsize = blocklimit - mat.paloffset; pals[i].palsize = blocklimit - mat.paloffset; } //printf("pal '%s' size = %d\n", mat->palname, mat->palsize); } material[i].palsize = blocklimit - material[i].paloffset; pals[i].palsize = blocklimit - material[i].paloffset;*/ List<int> offsets = new List<int>(); for (int k = 0; k < pals.Count; k++) { if (!offsets.Contains((int)pals[k].paloffset)) { offsets.Add((int)pals[k].paloffset); } } offsets.Add((int)blocklimit); offsets.Sort(); for (int k = 0; k < pals.Count; k++) { int pallength; int l = -1; do { l++; } while (offsets[l] - pals[k].paloffset <= 0);//nsbtx.PalInfo.infoBlock.PalInfo[i + j].Palette_Offset - nsbtx.PalInfo.infoBlock.PalInfo[i].Palette_Offset == 0) pallength = offsets[l] - (int)pals[k].paloffset; //RGBA[] c_ = pals[k].paldata; //List<RGBA> c = new List<RGBA>(); //c.AddRange(pals[k].paldata.Take(pallength / 2)); //pals[k].paldata = c.ToArray(); pals[k].palsize = (uint)pallength; material[k].palsize = (uint)pallength; } //////////////////////////////////////////////// // traverse each texture for (i = 0; i < texnum; i++) { var mat = material[i]; stream.Seek(mat.texoffset, SeekOrigin.Begin); //////////////////////////////////////////////// // read texture into memory byte[] by = reader.ReadBytes((int)mat.texsize); mat.texdata = by; texs[i].texdata = by; Console.WriteLine("DEBUG: texoffset = {0}, texsize = {1}", mat.texoffset, mat.texsize); //////////////////////////////////////////////// // additional data for format 5 4x4 compressed texels if (mat.format == 5) { UInt32 r = mat.texsize / 2;//>> 1; stream.Seek(spdataoffset + (mat.texoffset - sptexoffset) / 2, SeekOrigin.Begin); by = reader.ReadBytes((int)r); mat.spdata = by; texs[i].spdata = by; Console.WriteLine("DEBUG: 4x4-texel spdataoffset = {0}, spdatasize = {1}", spdataoffset, r); //spdataoffset += r; } } //////////////////////////////////////////////// // traverse each palette for (i = 0; i < palnum; i++) { try { NsbmdMaterial mat = material[i]; var palentry = mat.palsize >> 1; RGBA[] rgbq = new RGBA[palentry]; Console.WriteLine("DEBUG: converting pal '{0}', palentry = {1}", mat.palname, palentry); stream.Seek(mat.paloffset, SeekOrigin.Begin); for (j = 0; j < palentry; j++) { UInt16 p = reader.ReadUInt16(); rgbq[j].R = (byte)(((p >> 0) & 0x1f) << 3); // red rgbq[j].G = (byte)(((p >> 5) & 0x1f) << 3); // green rgbq[j].B = (byte)(((p >> 10) & 0x1f) << 3); // blue //rgbq[j].RotA = (p&0x8000) ? 0xff : 0; rgbq[j].A = (p & 0x8000) == 0 ? (byte)0xff : (byte)0;//0xff; // alpha } mat.paldata = rgbq; pals[i].paldata = rgbq; } catch { } } ptexnum = texnum; ppalnum = palnum; return material; }
/// <summary> /// Convert texel (wrapper for type safety issues). /// </summary> private void convert_4x4texel_b(byte[] tex, int width, int height, byte[] data, RGBA[] pal, RGBA[] rgbaOut) { var list1 = new List<uint>(); for (int i = 0; i < (tex.Length + 1)/4; ++i) list1.Add(Utils.Read4BytesAsUInt32(tex, i*4)); var list2 = new List<UInt16>(); for (int i = 0; i < (data.Length + 1)/2; ++i) list2.Add(Utils.Read2BytesAsUInt16(data, i*2)); var b = convert_4x4texel(list1.ToArray(), width, height, list2.ToArray(), pal, rgbaOut); }
//�Private�Methods�(7) /// <summary> /// Convert texel. /// </summary> private bool convert_4x4texel(uint[] tex, int width, int height, UInt16[] data, RGBA[] pal, RGBA[] rgbaOut) { int w = width/4; int h = height/4; // traverse 'w x h blocks' of 4x4-texel for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { int index = y*w + x; UInt32 t = tex[index]; UInt16 d = data[index]; UInt16 addr = (ushort) (d & 0x3fff); UInt16 mode = (ushort) ((d >> 14) & 3); // traverse every texel in the 4x4 texels for (int r = 0; r < 4; r++) for (int c = 0; c < 4; c++) { int texel = (int) ((t >> ((r*4 + c)*2)) & 3); RGBA pixel = rgbaOut[(y*4 + r)*width + (x*4 + c)]; switch (mode) { case 0: pixel = pal[(addr << 1) + texel]; if (texel == 3) pixel = RGBA.Transparent; // make it transparent, alpha = 0 break; case 2: pixel = pal[(addr << 1) + texel]; break; case 1: switch (texel) { case 0: case 1: pixel = pal[(addr << 1) + texel]; break; case 2: pixel.R = (byte) ((pal[(addr << 1)].R + pal[(addr << 1) + 1].R)/2L); pixel.G = (byte) ((pal[(addr << 1)].G + pal[(addr << 1) + 1].G)/2L); pixel.B = (byte) ((pal[(addr << 1)].B + pal[(addr << 1) + 1].B)/2L); pixel.A = 0xff; break; case 3: pixel = RGBA.Transparent; // make it transparent, alpha = 0 break; } break; case 3: switch (texel) { case 0: case 1: pixel = pal[(addr << 1) + texel]; break; case 2: pixel.R = (byte) ((pal[(addr << 1)].R*5L + pal[(addr << 1) + 1].R*3L)/8); pixel.G = (byte) ((pal[(addr << 1)].G*5L + pal[(addr << 1) + 1].G*3L)/8); pixel.B = (byte) ((pal[(addr << 1)].B*5L + pal[(addr << 1) + 1].B*3L)/8); pixel.A = 0xff; break; case 3: pixel.R = (byte) ((pal[(addr << 1)].R*3L + pal[(addr << 1) + 1].R*5L)/8); pixel.G = (byte) ((pal[(addr << 1)].G*3L + pal[(addr << 1) + 1].G*5L)/8); pixel.B = (byte) ((pal[(addr << 1)].B*3L + pal[(addr << 1) + 1].B*5L)/8); pixel.A = 0xff; break; } break; } } } return true; }
public static RGBA fromColor(System.Drawing.Color c) { RGBA a = new RGBA(); a.R = c.R; a.G = c.G; a.B = c.B; a.A = c.A; return a; }
/// <summary> /// Make texture for model. /// </summary> /// <param name="mod">NSBMD Model</param> private void MakeTexture(int i, NsbmdMaterial m) { try { if (m.format == 0) // format 0 is no texture { //matt.Add(new System.Windows.Media.ImageBrush()); return; } var mat = m; if (mat == null || (mat.paldata == null && mat.format != 7)) { //matt.Add(new System.Windows.Media.ImageBrush()); return; } int pixelnum = mat.width * mat.height; var image = new RGBA[pixelnum]; switch (mat.format) { // No Texture case 0: //puts( "ERROR: format 0" ); //mattt.Add(new System.Windows.Media.ImageBrush()); return; // A3I5 Translucent Texture (3bit Alpha, 5bit Color Index) case 1: for (int j = 0; j < pixelnum; j++) { int index = mat.texdata[j] & 0x1f; int alpha = (mat.texdata[j] >> 5);// & 7; alpha = ((alpha * 4) + (alpha / 2)) * 8;// << 3; image[j] = mat.paldata[index]; image[j].A = (byte)alpha; } break; // 4-Color Palette Texture case 2: if (mat.color0 != 0) mat.paldata[0] = RGBA.Transparent; // made palette entry 0 transparent for (int j = 0; j < pixelnum; j++) { uint index = mat.texdata[j / 4]; index = (index >> ((j % 4) << 1)) & 3; image[j] = mat.paldata[index]; } break; // 16-Color Palette Texture case 3: if (mat.color0 != 0) mat.paldata[0] = RGBA.Transparent; // made palette entry 0 transparent for (int j = 0; j < pixelnum; j++) { var matindex = j / 2; if (mat.texdata.Length < matindex) continue; int index = mat.texdata[matindex]; index = (index >> ((j % 2) << 2)) & 0x0f; if (mat.paldata == null) continue; if (index < 0 || index >= mat.paldata.Length) continue; if (j < 0 || j >= pixelnum) continue; image[j] = mat.paldata[index]; } break; // 256-Color Palette Texture case 4: if (mat.color0 != 0) mat.paldata[0] = RGBA.Transparent; // made palette entry 0 transparent // made palette entry 0 transparent for (int j = 0; j < pixelnum; j++) { image[j] = mat.paldata[mat.texdata[j]]; } break; // 4x4-Texel Compressed Texture case 5: convert_4x4texel_b(mat.texdata, mat.width, mat.height, mat.spdata, mat.paldata, image); break; // A5I3 Translucent Texture (5bit Alpha, 3bit Color Index) case 6: for (int j = 0; j < pixelnum; j++) { int index = mat.texdata[j] & 0x7; int alpha = (mat.texdata[j] >> 3); alpha *= 8; //((alpha * 4) + (alpha / 2)) << 3; image[j] = mat.paldata[index]; image[j].A = (byte)alpha; } break; // Direct Color Texture case 7: for (int j = 0; j < pixelnum; j++) { //UInt16 p = (ushort)(mat.texdata[j * 2] + (mat.texdata[j * 2 + 1] << 8)); //image[j].R = (byte)(((p >> 0) & 0x1f) << 3); //image[j].G = (byte)(((p >> 5) & 0x1f) << 3); //image[j].B = (byte)(((p >> 10) & 0x1f) << 3); //image[j].A = (byte)(((p & 0x8000) != 0) ? 0xff : 0); image[j] = RGBA.fromColor(Tinke.Convertir.BGR555(mat.texdata[j * 2], mat.texdata[j * 2 + 1])); UInt16 p = (ushort)(mat.texdata[j * 2] + (mat.texdata[j * 2 + 1] << 8)); image[j].A = (byte)(((p & 0x8000) != 0) ? 0xff : 0); } break; } Console.WriteLine("convert matid = {0}", i); Console.WriteLine("\ttex '{0}': {1} [{2},{3}] texsize = {4}", mat.texname, TEXTURE_FORMATS[mat.format], mat.width, mat.height, mat.texsize); Console.WriteLine("\tpal '{0}': pixelnum = {1}, repeat = {2}", mat.palname, pixelnum, mat.repeat); var imageBytesList = new List<byte>(); //System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(mat.width, mat.height); for (int k = 0; k < image.Length; ++k) { //bitmap.SetPixel(k - ((k / (mat.width)) * (mat.width)), k / (mat.width), image[k].ToColor()); imageBytesList.Add(image[k].R); imageBytesList.Add(image[k].G); imageBytesList.Add(image[k].B); //if (image[k].A != 0) //{ // imageBytesList.Add((byte)(image[k].A - (255 - (((mat.Alpha + 1) * 8) - 1)))); //} //else //{ imageBytesList.Add((byte)(image[k].A)); //} } var imageBytes = imageBytesList.ToArray(); /*System.Windows.Media.ImageBrush br = new System.Windows.Media.ImageBrush(CreateBitmapSourceFromBitmap(bitmap)); br.Viewbox = new System.Windows.Rect(0, 0, mat.width, mat.height); br.ViewboxUnits = System.Windows.Media.BrushMappingMode.Absolute; br.Viewport = new System.Windows.Rect(0, 0, 1, 1); br.ViewportUnits = System.Windows.Media.BrushMappingMode.Absolute; br.Stretch = System.Windows.Media.Stretch.None; if (mat.flipS == 1 && mat.flipT == 1) { br.TileMode = System.Windows.Media.TileMode.FlipXY; } else if (mat.flipS == 1) { br.TileMode = System.Windows.Media.TileMode.FlipX; } else if (mat.flipT == 1) { br.TileMode = System.Windows.Media.TileMode.FlipY; } else if (mat.repeatS == 1 || mat.repeatT == 1) { br.TileMode = System.Windows.Media.TileMode.Tile; } else { br.TileMode = System.Windows.Media.TileMode.None; } br.Opacity = (double)(((mat.Alpha + 1) * 8) - 1) / 1.0d; matt.Add(br);*/ //ttt Gl.glBindTexture(Gl.GL_TEXTURE_2D, i + 1 + matstart); Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, mat.width, mat.height, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, imageBytes); Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST); Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST); if (mat.flipS == 1 && mat.repeatS == 1) { Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_MIRRORED_REPEAT); } else if (mat.repeatS == 1) { Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT); } else { Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP); } if (mat.flipT == 1 && mat.repeatT == 1) { Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_MIRRORED_REPEAT); } else if (mat.repeatT == 1) { Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT); } else { Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP); } } catch { //matt.Add(new System.Windows.Media.ImageBrush()); } }
/// <summary> /// Load materials in stream. /// </summary> /// <param name="stream">Stream to use.</param> /// <returns>Material definitions.</returns> public static IEnumerable<NsbmdMaterial> ReadTex0(Stream stream, int blockoffset, out int ptexnum, out int ppalnum) { BinaryReader reader = new BinaryReader(stream); UInt32 blocksize, blockptr, blocklimit; int texnum; UInt32 texdataoffset; int texdatasize; UInt32 sptexoffset; // for 4x4 compressed texels only int sptexsize; // for 4x4 compressed texels only UInt32 spdataoffset; // for 4x4 compressed texels only int palnum; UInt32 paldefoffset; UInt32 paldataoffset; int paldatasize; NsbmdMaterial[] material = null; int i, j; blockptr = (uint) (blockoffset + 4); // already read the block ID, so skip 4 bytes blocksize = reader.ReadUInt32(); // block size blocklimit = (uint) (blocksize + blockoffset); Console.WriteLine("DEBUG: blockoffset = {0}, blocksize = {1}", blockoffset, blocksize); stream.Skip(4); // skip 4 padding 0s texdatasize = reader.ReadUInt16() << 3; // total texture data size div8 stream.Skip(6); // skip 6 bytes texdataoffset = (uint) (reader.ReadUInt32() + blockoffset); stream.Skip(4); // skip 4 padding 0s sptexsize = reader.ReadUInt16() << 3; // for format 5 4x4-texel, data size div8 stream.Skip(6); // skip 6 bytesmhm sptexoffset = (uint) (reader.ReadUInt32() + blockoffset); // for format 5 4x4-texel, data offset spdataoffset = (uint) (reader.ReadUInt32() + blockoffset); // for format 5 4x4-texel, palette info stream.Skip(4); // skip 4 bytes paldatasize = reader.ReadUInt16() << 3; // total palette data size div8 stream.Skip(2); // skip 2 bytes paldefoffset = (uint) (reader.ReadUInt32() + blockoffset); paldataoffset = (uint) (reader.ReadUInt32() + blockoffset); // printf( "texdataoffset = %08x texdatasize = %08x\n", texdataoffset, texdatasize ); // printf( "sptexoffset = %08x sptexsize = %08x spdataoffset = %08x\n", sptexoffset, sptexsize, spdataoffset ); // printf( "paldataoffset = %08x paldatasize = %08x\n", paldataoffset, paldatasize ); //////////////////////////////////////////////// // texture definition stream.Skip(1); // skip dummy '0' texnum = reader.ReadByte(); // no of texture blockptr = (uint) stream.Position; stream.Seek(paldefoffset, SeekOrigin.Begin); stream.Skip(1); // skip dummy '0' palnum = reader.ReadByte(); // no of palette stream.Seek(blockptr, SeekOrigin.Begin); Console.WriteLine("texnum = {0}, palnum = {1}", texnum, palnum); // allocate memory for material, great enough to hold all texture and palette material = new NsbmdMaterial[(texnum > palnum ? texnum : palnum)]; for (i = 0; i < material.Length; ++i) material[i] = new NsbmdMaterial(); stream.Skip(14 + (texnum*4)); // go straight to texture info for (i = 0; i < texnum; i++) { UInt32 offset; int param; int format; int width; int height; var mat = material[i]; offset = (uint) (reader.ReadUInt16() << 3); param = reader.ReadUInt16(); // texture parameter stream.Skip(4); // skip 4 bytes format = (param >> 10) & 7; // format 0..7, see DSTek width = 8 << ((param >> 4) & 7); height = 8 << ((param >> 7) & 7); mat.color0 = (param >> 13) & 1; if (format == 5) mat.texoffset = offset + sptexoffset; // 4x4-Texel Compressed Texture else mat.texoffset = offset + texdataoffset; mat.format = format; mat.width = width; mat.height = height; } //////////////////////////////////////////// // copy texture names for (i = 0; i < texnum; i++) { material[i].texname = Utils.ReadNSBMDString(reader); } //////////////////////////////////////////////// // calculate each texture's size for (i = 0; i < texnum; i++) { int[] bpp = {0, 8, 2, 4, 8, 2, 8, 16}; var mat = material[i]; mat.texsize = (uint) (mat.width*mat.height*bpp[mat.format]/8); Console.WriteLine("tex {0} '{1}': offset = {2} size = {3} [W,H] = [{4}, {5}]", i, mat.texname, mat.texoffset, mat.texsize, mat.width, mat.height); } //////////////////////////////////////////////// // palette definition stream.Seek(paldefoffset + 2, SeekOrigin.Begin); // skip palnum, already read stream.Seek(14 + (palnum*4), SeekOrigin.Current); // go straight to palette info for (i = 0; i < palnum; i++) { uint curOffset = (uint) ((reader.ReadUInt16() << 3) + paldataoffset); stream.Seek(2, SeekOrigin.Current); // skip 2 bytes material[i].paloffset = curOffset; } //////////////////////////////////////////////// // copy palette names for (i = 0; i < palnum; i++) { var mat = material[i]; mat.palname = Utils.ReadNSBMDString(reader); } //////////////////////////////////////////////// // calculate each palette's size // assume the palettes are stored sequentially for (i = 0; i < palnum - 1; i++) { var mat = material[i]; var r = i + 1; while (material[r].paloffset == mat.paloffset) r++; // below is RotA stupid way to calculate the size of palette: next's offset - current's offset // it works most of the time if (r != palnum) mat.palsize = material[r].paloffset - mat.paloffset; else mat.palsize = blocklimit - mat.paloffset; //printf("pal '%s' size = %d\n", mat->palname, mat->palsize); } material[i].palsize = blocklimit - material[i].paloffset; //////////////////////////////////////////////// // traverse each texture for (i = 0; i < texnum; i++) { var mat = material[i]; stream.Seek(mat.texoffset, SeekOrigin.Begin); //////////////////////////////////////////////// // read texture into memory mat.texdata = reader.ReadBytes((int) mat.texsize); Console.WriteLine("DEBUG: texoffset = {0}, texsize = {1}", mat.texoffset, mat.texsize); //////////////////////////////////////////////// // additional data for format 5 4x4 compressed texels if (true) //(mat.format == 5) { UInt32 r = mat.texsize >> 1; stream.Seek(spdataoffset, SeekOrigin.Begin); mat.spdata = reader.ReadBytes((int) r); Console.WriteLine("DEBUG: 4x4-texel spdataoffset = {0}, spdatasize = {1}", spdataoffset, r); spdataoffset += r; } } //////////////////////////////////////////////// // traverse each palette for (i = 0; i < palnum; i++) { try { NsbmdMaterial mat = material[i]; var palentry = mat.palsize >> 1; RGBA[] rgbq = new RGBA[palentry]; Console.WriteLine("DEBUG: converting pal '{0}', palentry = {1}", mat.palname, palentry); stream.Seek(mat.paloffset, SeekOrigin.Begin); for (j = 0; j < palentry; j++) { UInt16 p = reader.ReadUInt16(); rgbq[j].R = (byte) (((p >> 0) & 0x1f) << 3); // red rgbq[j].G = (byte) (((p >> 5) & 0x1f) << 3); // green rgbq[j].B = (byte) (((p >> 10) & 0x1f) << 3); // blue //rgbq[j].RotA = (p&0x8000) ? 0xff : 0; rgbq[j].A = 0xff; // alpha } mat.paldata = rgbq; } catch { } } ptexnum = texnum; ppalnum = palnum; return material; }