git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@492 52acb1d6-8a22-11de-b505-999d5b087335
356 lines
14 KiB
C#
356 lines
14 KiB
C#
using System;
|
|
using System.Text;
|
|
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
namespace sceneviewer
|
|
{
|
|
class WaterSurface
|
|
{
|
|
public const int REFLREFRDETAIL = 512;
|
|
public const float SUNSHININESS = 84.0f;
|
|
public const float SUNSTRENGTH = 12.0f;
|
|
public const float REFLREFROFFSET = 0.1f;
|
|
|
|
public bool Initialized;
|
|
public Color WaterColor;
|
|
public Matrix Range;
|
|
|
|
GraphicsDevice Device;
|
|
Plane Plane, UpperBound, LowerBound;
|
|
Vector3 Normal, U, V;
|
|
float Position;
|
|
int GridSizeX, GridSizeY;
|
|
NoiseMaker NoiseMaker;
|
|
Texture2D Fresnel, XYNoise, Reflection, Refraction;
|
|
Surface DepthStencil;
|
|
Effect WaterEffect;
|
|
VertexBuffer Vertices;
|
|
IndexBuffer Indices;
|
|
Camera Camera;
|
|
bool PlaneWithinFrustum;
|
|
|
|
public WaterSurface(GraphicsDevice device, Camera camera, Vector3 normal, float position, int sizeX, int sizeY)
|
|
{
|
|
Vector3 x;
|
|
|
|
Device = device;
|
|
Camera = camera;
|
|
Normal = normal;
|
|
Position = position;
|
|
Plane = new Plane(normal, position);
|
|
NoiseMaker = new NoiseMaker(Device, GridSizeX, GridSizeY);
|
|
PlaneWithinFrustum = false;
|
|
|
|
// Set the initial water color
|
|
WaterColor = Color.Aquamarine;
|
|
|
|
// Calculate the U and V vectors
|
|
if (Math.Abs(Vector3.Dot(Vector3.UnitX, normal)) < Math.Abs(Vector3.Dot(Vector3.UnitY, normal)))
|
|
{
|
|
x = Vector3.UnitX;
|
|
}
|
|
else
|
|
{
|
|
x = Vector3.UnitY;
|
|
}
|
|
|
|
U = x - normal * Vector3.Dot(normal, x);
|
|
U = Vector3.Normalize(U);
|
|
|
|
// Get V (cross)
|
|
V = Vector3.Cross(U, normal);
|
|
|
|
GridSizeX = sizeX + 1;
|
|
GridSizeY = sizeY + 1;
|
|
|
|
SetDisplacementAmplitude(0);
|
|
|
|
if (!InitializeBuffers())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Load the textures
|
|
if ((Fresnel = Texture2D.FromFile(Device, "textures/fresnel_water_linear.bmp")) == null)
|
|
{
|
|
return;
|
|
}
|
|
if ((XYNoise = Texture2D.FromFile(Device, "textures/xynoise.png")) == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Initialize the reflection and refraction textures, and the depth stencil
|
|
Reflection = new Texture2D(Device, REFLREFRDETAIL, REFLREFRDETAIL, 1, ResourceUsage.RenderTarget,
|
|
SurfaceFormat.Color, ResourcePool.Default);
|
|
Refraction = new Texture2D(Device, REFLREFRDETAIL, REFLREFRDETAIL, 1, ResourceUsage.RenderTarget,
|
|
SurfaceFormat.Color, ResourcePool.Default);
|
|
DepthStencil = Device.CreateDepthStencilSurface(REFLREFRDETAIL, REFLREFRDETAIL, DepthFormat.Depth24Stencil8,
|
|
MultiSampleType.None, 0, true);
|
|
|
|
// Load the effect
|
|
CompiledEffect water = Effect.CompileEffectFromFile("shaders/watereffect.fx", null, null,
|
|
CompilerOptions.Debug | CompilerOptions.SkipOptimization, TargetPlatform.Windows);
|
|
if (!water.Success)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
WaterEffect = new Effect(Device, water.GetShaderCode(), CompilerOptions.None, null);
|
|
}
|
|
|
|
Initialized = true;
|
|
}
|
|
|
|
public bool Prepare()
|
|
{
|
|
if (!Initialized) return false;
|
|
|
|
// Check if the water plane is within the viewing frustum
|
|
//BoundingFrustum frustum = new BoundingFrustum(Camera.ViewProjectionMatrix);
|
|
|
|
//if ((frustum.Intersects(UpperBound) == PlaneIntersectionType.Intersecting) ||
|
|
// (frustum.Intersects(LowerBound) == PlaneIntersectionType.Intersecting))
|
|
//{
|
|
// PlaneWithinFrustum = true;
|
|
|
|
// NoiseMaker.RenderGeometry(out Range);
|
|
//}
|
|
|
|
PlaneWithinFrustum = GetMinMax(out Range);
|
|
|
|
if (PlaneWithinFrustum)
|
|
{
|
|
NoiseMaker.RenderGeometry(Range);
|
|
|
|
Vertices.SetData<VertexPositionNormalTexture>(NoiseMaker.Vertices);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void RenderCutter()
|
|
{
|
|
if (PlaneWithinFrustum)
|
|
{
|
|
Device.VertexDeclaration = new VertexDeclaration(Device, VertexPositionNormalTexture.VertexElements);
|
|
Device.Vertices[0].SetSource(Vertices, 0, VertexPositionNormalTexture.SizeInBytes);
|
|
Device.Indices = Indices;
|
|
Device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, GridSizeX * GridSizeY,
|
|
0, 2 * (GridSizeX - 1) * (GridSizeY - 1));
|
|
}
|
|
}
|
|
|
|
public void Render(Vector4 sunVector)
|
|
{
|
|
if (PlaneWithinFrustum)
|
|
{
|
|
Device.RenderState.CullMode = CullMode.None;
|
|
Device.VertexDeclaration = new VertexDeclaration(Device, VertexPositionNormalTexture.VertexElements);
|
|
Device.Vertices[0].SetSource(Vertices, 0, VertexPositionNormalTexture.SizeInBytes);
|
|
Device.Indices = Indices;
|
|
|
|
NoiseMaker.GenerateNormalMap();
|
|
|
|
Device.RenderState.CullMode = CullMode.CullClockwiseFace;
|
|
WaterEffect.Begin(EffectStateOptions.Default);
|
|
WaterEffect.CurrentTechnique.Passes[0].Begin();
|
|
// Beginning of the water rendering pass
|
|
|
|
WaterEffect.Parameters["mViewProj"].SetValue(Camera.ViewProjectionMatrix);
|
|
WaterEffect.Parameters["mView"].SetValue(Camera.ViewMatrix);
|
|
WaterEffect.Parameters["sun_vec"].SetValue(sunVector);
|
|
WaterEffect.Parameters["sun_shininess"].SetValue(SUNSHININESS);
|
|
WaterEffect.Parameters["sun_strength"].SetValue(SUNSTRENGTH);
|
|
WaterEffect.Parameters["reflrefr_offset"].SetValue(REFLREFROFFSET);
|
|
WaterEffect.Parameters["diffuseSkyRef"].SetValue(true);
|
|
WaterEffect.Parameters["watercolour"].SetValue(WaterColor.ToVector4());
|
|
WaterEffect.Parameters["LODbias"].SetValue(0.0f);
|
|
WaterEffect.Parameters["view_position"].SetValue(new Vector4(Camera.Position, 1));
|
|
//WaterEffect.Parameters["EnvironmentMap", SkyboxCubemap);
|
|
WaterEffect.Parameters["FresnelMap"].SetValue(Fresnel);
|
|
WaterEffect.Parameters["Heightmap"].SetValue(NoiseMaker.HeightMap);
|
|
WaterEffect.Parameters["Normalmap"].SetValue(NoiseMaker.NormalMap);
|
|
WaterEffect.Parameters["Refractionmap"].SetValue(Refraction);
|
|
WaterEffect.Parameters["Reflectionmap"].SetValue(Reflection);
|
|
|
|
Device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, GridSizeX * GridSizeY, 0,
|
|
2 * (GridSizeX - 1) * (GridSizeY - 1));
|
|
|
|
// End of the water rendering pass
|
|
WaterEffect.CurrentTechnique.Passes[0].End();
|
|
WaterEffect.End();
|
|
}
|
|
}
|
|
|
|
public void SetDisplacementAmplitude(float amplitude)
|
|
{
|
|
UpperBound = new Plane(Normal, Position + amplitude);
|
|
LowerBound = new Plane(Normal, Position - amplitude);
|
|
}
|
|
|
|
private bool InitializeBuffers()
|
|
{
|
|
if ((Vertices = new VertexBuffer(Device, GridSizeX * GridSizeY * VertexPositionNormalTexture.SizeInBytes,
|
|
ResourceUsage.WriteOnly | ResourceUsage.Dynamic, ResourcePool.Default)) == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ((Indices = new IndexBuffer(Device, sizeof(int) * 6 * (GridSizeX - 1) * (GridSizeY - 1),
|
|
ResourceUsage.WriteOnly, ResourcePool.Default, IndexElementSize.ThirtyTwoBits)) == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Fill the index buffer
|
|
for (int v = 0; v < GridSizeY - 1; v++)
|
|
{
|
|
for (int u = 0; u < GridSizeX - 1; u++)
|
|
{
|
|
int[] indexes = new int[6];
|
|
|
|
// Face 1 |/
|
|
indexes[0] = v * GridSizeX + u;
|
|
indexes[1] = v * GridSizeX + u + 1;
|
|
indexes[2] = (v + 1) * GridSizeX + u;
|
|
|
|
// Face 2 /|
|
|
indexes[3] = (v + 1) * GridSizeX + u;
|
|
indexes[4] = v * GridSizeX + u + 1;
|
|
indexes[5] = (v + 1) * GridSizeX + u + 1;
|
|
|
|
Indices.SetData<int>(indexes);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool GetMinMax(out Matrix range)
|
|
{
|
|
// FIXME:
|
|
range = new Matrix();
|
|
return false;
|
|
}
|
|
|
|
/*private bool GetMinMax(out Matrix range)
|
|
{
|
|
BoundingFrustum frustum = new BoundingFrustum(Camera.ViewMatrix);
|
|
Vector3[] corners = frustum.GetCorners();
|
|
range = Matrix.CreateOrthographic(frustum.
|
|
|
|
float xmin, ymin, xmax, ymax;
|
|
// Frustum to check the camera against
|
|
//Vector3[] frustum = new Vector3[8];
|
|
Vector3[] projPoints = new Vector3[24];
|
|
int npoints = 0;
|
|
// Which frustum points are connected together?
|
|
//int[] cube = { 0,1, 0,2, 2,3, 1,3,
|
|
// 0,4, 2,6, 3,7, 1,5,
|
|
// 4,6, 4,5, 5,7, 6,7
|
|
// };
|
|
|
|
// Transform frustum points to worldspace
|
|
//Matrix invView = Matrix.Invert(Camera.ViewMatrix);
|
|
//frustum[0] = Vector3.Transform(new Vector3(-1, -1, -1), invView);
|
|
//frustum[1] = Vector3.Transform(new Vector3(+1, -1, -1), invView);
|
|
//frustum[2] = Vector3.Transform(new Vector3(-1, +1, -1), invView);
|
|
//frustum[3] = Vector3.Transform(new Vector3(+1, +1, -1), invView);
|
|
//frustum[4] = Vector3.Transform(new Vector3(-1, -1, +1), invView);
|
|
//frustum[5] = Vector3.Transform(new Vector3(+1, -1, +1), invView);
|
|
//frustum[6] = Vector3.Transform(new Vector3(-1, +1, +1), invView);
|
|
//frustum[7] = Vector3.Transform(new Vector3(+1, +1, +1), invView);
|
|
|
|
// Check intersections with UpperBound and LowerBound
|
|
for (int i = 0; i < 12; i++)
|
|
{
|
|
int src = cube[i * 2];
|
|
int dst = cube[i * 2 + 1];
|
|
|
|
// FIXME: Since Z is up in our world we may need to fix some things here
|
|
if ((UpperBound.A * frustum[src].X + UpperBound.B * frustum[src].Y + UpperBound.C * frustum[src].Z + UpperBound.D * 1) /
|
|
(UpperBound.A * frustum[dst].X + UpperBound.B * frustum[dst].Y + UpperBound.C * frustum[dst].Z + UpperBound.D * 1) < 0)
|
|
{
|
|
BoundingFrustum f = new BoundingFrustum(
|
|
//projPoints[npoints++] =
|
|
Plane.Intersects(
|
|
D3DXPlaneIntersectLine(&proj_points[n_points++], &upper_bound, &frustum[src], &frustum[dst]);
|
|
}
|
|
if ((LowerBound.A * frustum[src].X + LowerBound.B * frustum[src].Y + LowerBound.C * frustum[src].Z + LowerBound.D * 1) /
|
|
(LowerBound.A * frustum[dst].X + LowerBound.B * frustum[dst].Y + LowerBound.C * frustum[dst].Z + LowerBound.D * 1) < 0)
|
|
{
|
|
D3DXPlaneIntersectLine(&proj_points[n_points++], &lower_bound, &frustum[src], &frustum[dst]);
|
|
}
|
|
}
|
|
|
|
// Check if any of the frustums vertices lie between the upper_bound and lower_bound planes
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if ((UpperBound.A * frustum[i].X + UpperBound.B * frustum[i].Y + UpperBound.C * frustum[i].Z + UpperBound.D * 1) /
|
|
(LowerBound.A * frustum[i].X + LowerBound.B * frustum[i].Y + LowerBound.C * frustum[i].Z + LowerBound.D * 1) < 0)
|
|
{
|
|
projPoints[npoints++] = frustum[i];
|
|
}
|
|
}
|
|
|
|
// TODO: Advanced camera stuff?
|
|
//
|
|
|
|
for (int i = 0; i < npoints; i++)
|
|
{
|
|
projPoints[i] = Vector3.Transform(projPoints[i], Camera.ViewMatrix);
|
|
projPoints[i] = Vector3.Transform(projPoints[i], Camera.ProjectionMatrix);
|
|
}
|
|
|
|
// Get max/min x & y-values to determine how big the "projection window" must be
|
|
if (npoints > 0)
|
|
{
|
|
xmin = projPoints[0].X;
|
|
xmax = projPoints[0].X;
|
|
ymin = projPoints[0].Y;
|
|
ymax = projPoints[0].Y;
|
|
|
|
for(int i = 1; i < npoints; i++)
|
|
{
|
|
if (projPoints[i].X > xmax) xmax = projPoints[i].X;
|
|
if (projPoints[i].X < xmin) xmin = projPoints[i].X;
|
|
if (projPoints[i].Y > ymax) ymax = projPoints[i].Y;
|
|
if (projPoints[i].Y < ymin) ymin = projPoints[i].Y;
|
|
}
|
|
|
|
// Build the packing matrix that spreads the grid across the "projection window"
|
|
Matrix pack = new Matrix(xmax-xmin, 0, 0, xmin,
|
|
0, ymax-ymin, 0, ymin,
|
|
0, 0, 1, 0,
|
|
0, 0, 0, 1);
|
|
|
|
pack = Matrix.Transpose(pack);
|
|
range = pack * invView;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}*/
|
|
|
|
private bool WithinFrustum(Vector3 position)
|
|
{
|
|
Vector3 test = Vector3.Transform(position, Camera.ViewProjectionMatrix);
|
|
|
|
if ((Math.Abs(test.X) < 1.00001f) && (Math.Abs(test.Y) < 1.00001f) && (Math.Abs(test.Z) < 1.00001f))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|