Шейдеры вершин и фрагментов с картой-кубом, хранящей образ географической карты Земли, употребляются для воспроизведения глобуса – карты земли на сфере (рис. 1).
Рис. 1. Глобус
Для получения результата использованы C#, OpenGL библиотеки OpenTK.
Приводимый пример – это слегка модифицированный код примера DDS Cube Map, имеющийся в демонстрационном пакете OpenTK.
На основе образов образов файла earthCubemap.dds (DDS – Direct Draw surface file) создается текстура TMU0_Unitt, для наложения которой используются GLSL-шейдеры вершин и фрагментов, входящие в состав OpenTK (GLSL – Graphics Library Shader Language).
Используется консоль-проект, в пространство имен которого добавляются недостающие ссылки (Solution Explorre – References – правая кнопка мыши – Add Reference – Browse):
using System;
using OpenTK; // WindowState , Exit() and so on
using OpenTK.Graphics.OpenGL;
Описываемые далее файл образов и файлы шейдеров могут быть получены по следующей ссылке: скачать dds- и glsl-файлы
пространство имен TextureLoaders, поддержиивающее загрузку текстур, такое же, что в примере Шейдер с иллюзией выступов и впадин
Создание полигональной модели сферы (рис. 2) обеспечивается пространством имен Shapes.
Рис. 2. Полигональная модель сферы
Файл earthCubeMap.dds содержит 6 образов, в совокупности представляющих географическую карту Земли (рис. 2).
Рис. 3. Образы файла earthCubeMap.dds – куски карты Земли
Так же на рис. 3 показаны свойства файла earthCubeMap.dds, одинаковые для всех образов.
Код шейдеров вершин и фрагментов загружается соответственно из файлов CubeMap_VS.glsl и CubeMap_FS.glsl.
Код шейдера вершин:
// Copyright (c) 2008 the OpenTK Team
// MUST be written to for FS
varying vec3 Normal;
void main()
{
gl_Position = ftransform();
Normal = /*gl_NormalMatrix * */ gl_Normal;
}
Код шейдера фрагментов:
// Copyright (c) 2008 the OpenTK Team
uniform samplerCube Earth;
varying vec3 Normal;
void main()
{
gl_FragColor = textureCube(Earth, Normal.xyz);
}
Шейдер фрагментов получает от шейдера вершин вектор нормали и, оперируя данными dds-файла, вычисляет цвет текущего пикселя (gl_FragColor).
В приводимой ниже программе изображение управляется движениями мыши (см. обработчики OnUpdateFrame и OnRenderFrame).
Создаваемая текстура с картой Земли основана на образах карты-куба:
Код приложения:
// Based on code from the OpenTK library
using System;
using System.Collections.Generic; // List
using System.IO;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using Shapes;
using TextureLoaders;
namespace earthCubemap
{
public class T13_GLSL_Earth: GameWindow
{
public T13_GLSL_Earth() : base(600, 600)
{
}
// Шейдер
int VertexShaderObject, FragmentShaderObject, ProgramObject;
const string VertexShaderFilename = "G:/AM/CubeMap_VS.glsl";
const string FragmentShaderFilename = "G:/AM/CubeMap_FS.glsl";
// Текстура
const TextureUnit TMU0_Unit = TextureUnit.Texture0;
const int TMU0_UnitInteger = 0;
const string TMU0_Filename = "G:/AM/earthCubemap.dds";
uint TMU0_Handle;
TextureTarget TMU0_Target;
// DL
DrawableShape sphere;
// Камера
Vector3 EyePos = new Vector3(0.0f, 0.0f, 6.0f);
Vector3 Trackball = Vector3.Zero;
protected override void OnLoad(EventArgs e)
{
this.VSync = VSyncMode.Off;
// Проверки возможностей текущей версии OpenGL
string extensions = GL.GetString(StringName.Extensions);
if (!GL.GetString(StringName.Extensions).Contains("GL_ARB_shading_language"))
throw new NotSupportedException(String.Format("Пример тербует OpenGL версии 2.0. Найдена {0}. Отказ.",
GL.GetString(StringName.Version).Substring(0, 3)));
if (!extensions.Contains("GL_ARB_texture_compression") || !extensions.Contains("GL_EXT_texture_compression_s3tc"))
throw new NotSupportedException("Пример требует поддержки сжатия текстур. Отказ.");
GL.ClearColor(0.5f, 0.5f, 0.5f, 0f); // Серый цвет
GL.Disable(EnableCap.Dither);
GL.Enable(EnableCap.CullFace);
GL.FrontFace(FrontFaceDirection.Ccw); // Лицевые грани обходятся против часовой стрелки
GL.PolygonMode(MaterialFace.Front, PolygonMode.Fill);
string LogInfo;
// Загрузка и компиляция шейдера вершин
using (StreamReader sr = new StreamReader(VertexShaderFilename))
{
VertexShaderObject = GL.CreateShader(ShaderType.VertexShader);
GL.ShaderSource(VertexShaderObject, sr.ReadToEnd());
GL.CompileShader(VertexShaderObject);
}
GL.GetShaderInfoLog(VertexShaderObject, out LogInfo);
if (LogInfo.Length > 0 && !LogInfo.Contains("hardware"))
Console.WriteLine("Ошибка компиляции шейдера вершин\nLog:\n" + LogInfo);
else
Console.WriteLine("Компиляция шейдера вершин завершена успешно");
// Загрузка и компиляция шейдера фрагментов
using (StreamReader sr = new StreamReader(FragmentShaderFilename))
{
FragmentShaderObject = GL.CreateShader(ShaderType.FragmentShader);
GL.ShaderSource(FragmentShaderObject, sr.ReadToEnd());
GL.CompileShader(FragmentShaderObject);
}
GL.GetShaderInfoLog(FragmentShaderObject, out LogInfo);
if (LogInfo.Length > 0 && !LogInfo.Contains("hardware"))
Console.WriteLine("Ошибка компиляции шейдера фрагментов\nLog:\n" + LogInfo);
else
Console.WriteLine("Компиляция шейдера фрагментов завершена успешно");
// Связываем шейдеры с рабочей программой
ProgramObject = GL.CreateProgram();
GL.AttachShader(ProgramObject, VertexShaderObject);
GL.AttachShader(ProgramObject, FragmentShaderObject);
// Связываем все вместе
GL.LinkProgram(ProgramObject);
// Удаляем ранее созданные шейдеры
GL.DeleteShader(VertexShaderObject);
GL.DeleteShader(FragmentShaderObject);
int[] temp = new int[1];
GL.GetProgram(ProgramObject, GetProgramParameterName.LinkStatus, out temp[0]);
Console.WriteLine("Связывание программ (" + ProgramObject + ") " + ((temp[0] == 1) ? "выполнено." : "НЕ ВЫПОЛНЕНО"));
if (temp[0] != 1) // В случае неудачи при связывании
{
GL.GetProgramInfoLog(ProgramObject, out LogInfo);
Console.WriteLine("Program Log:\n" + LogInfo);
}
GL.GetProgram(ProgramObject, GetProgramParameterName.ActiveAttributes, out temp[0]);
Console.WriteLine("Зарегестрировано " + temp[0] + " атрибута. (Должно быть 4: Pos, UV, Normal, Tangent)");
Console.WriteLine("Положение атрибута AttributeTangent: " + GL.GetAttribLocation(ProgramObject, "AttributeTangent"));
Console.WriteLine("Создание шейдера завершено. GL-ошибка: " + GL.GetError());
ImageDDS.LoadFromDisk(TMU0_Filename, false, false, out TMU0_Handle, out TMU0_Target);
Console.WriteLine("Загружен " + TMU0_Filename + " с обработчиком " + TMU0_Handle + " как " + TMU0_Target);
Console.WriteLine("Загрузка текстур завершена. GL-ошибка: " + GL.GetError());
Console.WriteLine("");
sphere = new SlicedSphere(1.5f, Vector3d.Zero, SlicedSphere.eSubdivisions.Four, new SlicedSphere.eDir[] { SlicedSphere.eDir.All }, true);
}
protected override void OnUnload(EventArgs e)
{
sphere.Dispose();
GL.DeleteProgram(ProgramObject);
GL.DeleteTextures(1, ref TMU0_Handle);
base.OnUnload(e);
}
// Обработчик изменения размеров окна графического вывода
protected override void OnResize(EventArgs e)
{
GL.Viewport(0, 0, Width, Height);
GL.MatrixMode(MatrixMode.Projection);
Matrix4 p = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Width / (float)Height, 0.1f, 10.0f);
GL.LoadMatrix(ref p);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
base.OnResize(e);
}
// Обработчик обновления окна графического вывода
protected override void OnUpdateFrame(FrameEventArgs e)
{
base.OnUpdateFrame(e);
var keyboard = OpenTK.Input.Keyboard.GetState();
var mouse = OpenTK.Input.Mouse.GetState();
if (keyboard[OpenTK.Input.Key.Escape]) this.Exit();
if (keyboard[OpenTK.Input.Key.Space]) Console.WriteLine("GL: " + GL.GetError());
// Запоминаем координаты и прокрутку мыши
Trackball.X = mouse.X;
Trackball.Y = mouse.Y;
Trackball.Z = mouse.Scroll.Y * 0.5f;
}
// Обработчик воспроизведения
protected override void OnRenderFrame(FrameEventArgs e)
{
this.Title = "FPS: " + (1 / e.Time).ToString("0.");
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.UseProgram(ProgramObject);
GL.ActiveTexture(TMU0_Unit);
GL.BindTexture(TMU0_Target, TMU0_Handle);
GL.Uniform1(GL.GetUniformLocation(ProgramObject, "Earth"), TMU0_UnitInteger);
GL.PushMatrix();
Matrix4 temp = Matrix4.LookAt(EyePos, Vector3.Zero, Vector3.UnitY);
GL.MultMatrix(ref temp);
GL.Rotate(Trackball.X, Vector3.UnitY);
GL.Rotate(Trackball.Y, Vector3.UnitX);
GL.Color3(1f, 1f, 1f);
sphere.Draw();
GL.PopMatrix();
this.SwapBuffers();
}
// Entry point
[STAThread]
public static void Main()
{
using (T13_GLSL_Earth earth = new T13_GLSL_Earth())
{
earth.Title = "Earth cube mapping";
earth.Run(30.0, 0.0);
}
}
}
}
namespace TextureLoaders
{
// Нужна подходящая версия OpenGL с поддержкой сжатия текстур (GL 1.5) и кубических карт (GL 1.3)
// Необходимо, чтобы размеры текстуры делились на 4, поскольку DXTn использует блоки 4x4
// Кубическая карта должна быть задана для всех шести сторон куба
static class ImageDDS
{
private const byte HeaderSizeInBytes = 128; // Рабзмер заголовка в байтах
private const uint BitMask = 0x00000007; // биты 00 00 01 11
private static NotImplementedException Unfinished = new NotImplementedException("ERROR: Only 2 Dimensional DXT1/3/5 compressed images for now. 1D/3D Textures may not be compressed according to spec.");
private static bool _IsCompressed;
private static int _Width, _Height, _Depth, _MipMapCount;
private static int _BytesForMainSurface;
private static byte _BytesPerBlock;
private static PixelInternalFormat _PixelInternalFormat;
[Flags] // Описание поверхности
private enum eDDSD : uint
{
CAPS = 0x00000001, // is always present
HEIGHT = 0x00000002, // is always present
WIDTH = 0x00000004, // is always present
PITCH = 0x00000008, // is set if the image is uncompressed
PIXELFORMAT = 0x00001000, // is always present
MIPMAPCOUNT = 0x00020000, // is set if the image contains MipMaps
LINEARSIZE = 0x00080000, // is set if the image is compressed
DEPTH = 0x00800000 // is set for 3D Volume Textures
}
[Flags] // Формат пикселя
private enum eDDPF : uint
{
NONE = 0x00000000, // Not part of DX, added for convenience
ALPHAPIXELS = 0x00000001,
FOURCC = 0x00000004,
RGB = 0x00000040,
RGBA = 0x00000041
}
// Перечень взят из nVidia OpenGL SDK
[Flags] // Типы текстур
private enum eFOURCC : uint
{
UNKNOWN = 0,
DXT1 = 0x31545844,
DXT2 = 0x32545844,
DXT3 = 0x33545844,
DXT4 = 0x34545844,
DXT5 = 0x35545844,
}
[Flags] // dwCaps1
private enum eDDSCAPS : uint
{
NONE = 0x00000000, // not part of DX, added for convenience
COMPLEX = 0x00000008, // should be set for any DDS file with more than one main surface
TEXTURE = 0x00001000, // should always be set
MIPMAP = 0x00400000 // only for files with MipMaps
}
[Flags] // dwCaps2
private enum eDDSCAPS2 : uint
{
NONE = 0x00000000, // not part of DX, added for convenience
CUBEMAP = 0x00000200,
CUBEMAP_POSITIVEX = 0x00000400,
CUBEMAP_NEGATIVEX = 0x00000800,
CUBEMAP_POSITIVEY = 0x00001000,
CUBEMAP_NEGATIVEY = 0x00002000,
CUBEMAP_POSITIVEZ = 0x00004000,
CUBEMAP_NEGATIVEZ = 0x00008000,
CUBEMAP_ALL_FACES = 0x0000FC00,
VOLUME = 0x00200000 // for 3D Textures
}
private static string idString; // 4 байта, должно быть "DDS"
private static UInt32 dwSize; // Size of structure is 124 bytes, 128 including all sub-structs and the header
private static UInt32 dwFlags; // Flags to indicate valid fields
private static UInt32 dwHeight; // Height of the main image in pixels
private static UInt32 dwWidth; // Width of the main image in pixels
private static UInt32 dwPitchOrLinearSize; // For compressed formats, this is the total number of bytes for the main image
private static UInt32 dwDepth; // For volume textures, this is the depth of the volume
private static UInt32 dwMipMapCount; // Total number of levels in the mipmap chain of the main image
// Pixelformat sub-struct, 32 bytes
private static UInt32 pfSize; // Size of Pixelformat structure. This member must be set to 32
private static UInt32 pfFlags; // Flags to indicate valid fields
private static UInt32 pfFourCC; // This is the four-character code for compressed formats
// Capabilities sub-struct, 16 bytes
private static UInt32 dwCaps1; // Always includes DDSCAPS_TEXTURE with more than one main surface DDSCAPS_COMPLEX should also be set
// For cubic environment maps DDSCAPS2_CUBEMAP should be included
// as well as one or more faces of the map (DDSCAPS2_CUBEMAP_POSITIVEX,
// DDSCAPS2_CUBEMAP_NEGATIVEX, DDSCAPS2_CUBEMAP_POSITIVEY,
// DDSCAPS2_CUBEMAP_NEGATIVEY, DDSCAPS2_CUBEMAP_POSITIVEZ, DDSCAPS2_CUBEMAP_NEGATIVEZ)
// For volume textures, DDSCAPS2_VOLUME should be included
private static UInt32 dwCaps2;
// This function will generate, bind and fill a Texture Object with a DXT1/3/5 compressed Texture in .dds Format.
// MipMaps below 4x4 Pixel Size are discarded, because DXTn's smallest unit is a 4x4 block of Pixel data.
// It will set correct MipMap parameters, Filtering, Wrapping and EnvMode for the Texture.
// The only call inside this function affecting OpenGL State is GL.BindTexture();
// The name of the file you wish to load, including path and file extension
// 0 if invalid, otherwise a Texture Object usable with GL.BindTexture()
// 0 if invalid, will output what was loaded (typically Texture1D/2D/3D or Cubemap)
public static void LoadFromDisk(string filename, bool FlipImages, bool Verbose, out uint texturehandle, out TextureTarget dimension)
{
// Начальные установки
dimension = (TextureTarget)0;
ErrorCode GLError = ErrorCode.NoError;
_IsCompressed = false;
_Width = 0;
_Height = 0;
_Depth = 0;
_MipMapCount = 0;
_BytesForMainSurface = 0;
_BytesPerBlock = 0;
_PixelInternalFormat = PixelInternalFormat.Rgba8;
byte[] _RawDataFromFile;
try // Исключение возникнет при ошибках чтения файла
{
_RawDataFromFile = File.ReadAllBytes(@filename);
ConvertDX9Header(ref _RawDataFromFile); // The first 128 Bytes of the file is non-image data
// Поверка всех констант и флагов
if (idString != "DDS " || // magic key
dwSize != 124 || // constant size of struct, never reused
pfSize != 32 || // constant size of struct, never reused
!CheckFlag(dwFlags, (uint)eDDSD.CAPS) || // must know it's caps
!CheckFlag(dwFlags, (uint)eDDSD.PIXELFORMAT) || // must know it's format
!CheckFlag(dwCaps1, (uint)eDDSCAPS.TEXTURE) // must be a Texture
)
throw new ArgumentException("ERROR: File has invalid signature or missing Flags.");
if (CheckFlag(dwFlags, (uint)eDDSD.WIDTH))
_Width = (int)dwWidth;
else
throw new ArgumentException("ERROR: Flag for Width not set.");
if (CheckFlag(dwFlags, (uint)eDDSD.HEIGHT))
_Height = (int)dwHeight;
else
throw new ArgumentException("ERROR: Flag for Height not set.");
if (CheckFlag(dwFlags, (uint)eDDSD.DEPTH) && CheckFlag(dwCaps2, (uint)eDDSCAPS2.VOLUME))
{
dimension = TextureTarget.Texture3D; // image is 3D Volume
_Depth = (int)dwDepth;
throw Unfinished;
}
else
{// Куб или образ 2D
if (CheckFlag(dwCaps2, (uint)eDDSCAPS2.CUBEMAP))
{
dimension = TextureTarget.TextureCubeMap;
_Depth = 6;
}
else
{
dimension = TextureTarget.Texture2D;
_Depth = 1;
}
}
// Устанавливаем флаг при наличии mip-карт
if (CheckFlag(dwCaps1, (uint)eDDSCAPS.MIPMAP) && CheckFlag(dwFlags, (uint)eDDSD.MIPMAPCOUNT))
_MipMapCount = (int)dwMipMapCount; // Образ содержит mip-карты
else
_MipMapCount = 1; // Только один главный образ
// Should never happen
if (CheckFlag(dwFlags, (uint)eDDSD.PITCH) && CheckFlag(dwFlags, (uint)eDDSD.LINEARSIZE))
throw new ArgumentException("ОШИБКА: Одновременно указаны флаги Наклонный и Линейный. Образ не может быть одновременно несжатым и DTXn-сжатым");
// This flag is set if format is uncompressed RGB RGBA etc.
if (CheckFlag(dwFlags, (uint)eDDSD.PITCH))
{
_IsCompressed = false;
throw Unfinished;
}
// This flag is set if format is compressed DXTn.
if (CheckFlag(dwFlags, (uint)eDDSD.LINEARSIZE))
{
_BytesForMainSurface = (int)dwPitchOrLinearSize;
_IsCompressed = true;
}
if (CheckFlag(pfFlags, (uint)eDDPF.FOURCC))
switch ((eFOURCC)pfFourCC)
{
case eFOURCC.DXT1:
_PixelInternalFormat = (PixelInternalFormat)ExtTextureCompressionS3tc.CompressedRgbS3tcDxt1Ext;
_BytesPerBlock = 8;
_IsCompressed = true;
break;
case eFOURCC.DXT3:
_PixelInternalFormat = (PixelInternalFormat)ExtTextureCompressionS3tc.CompressedRgbaS3tcDxt3Ext;
_BytesPerBlock = 16;
_IsCompressed = true;
break;
case eFOURCC.DXT5:
_PixelInternalFormat = (PixelInternalFormat)ExtTextureCompressionS3tc.CompressedRgbaS3tcDxt5Ext;
_BytesPerBlock = 16;
_IsCompressed = true;
break;
default:
throw Unfinished; // Handle uncompressed formats
}
else
throw Unfinished;
if (Verbose) Console.WriteLine("\n" + GetDescriptionFromMemory(filename, dimension));
GL.GenTextures(1, out texturehandle);
GL.BindTexture(dimension, texturehandle);
int Cursor = HeaderSizeInBytes;
// Для кубической карты получаем все mip-карты. Только одна итерация для Texture2D
for (int Slices = 0; Slices < _Depth; Slices++)
{
int trueMipMapCount = _MipMapCount - 1;
int Width = _Width;
int Height = _Height;
for (int Level = 0; Level < _MipMapCount; Level++) // Начинаем с базового образа
{
int BlocksPerRow = (Width + 3) >> 2;
int BlocksPerColumn = (Height + 3) >> 2;
int SurfaceBlockCount = BlocksPerRow * BlocksPerColumn; // DXTn stores Texels in 4x4 blocks, a Color block is 8 Bytes, an Alpha block is 8 Bytes for DXT3/5
int SurfaceSizeInBytes = SurfaceBlockCount * _BytesPerBlock;
// This check must evaluate to false for 2D and Cube maps, or it's impossible to determine MipMap sizes.
if (Verbose && Level == 0 && _IsCompressed && _BytesForMainSurface != SurfaceSizeInBytes)
Console.WriteLine("Warning: Calculated byte-count of main image differs from what was read from file.");
// Skip mipmaps smaller than a 4x4 Pixels block, which is the smallest DXTn unit
if (Width > 2 && Height > 2)
{ // Замечание: при работе с образами, размеры которых не являются степенью числа 2, моогут быть проблемы
byte[] RawDataOfSurface = new byte[SurfaceSizeInBytes];
if (!FlipImages)
{ // Без изменений образа
Array.Copy(_RawDataFromFile, Cursor, RawDataOfSurface, 0, SurfaceSizeInBytes);
}
else
{ // Поворот образа на 180 градусов
for (int sourceColumn = 0; sourceColumn < BlocksPerColumn; sourceColumn++)
{
int targetColumn = BlocksPerColumn - sourceColumn - 1;
for (int row = 0; row < BlocksPerRow; row++)
{
int target = (targetColumn * BlocksPerRow + row) * _BytesPerBlock;
int source = (sourceColumn * BlocksPerRow + row) * _BytesPerBlock + Cursor;
switch (_PixelInternalFormat)
{
case (PixelInternalFormat)ExtTextureCompressionS3tc.CompressedRgbS3tcDxt1Ext:
// Color only
RawDataOfSurface[target + 0] = _RawDataFromFile[source + 0];
RawDataOfSurface[target + 1] = _RawDataFromFile[source + 1];
RawDataOfSurface[target + 2] = _RawDataFromFile[source + 2];
RawDataOfSurface[target + 3] = _RawDataFromFile[source + 3];
RawDataOfSurface[target + 4] = _RawDataFromFile[source + 7];
RawDataOfSurface[target + 5] = _RawDataFromFile[source + 6];
RawDataOfSurface[target + 6] = _RawDataFromFile[source + 5];
RawDataOfSurface[target + 7] = _RawDataFromFile[source + 4];
break;
case (PixelInternalFormat)ExtTextureCompressionS3tc.CompressedRgbaS3tcDxt3Ext:
// Alpha
RawDataOfSurface[target + 0] = _RawDataFromFile[source + 6];
RawDataOfSurface[target + 1] = _RawDataFromFile[source + 7];
RawDataOfSurface[target + 2] = _RawDataFromFile[source + 4];
RawDataOfSurface[target + 3] = _RawDataFromFile[source + 5];
RawDataOfSurface[target + 4] = _RawDataFromFile[source + 2];
RawDataOfSurface[target + 5] = _RawDataFromFile[source + 3];
RawDataOfSurface[target + 6] = _RawDataFromFile[source + 0];
RawDataOfSurface[target + 7] = _RawDataFromFile[source + 1];
// Color
RawDataOfSurface[target + 8] = _RawDataFromFile[source + 8];
RawDataOfSurface[target + 9] = _RawDataFromFile[source + 9];
RawDataOfSurface[target + 10] = _RawDataFromFile[source + 10];
RawDataOfSurface[target + 11] = _RawDataFromFile[source + 11];
RawDataOfSurface[target + 12] = _RawDataFromFile[source + 15];
RawDataOfSurface[target + 13] = _RawDataFromFile[source + 14];
RawDataOfSurface[target + 14] = _RawDataFromFile[source + 13];
RawDataOfSurface[target + 15] = _RawDataFromFile[source + 12];
break;
case (PixelInternalFormat)ExtTextureCompressionS3tc.CompressedRgbaS3tcDxt5Ext:
// Alpha, the first 2 bytes remain
RawDataOfSurface[target + 0] = _RawDataFromFile[source + 0];
RawDataOfSurface[target + 1] = _RawDataFromFile[source + 1];
// extract 3 bits each and flip them
GetBytesFromUInt24(ref RawDataOfSurface, (uint)target + 5, FlipUInt24(GetUInt24(ref _RawDataFromFile, (uint)source + 2)));
GetBytesFromUInt24(ref RawDataOfSurface, (uint)target + 2, FlipUInt24(GetUInt24(ref _RawDataFromFile, (uint)source + 5)));
// Color
RawDataOfSurface[target + 8] = _RawDataFromFile[source + 8];
RawDataOfSurface[target + 9] = _RawDataFromFile[source + 9];
RawDataOfSurface[target + 10] = _RawDataFromFile[source + 10];
RawDataOfSurface[target + 11] = _RawDataFromFile[source + 11];
RawDataOfSurface[target + 12] = _RawDataFromFile[source + 15];
RawDataOfSurface[target + 13] = _RawDataFromFile[source + 14];
RawDataOfSurface[target + 14] = _RawDataFromFile[source + 13];
RawDataOfSurface[target + 15] = _RawDataFromFile[source + 12];
break;
default:
throw new ArgumentException("ERROR: Should have never arrived here! Bad _PixelInternalFormat! Should have been dealt with much earlier.");
}
}
}
}
switch (dimension)
{
case TextureTarget.Texture2D:
GL.CompressedTexImage2D(TextureTarget.Texture2D, Level, _PixelInternalFormat,
Width, Height, 0, SurfaceSizeInBytes, RawDataOfSurface);
break;
case TextureTarget.TextureCubeMap:
GL.CompressedTexImage2D(TextureTarget.TextureCubeMapPositiveX + Slices, Level, _PixelInternalFormat,
Width, Height, 0, SurfaceSizeInBytes, RawDataOfSurface);
break;
default:
throw new ArgumentException("Ошибка: Используйтеп DXT для 2D-образов. Не могу работать с " + dimension);
}
GL.Finish();
int width, height, internalformat, compressed;
switch (dimension)
{
case TextureTarget.Texture1D:
case TextureTarget.Texture2D:
case TextureTarget.Texture3D:
GL.GetTexLevelParameter(dimension, Level, GetTextureParameter.TextureWidth, out width);
GL.GetTexLevelParameter(dimension, Level, GetTextureParameter.TextureHeight, out height);
GL.GetTexLevelParameter(dimension, Level, GetTextureParameter.TextureInternalFormat, out internalformat);
GL.GetTexLevelParameter(dimension, Level, GetTextureParameter.TextureCompressed, out compressed);
break;
case TextureTarget.TextureCubeMap:
GL.GetTexLevelParameter(TextureTarget.TextureCubeMapPositiveX + Slices, Level, GetTextureParameter.TextureWidth, out width);
GL.GetTexLevelParameter(TextureTarget.TextureCubeMapPositiveX + Slices, Level, GetTextureParameter.TextureHeight, out height);
GL.GetTexLevelParameter(TextureTarget.TextureCubeMapPositiveX + Slices, Level, GetTextureParameter.TextureInternalFormat, out internalformat);
GL.GetTexLevelParameter(TextureTarget.TextureCubeMapPositiveX + Slices, Level, GetTextureParameter.TextureCompressed, out compressed);
break;
default:
throw Unfinished;
}
GLError = GL.GetError();
if (Verbose)
Console.WriteLine("GL: " + GLError.ToString() + " Level: " + Level + " DXTn: " + ((compressed == 1) ? "Yes" : "No") + " Frmt:" + (ExtTextureCompressionS3tc)internalformat + " " + width + "*" + height);
if (GLError != ErrorCode.NoError || compressed == 0 || width == 0 || height == 0 || internalformat == 0)
{
GL.DeleteTextures(1, ref texturehandle);
throw new ArgumentException("ERROR: Something went wrong after GL.CompressedTexImage(); Last GL Error: " + GLError.ToString());
}
}
else
if (trueMipMapCount > Level) trueMipMapCount = Level - 1; // The current Level is invalid
Width /= 2;
if (Width < 1) Width = 1;
Height /= 2;
if (Height < 1) Height = 1;
Cursor += SurfaceSizeInBytes;
}
GL.TexParameter(dimension, (TextureParameterName)All.TextureBaseLevel, 0);
GL.TexParameter(dimension, (TextureParameterName)All.TextureMaxLevel, trueMipMapCount);
int TexMaxLevel;
GL.GetTexParameter(dimension, GetTextureParameter.TextureMaxLevel, out TexMaxLevel);
if (Verbose)
Console.WriteLine("Verification: GL: " + GL.GetError().ToString() + " TextureMaxLevel: " + TexMaxLevel + ((TexMaxLevel == trueMipMapCount) ? " (Correct.)" : " (Wrong!)"));
}
GL.TexParameter(dimension, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(dimension, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexParameter(dimension, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(dimension, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Modulate);
GLError = GL.GetError();
if (GLError != ErrorCode.NoError)
throw new ArgumentException("Ошибка при установки параметров тестуры. GL-ошибка: " + GLError);
return; // Удача
}
catch (Exception e)
{
dimension = (TextureTarget)0;
throw new ArgumentException("ERROR: Exception caught when attempting to load file " + filename + ".\n" + e + "\n" + GetDescriptionFromFile(filename));
}
finally
{
_RawDataFromFile = null;
}
}
private static void ConvertDX9Header(ref byte[] input)
{
UInt32 offset = 0;
idString = GetString(ref input, offset);
offset += 4;
dwSize = GetUInt32(ref input, offset);
offset += 4;
dwFlags = GetUInt32(ref input, offset);
offset += 4;
dwHeight = GetUInt32(ref input, offset);
offset += 4;
dwWidth = GetUInt32(ref input, offset);
offset += 4;
dwPitchOrLinearSize = GetUInt32(ref input, offset);
offset += 4;
dwDepth = GetUInt32(ref input, offset);
offset += 4;
dwMipMapCount = GetUInt32(ref input, offset);
offset += 4;
offset += 4 * 11;
pfSize = GetUInt32(ref input, offset);
offset += 4;
pfFlags = GetUInt32(ref input, offset);
offset += 4;
pfFourCC = GetUInt32(ref input, offset);
offset += 4;
offset += 20;
dwCaps1 = GetUInt32(ref input, offset);
offset += 4;
dwCaps2 = GetUInt32(ref input, offset);
offset += 4;
offset += 4 * 3;
}
// Returns true if the flag is set, false otherwise
private static bool CheckFlag(uint variable, uint flag)
{
return (variable & flag) > 0 ? true : false;
}
private static string GetString(ref byte[] input, uint offset)
{
return "" + (char)input[offset + 0] + (char)input[offset + 1] + (char)input[offset + 2] + (char)input[offset + 3];
}
private static uint GetUInt32(ref byte[] input, uint offset)
{
return (uint)(((input[offset + 3] * 256 + input[offset + 2]) * 256 + input[offset + 1]) * 256 + input[offset + 0]);
}
private static uint GetUInt24(ref byte[] input, uint offset)
{
return (uint)((input[offset + 2] * 256 + input[offset + 1]) * 256 + input[offset + 0]);
}
private static void GetBytesFromUInt24(ref byte[] input, uint offset, uint splitme)
{
input[offset + 0] = (byte)(splitme & 0x000000ff);
input[offset + 1] = (byte)((splitme & 0x0000ff00) >> 8);
input[offset + 2] = (byte)((splitme & 0x00ff0000) >> 16);
return;
}
// DXT5 Alpha block flipping, inspired by code from Evan Hart (nVidia SDK)
private static uint FlipUInt24(uint inputUInt24)
{
byte[][] ThreeBits = new byte[2][];
for (int i = 0; i < 2; i++) ThreeBits[i] = new byte[4];
// Extract 3 bits each into the array
ThreeBits[0][0] = (byte)(inputUInt24 & BitMask);
inputUInt24 >>= 3;
ThreeBits[0][1] = (byte)(inputUInt24 & BitMask);
inputUInt24 >>= 3;
ThreeBits[0][2] = (byte)(inputUInt24 & BitMask);
inputUInt24 >>= 3;
ThreeBits[0][3] = (byte)(inputUInt24 & BitMask);
inputUInt24 >>= 3;
ThreeBits[1][0] = (byte)(inputUInt24 & BitMask);
inputUInt24 >>= 3;
ThreeBits[1][1] = (byte)(inputUInt24 & BitMask);
inputUInt24 >>= 3;
ThreeBits[1][2] = (byte)(inputUInt24 & BitMask);
inputUInt24 >>= 3;
ThreeBits[1][3] = (byte)(inputUInt24 & BitMask);
// Stuff 8x 3bits into 3 bytes
uint Result = 0;
Result = Result | (uint)(ThreeBits[1][0] << 0);
Result = Result | (uint)(ThreeBits[1][1] << 3);
Result = Result | (uint)(ThreeBits[1][2] << 6);
Result = Result | (uint)(ThreeBits[1][3] << 9);
Result = Result | (uint)(ThreeBits[0][0] << 12);
Result = Result | (uint)(ThreeBits[0][1] << 15);
Result = Result | (uint)(ThreeBits[0][2] << 18);
Result = Result | (uint)(ThreeBits[0][3] << 21);
return Result;
}
private static string GetDescriptionFromFile(string filename)
{
return "\n--> Header of " + filename +
"\nID: " + idString +
"\nSize: " + dwSize +
"\nFlags: " + dwFlags + " (" + (eDDSD)dwFlags + ")" +
"\nHeight: " + dwHeight +
"\nWidth: " + dwWidth +
"\nPitch: " + dwPitchOrLinearSize +
"\nDepth: " + dwDepth +
"\nMipMaps: " + dwMipMapCount +
"\n\n---PixelFormat---" + filename +
"\nSize: " + pfSize +
"\nFlags: " + pfFlags + " (" + (eDDPF)pfFlags + ")" +
"\nFourCC: " + pfFourCC + " (" + (eFOURCC)pfFourCC + ")" +
"\n\n---Capabilities---" + filename +
"\nCaps1: " + dwCaps1 + " (" + (eDDSCAPS)dwCaps1 + ")" +
"\nCaps2: " + dwCaps2 + " (" + (eDDSCAPS2)dwCaps2 + ")";
}
private static string GetDescriptionFromMemory(string filename, TextureTarget Dimension)
{
return "\nFile: " + filename +
"\nDimension: " + Dimension +
"\nSize: " + _Width + " * " + _Height + " * " + _Depth +
"\nCompressed: " + _IsCompressed +
"\nBytes for Main Image: " + _BytesForMainSurface +
"\nMipMaps: " + _MipMapCount;
}
}
}
namespace Shapes
{
// Abstract base class for procedurally generated geometry
// All classes derived from it must produce Counter-Clockwise (CCW) primitives.
// Derived classes must create a single VBO and IBO, without primitive restarts for strips.
// Uses an double-precision all-possible-attributes VertexT2dN3dV3d Array internally.
// Cannot directly use VBO, but has Get-methods to retrieve VBO-friendly data.
// Can use a Display List to prevent repeated immediate mode draws.
// VBO - Vertex Buffer Object
public abstract class DrawableShape : IDisposable
{
protected PrimitiveType PrimitiveMode;
protected VertexT2dN3dV3d[] VertexArray;
protected uint[] IndexArray;
public int GetTriangleCount
{
get
{
switch (PrimitiveMode)
{
case PrimitiveType.Triangles:
if (IndexArray != null)
{
return IndexArray.Length / 3;
}
else
{
return VertexArray.Length / 3;
}
// break;
default: throw new NotImplementedException("Unknown primitive type.");
}
}
}
private bool UseDisplayList;
private int DisplayListHandle = 0;
public DrawableShape(bool useDisplayList)
{
UseDisplayList = useDisplayList;
PrimitiveMode = PrimitiveType.Triangles;
VertexArray = null;
IndexArray = null;
}
public void GetArraysforVBO(out PrimitiveType primitives, out VertexT2dN3dV3d[] vertices, out uint[] indices)
{
primitives = PrimitiveMode;
vertices = new VertexT2dN3dV3d[VertexArray.Length];
for (uint i = 0; i < VertexArray.Length; i++)
{
vertices[i].TexCoord = VertexArray[i].TexCoord;
vertices[i].Normal = VertexArray[i].Normal;
vertices[i].Position = VertexArray[i].Position;
}
indices = IndexArray;
}
public void GetArraysforVBO(out PrimitiveType primitives, out VertexT2fN3fV3f[] vertices, out uint[] indices)
{
primitives = PrimitiveMode;
vertices = new VertexT2fN3fV3f[VertexArray.Length];
for (uint i = 0; i < VertexArray.Length; i++)
{
vertices[i].TexCoord = (Vector2)VertexArray[i].TexCoord;
vertices[i].Normal = (Vector3)VertexArray[i].Normal;
vertices[i].Position = (Vector3)VertexArray[i].Position;
}
indices = IndexArray;
}
public void GetArraysforVBO(out PrimitiveType primitives, out VertexT2hN3hV3h[] vertices, out uint[] indices)
{
primitives = PrimitiveMode;
vertices = new VertexT2hN3hV3h[VertexArray.Length];
for (uint i = 0; i < VertexArray.Length; i++)
{
vertices[i].TexCoord = (Vector2h)VertexArray[i].TexCoord;
vertices[i].Normal = (Vector3h)VertexArray[i].Normal;
vertices[i].Position = (Vector3h)VertexArray[i].Position;
}
indices = IndexArray;
}
private void DrawImmediateMode()
{
GL.Begin(PrimitiveMode);
{
if (IndexArray == null)
foreach (VertexT2dN3dV3d v in VertexArray)
{
GL.TexCoord2(v.TexCoord.X, v.TexCoord.Y);
GL.Normal3(v.Normal.X, v.Normal.Y, v.Normal.Z);
GL.Vertex3(v.Position.X, v.Position.Y, v.Position.Z);
}
else
{
for (uint i = 0; i < IndexArray.Length; i++)
{
uint index = IndexArray[i];
GL.TexCoord2(VertexArray[index].TexCoord.X, VertexArray[index].TexCoord.Y);
GL.Normal3(VertexArray[index].Normal.X, VertexArray[index].Normal.Y, VertexArray[index].Normal.Z);
GL.Vertex3(VertexArray[index].Position.X, VertexArray[index].Position.Y, VertexArray[index].Position.Z);
}
}
}
GL.End();
}
// Does not touch any state/matrices. Does call Begin/End and Vertex&Co.
// Creates and compiles a display list if not present yet. Requires an OpenGL context.
public void Draw()
{
if (!UseDisplayList)
DrawImmediateMode();
else
if (DisplayListHandle == 0)
{
if (VertexArray == null)
throw new Exception("Cannot draw null Vertex Array.");
DisplayListHandle = GL.GenLists(1);
GL.NewList(DisplayListHandle, ListMode.CompileAndExecute);
DrawImmediateMode();
GL.EndList();
}
else
GL.CallList(DisplayListHandle);
}
// Removes reference to VertexArray and IndexArray
// Deletes the Display List, so it requires an OpenGL context
// The instance is effectively destroyed
public void Dispose()
{
if (VertexArray != null) VertexArray = null;
if (IndexArray != null) IndexArray = null;
if (DisplayListHandle != 0)
{
GL.DeleteLists(DisplayListHandle, 1);
DisplayListHandle = 0;
}
}
}
public sealed class SlicedSphere : DrawableShape
{
public enum eSubdivisions
{
Zero = 0,
One = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
}
public enum eDir
{
All,
FrontTopRight,
FrontBottomRight,
FrontBottomLeft,
FrontTopLeft,
BackTopRight,
BackBottomRight,
BackBottomLeft,
BackTopLeft,
}
public SlicedSphere(double radius, Vector3d offset, eSubdivisions subdivs, eDir[] sides, bool useDL) : base(useDL)
{
double Diameter = radius;
PrimitiveMode = OpenTK.Graphics.OpenGL.PrimitiveType.Triangles;
if (sides[0] == eDir.All)
{
sides = new eDir[] {
eDir.FrontTopRight,
eDir.FrontBottomRight,
eDir.FrontBottomLeft,
eDir.FrontTopLeft,
eDir.BackTopRight,
eDir.BackBottomRight,
eDir.BackBottomLeft,
eDir.BackTopLeft,};
}
VertexArray = new VertexT2dN3dV3d[sides.Length * 3];
IndexArray = new uint[sides.Length * 3];
uint counter = 0;
foreach (eDir s in sides)
{
GetDefaultVertices(s, Diameter, out VertexArray[counter + 0], out VertexArray[counter + 1], out VertexArray[counter + 2]);
IndexArray[counter + 0] = counter + 0;
IndexArray[counter + 1] = counter + 1;
IndexArray[counter + 2] = counter + 2;
counter += 3;
}
if (subdivs != eSubdivisions.Zero)
{
for (int s = 0; s < (int)subdivs; s++)
{
List<Chunk> AllChunks = new List<Chunk>();
for (uint i = 0; i < IndexArray.Length; i += 3)
{
Chunk chu;
Subdivide(Diameter,
ref VertexArray[IndexArray[i + 0]],
ref VertexArray[IndexArray[i + 1]],
ref VertexArray[IndexArray[i + 2]],
out chu);
AllChunks.Add(chu);
}
Chunk.GetArray(ref AllChunks, out VertexArray, out IndexArray);
AllChunks.Clear();
}
}
for (int i = 0; i < VertexArray.Length; i++)
{
Vector3d.Add(ref VertexArray[i].Position, ref offset, out VertexArray[i].Position);
}
}
private void GetDefaultVertices(eDir s, double scale, out VertexT2dN3dV3d first, out VertexT2dN3dV3d second, out VertexT2dN3dV3d third)
{
VertexT2dN3dV3d t1 = new VertexT2dN3dV3d(), t2 = new VertexT2dN3dV3d(), t3 = new VertexT2dN3dV3d();
switch (s)
{
case eDir.FrontTopRight:
t1 = new VertexT2dN3dV3d(new Vector2d(0.5, 1.0), Vector3d.UnitY, Vector3d.UnitY * scale);
t2 = new VertexT2dN3dV3d(new Vector2d(0.0, 0.0), Vector3d.UnitZ, Vector3d.UnitZ * scale);
t3 = new VertexT2dN3dV3d(new Vector2d(0.5, 0.0), Vector3d.UnitX, Vector3d.UnitX * scale);
break;
case eDir.FrontBottomRight:
t1 = new VertexT2dN3dV3d(new Vector2d(0.5, 0.0), Vector3d.UnitX, Vector3d.UnitX * scale);
t2 = new VertexT2dN3dV3d(new Vector2d(0.0, 0.0), Vector3d.UnitZ, Vector3d.UnitZ * scale);
t3 = new VertexT2dN3dV3d(new Vector2d(0.5, 1.0), -Vector3d.UnitY, -Vector3d.UnitY * scale);
break;
case eDir.FrontBottomLeft:
t1 = new VertexT2dN3dV3d(new Vector2d(0.5, 0.0), Vector3d.UnitX, Vector3d.UnitX * scale);
t2 = new VertexT2dN3dV3d(new Vector2d(0.5, 1.0), -Vector3d.UnitY, -Vector3d.UnitY * scale);
t3 = new VertexT2dN3dV3d(new Vector2d(1.0, 0.0), -Vector3d.UnitZ, -Vector3d.UnitZ * scale);
break;
case eDir.FrontTopLeft:
t1 = new VertexT2dN3dV3d(new Vector2d(1.0, 0.0), -Vector3d.UnitZ, -Vector3d.UnitZ * scale);
t2 = new VertexT2dN3dV3d(new Vector2d(0.5, 1.0), Vector3d.UnitY, Vector3d.UnitY * scale);
t3 = new VertexT2dN3dV3d(new Vector2d(0.5, 0.0), Vector3d.UnitX, Vector3d.UnitX * scale);
break;
case eDir.BackTopRight:
t1 = new VertexT2dN3dV3d(new Vector2d(0.5, 1.0), Vector3d.UnitY, Vector3d.UnitY * scale);
t2 = new VertexT2dN3dV3d(new Vector2d(0.0, 1.0), -Vector3d.UnitX, -Vector3d.UnitX * scale);
t3 = new VertexT2dN3dV3d(new Vector2d(0.0, 0.0), Vector3d.UnitZ, Vector3d.UnitZ * scale);
break;
case eDir.BackBottomRight:
t1 = new VertexT2dN3dV3d(new Vector2d(0.5, 1.0), -Vector3d.UnitY, -Vector3d.UnitY * scale);
t2 = new VertexT2dN3dV3d(new Vector2d(0.0, 0.0), Vector3d.UnitZ, Vector3d.UnitZ * scale);
t3 = new VertexT2dN3dV3d(new Vector2d(0.0, 1.0), -Vector3d.UnitX, -Vector3d.UnitX * scale);
break;
case eDir.BackBottomLeft:
t1 = new VertexT2dN3dV3d(new Vector2d(0.5, 1.0), -Vector3d.UnitY, -Vector3d.UnitY * scale);
t2 = new VertexT2dN3dV3d(new Vector2d(1.0, 1.0), -Vector3d.UnitX, -Vector3d.UnitX * scale);
t3 = new VertexT2dN3dV3d(new Vector2d(1.0, 0.0), -Vector3d.UnitZ, -Vector3d.UnitZ * scale);
break;
case eDir.BackTopLeft:
t1 = new VertexT2dN3dV3d(new Vector2d(0.5, 1.0), Vector3d.UnitY, Vector3d.UnitY * scale);
t2 = new VertexT2dN3dV3d(new Vector2d(1.0, 0.0), -Vector3d.UnitZ, -Vector3d.UnitZ * scale);
t3 = new VertexT2dN3dV3d(new Vector2d(1.0, 1.0), -Vector3d.UnitX, -Vector3d.UnitX * scale);
break;
}
first = t1;
second = t2;
third = t3;
}
private void Subdivide(double Scale, ref VertexT2dN3dV3d first, ref VertexT2dN3dV3d second, ref VertexT2dN3dV3d third, out Chunk c)
{
c = new Chunk(6, 12);
c.Vertices[0] = first;
Vector3d.Lerp(ref first.Position, ref second.Position, 0.5, out c.Vertices[1].Normal);
c.Vertices[1].Normal.Normalize();
c.Vertices[1].Position = c.Vertices[1].Normal * Scale;
Vector2d.Lerp(ref first.TexCoord, ref second.TexCoord, 0.5, out c.Vertices[1].TexCoord);
Vector3d.Lerp(ref third.Position, ref first.Position, 0.5, out c.Vertices[2].Normal);
c.Vertices[2].Normal.Normalize();
c.Vertices[2].Position = c.Vertices[2].Normal * Scale;
Vector2d.Lerp(ref third.TexCoord, ref first.TexCoord, 0.5, out c.Vertices[2].TexCoord);
c.Vertices[3] = second;
Vector3d.Lerp(ref second.Position, ref third.Position, 0.5, out c.Vertices[4].Normal);
c.Vertices[4].Normal.Normalize();
c.Vertices[4].Position = c.Vertices[4].Normal * Scale;
Vector2d.Lerp(ref second.TexCoord, ref third.TexCoord, 0.5, out c.Vertices[4].TexCoord);
c.Vertices[5] = third;
c.Indices[0] = 0;
c.Indices[1] = 1;
c.Indices[2] = 2;
c.Indices[3] = 2;
c.Indices[4] = 1;
c.Indices[5] = 4;
c.Indices[6] = 1;
c.Indices[7] = 3;
c.Indices[8] = 4;
c.Indices[9] = 2;
c.Indices[10] = 4;
c.Indices[11] = 5;
}
}
public class Chunk
{
public VertexT2dN3dV3d[] Vertices;
public uint[] Indices;
public uint VertexCount
{
get
{
return (uint)Vertices.Length;
}
}
public uint IndexCount
{
get
{
return (uint)Indices.Length;
}
}
public Chunk(uint vertexcount, uint indexcount)
{
Vertices = new VertexT2dN3dV3d[vertexcount];
Indices = new uint[indexcount];
}
public Chunk(ref VertexT2dN3dV3d[] vbo, ref uint[] ibo)
{
Vertices = new VertexT2dN3dV3d[vbo.Length];
for (int i = 0; i < Vertices.Length; i++) Vertices[i] = vbo[i];
Indices = new uint[ibo.Length];
for (int i = 0; i < Indices.Length; i++) Indices[i] = ibo[i];
}
public static void GetArray(ref List<Chunk> c, out VertexT2dN3dV3d[] vbo, out uint[] ibo)
{
uint VertexCounter = 0;
uint IndexCounter = 0;
foreach (Chunk ch in c)
{
VertexCounter += ch.VertexCount;
IndexCounter += ch.IndexCount;
}
vbo = new VertexT2dN3dV3d[VertexCounter]; // Vertex Buffer Object
ibo = new uint[IndexCounter];
VertexCounter = 0;
IndexCounter = 0;
foreach (Chunk ch in c)
{
for (int i = 0; i < ch.Vertices.Length; i++) vbo[VertexCounter + i] = ch.Vertices[i];
for (int i = 0; i < ch.Indices.Length; i++) ibo[IndexCounter + i] = ch.Indices[i] + VertexCounter;
VertexCounter += (uint)ch.VertexCount;
IndexCounter += (uint)ch.IndexCount;
}
}
}
public struct VertexT2dN3dV3d
{
public Vector2d TexCoord;
public Vector3d Normal;
public Vector3d Position;
public VertexT2dN3dV3d(Vector2d texcoord, Vector3d normal, Vector3d position)
{
TexCoord = texcoord;
Normal = normal;
Position = position;
}
}
public struct VertexT2fN3fV3f
{
public Vector2 TexCoord;
public Vector3 Normal;
public Vector3 Position;
}
public struct VertexT2hN3hV3h
{
public Vector2h TexCoord;
public Vector3h Normal;
public Vector3h Position;
}
}