using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using OpenGL4NET;
namespace ZPG
{
///
/// Hlavni okno
///
class Osvetleni : System.Windows.Forms.Form
{
// Pocet vzorku na ose
uint n = 150;
// Context
RenderingContext rc;
// Mereni casu
Time time;
// Objekt
Vlna vlna;
///
/// Inicializace
///
/// True, pokud uspech, jinak false
public bool Init()
{
try
{
ClientSize = new System.Drawing.Size(800, 800);
rc = RenderingContext.CreateContext(this, new RenderingContextSetting() { multisample = 0 });
vlna = new Vlna(n);
// Zapnuti svetel
gl.Enable(GL.LIGHTING);
gl.Enable(GL.LIGHT0);
// Povoleni obarvovani telesa dle barev vrcholu, v opacnem pripade je treba nastavit pro kazdy objekt material pomoci glMaterial*
gl.Enable(GL.COLOR_MATERIAL);
// Povoleni hloubkovehoi testu
gl.Enable(GL.DEPTH_TEST);
time = new Time();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return false;
}
return true;
}
///
/// Kresleni sceny
///
void Render()
{
// Dalsi snimek
time.NextFrame();
// Vycisteni bufferu
gl.Clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);
// Nastaveni projekce
gl.MatrixMode(GL.PROJECTION);
gl.LoadIdentity();
gl.LoadMatrixf(Matrix.Perspective(45,(float)ClientSize.Width/ClientSize.Height, 1,2*n));
// Nastaveni modelview
gl.MatrixMode(GL.MODELVIEW);
gl.LoadIdentity();
gl.Translatef(-n / 2, n/3, -2f*n);
gl.Rotated(30, 1, 0, 0);
// Vypocet pozice svetla, homogenni souradnice udava, zda se jedna o vektor (0) ci vrchol v E3 (1)
Vector4 lightPos = new Vector4((float)Math.Sin(time.FrameTime / 1000.0) * n / 4 + n / 2, 2*n / 30, (float)Math.Cos(time.FrameTime / 1000.0) * n / 4 + n / 2, 1);
// Nastaveni pozice svetla
gl.Lightfv(GL.LIGHT0, GL.POSITION, new float[] {lightPos.x, lightPos.y, lightPos.z, lightPos.w});
// Nastaveni utlumu se vzdalenosti
gl.Lightf(GL.LIGHT0, GL.CONSTANT_ATTENUATION, 0);
gl.Lightf(GL.LIGHT0, GL.LINEAR_ATTENUATION, 0.05f);
gl.Lightf(GL.LIGHT0, GL.QUADRATIC_ATTENUATION, 0.005f);
// Vykresleni objektu reprezentujiciho svetlo
gl.PointSize(8);
gl.Disable(GL.LIGHTING);
gl.Begin(GL.POINTS);
gl.Color3f(1, 1, 1);
gl.Vertex3d(lightPos.x, lightPos.y, lightPos.z);
gl.End();
gl.Enable(GL.LIGHTING);
vlna.Render();
rc.SwapBuffers();
}
///
/// Prekryti metody na zpracovani zprav
///
/// Zprava
protected override void WndProc(ref System.Windows.Forms.Message m)
{
switch (m.Msg)
{
// Pokud WM_PAINT, prekresli scenu
case Windows.WM_PAINT: Render(); break;
default: base.WndProc(ref m); break;
}
}
///
/// Vstupni bod programu
///
/// Na parametry nereaguje
static void Main(string[] args)
{
Osvetleni prg = new Osvetleni();
if (prg.Init())
System.Windows.Forms.Application.Run(prg);
}
}
///
/// Trida pro kresleni vlny
///
class Vlna
{
// Vertex a index buffer object
uint vboData;
uint iboData;
// Pocet trojuhelniku
int triCount;
///
/// Konstruktor, vytvori data
///
/// Pocet vzorku v jedne ose
public Vlna(uint n)
{
// Vytvoreni pole vrcholu obsahujicich pozici, normalu a barvu
PositionNormalColor[] vertex = new PositionNormalColor[n * n];
// Naplneni pole vrcholu pozici a barvou. Normalu musime nechat na pozdeji, az budeme znat sousedy
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
double sx = (i - n / 2.0) / n * Math.PI * 2;
sx *= sx;
double sy = (j - n / 2.0) / n * Math.PI * 2;
sy *= sy;
float height = (float)(Math.Sin(sx + sy) * (n / 30.0));
vertex[i * n + j] = new PositionNormalColor(j, height, i, 0, 0, 0, 1, 0, 0);
vertex[i * n + j].color = ComputeColor(height / (n / 30.0));
}
}
// Vytvoreni trojuhelnikove site
triCount = (int)(3 * (n - 1) * (n - 1) * 2);
uint[] index = new uint[triCount];
int p = 0;
for (uint i = 0; i < n - 1; i++)
{
for (uint j = 0; j < n - 1; j++)
{
index[p++] = i * n + j;
index[p++] = (i + 1) * n + j;
index[p++] = i * n + j + 1;
index[p++] = i * n + j + 1;
index[p++] = (i + 1) * n + j;
index[p++] = (i + 1) * n + j + 1;
}
}
// Vypocet normal
ComputeNormals(vertex, index);
// Priprava vbo
vboData = gl.GenBuffer();
gl.BindBuffer(GL.ARRAY_BUFFER, vboData);
gl.BufferData(GL.ARRAY_BUFFER, (int)(n * n) * PositionNormalColor.SizeInBytes, vertex, GL.STATIC_DRAW);
gl.BindBuffer(GL.ARRAY_BUFFER, 0);
// Priiprava ibo
iboData = gl.GenBuffer();
gl.BindBuffer(GL.ELEMENT_ARRAY_BUFFER, iboData);
gl.BufferData(GL.ELEMENT_ARRAY_BUFFER, index.Length * sizeof(uint), index, GL.STATIC_DRAW);
gl.BindBuffer(GL.ELEMENT_ARRAY_BUFFER, 0);
gl.BindBuffer(GL.ARRAY_BUFFER, 0);
}
///
/// Vypocet barvicek na zaklade vysky
///
/// Vyska z intervalu [-1;1]
/// Interpolovana barva
Vector3 ComputeColor(double param)
{
Vector3 low = new Vector3(0, 0.5f, 1);
Vector3 mid = new Vector3(.9f, .8f, 0);
Vector3 hig = new Vector3(.2f, .7f, 0);
Vector3 res = new Vector3();
if (param < 0)
{
res = mid * (1 + (float)param) - low * (float)param;
}
else
{
res = mid * (1 - (float)(param)) + hig * (float)(param);
}
return res;
}
///
/// Vypocet normal
///
/// Pole vrcholu
/// Indexy trojuhelniku
void ComputeNormals(PositionNormalColor[] vertex, uint[] index)
{
// Projit kazdy trojuhelnik (sklada se ze 3 vrcholu)
for (int i = 0; i < index.Length; i += 3)
{
// Zjisit vektor od vrcholu 0 k 1
Vector3 v1 = vertex[index[i + 1]].position - vertex[index[i]].position;
// Zjisit vektor od vrcholu 0 k 2
Vector3 v2 = vertex[index[i + 2]].position - vertex[index[i]].position;
// Vektorovy soucin urci kolmy vektor
Vector3 n = Vector3.Cross(v1, v2);
n.Normalize();
// K normale kazdeho vrcholu pricist spocitanou normalu
vertex[index[i]].normal += n;
vertex[index[i + 1]].normal += n;
vertex[index[i + 2]].normal += n;
}
// Normaly normalizovat! Ve vypoctu osvetleni se na to spoleha
// Lze obejit povoleni glEnable(GL_NORMALIZE), pocita se vsak v kazdem snimku a brzdi vykreslovani
for (int i = 0; i < vertex.Length; i++)
{
vertex[i].normal.Normalize();
}
}
public void Render()
{
// Vykresleni dat
gl.BindBuffer(GL.ARRAY_BUFFER, vboData);
gl.BindBuffer(GL.ELEMENT_ARRAY_BUFFER, iboData);
gl.EnableClientState(GL.VERTEX_ARRAY);
gl.VertexPointer(3, GL.FLOAT, PositionNormalColor.SizeInBytes, (IntPtr)0);
gl.EnableClientState(GL.NORMAL_ARRAY);
gl.NormalPointer(GL.FLOAT, PositionNormalColor.SizeInBytes, (IntPtr)12);
gl.EnableClientState(GL.COLOR_ARRAY);
gl.ColorPointer(3, GL.FLOAT, PositionNormalColor.SizeInBytes, (IntPtr)24);
gl.DrawElements(GL.TRIANGLES, triCount, GL.UNSIGNED_INT, (IntPtr)0);
gl.DisableClientState(GL.VERTEX_ARRAY);
gl.DisableClientState(GL.NORMAL_ARRAY);
gl.DisableClientState(GL.COLOR_ARRAY);
gl.BindBuffer(GL.ARRAY_BUFFER, 0);
gl.BindBuffer(GL.ELEMENT_ARRAY_BUFFER, 0);
}
}
///
/// Struktura vrcholu obsahujci pozici, normalu a barvu
///
[StructLayout(LayoutKind.Sequential)]
struct PositionNormalColor
{
public Vector3 position;
public Vector3 normal;
public Vector3 color;
///
/// Konstruktor z floatu
///
/// X
/// Y
/// Z
/// nX
/// nY
/// nZ
/// R
/// G
/// B
public PositionNormalColor(float x, float y, float z, float nx, float ny, float nz, float r, float g, float b)
{
position = new Vector3(x, y, z);
normal = new Vector3(nx, ny, nz);
color = new Vector3(r, g, b);
}
///
/// Velikost struktury v bytech
///
public static int SizeInBytes
{
get
{
return Marshal.SizeOf(typeof(PositionNormalColor));
}
}
}
}