Bene, il paesaggio sembra avviarsi sempre di più verso una conclusione. Che ne dite se dessimo uno sguardo più da vicino?
Nella fattispecie, avrei intenzione di camminarci sopra.
Una bella camminata
In verità non cammineremo veramente sul terreno, ma simuleremo una camminata. L'idea di base è molto semplice: quando ci troviamo
in un punto del terreno, bisogna impostare la coordinata y della telecamera poco sopra quel punto. Facile, direte. Allora sperimentiamo
la cosa prima diminuendo un po' MoveSpeed e poi modificando la procedura UpdatePosition, nella regione "Gestione telecamera":
PrivateSub UpdatePosition(ByVal Motion As Vector3)
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim RotMotion As Vector3 = Vector3.Transform(Motion, Rotation)
CameraPosition += RotMotion * MoveSpeed
'Imposta la Y prendendola dalla height map, e aggiungendo un piccolo
'fattore che permette di capire che siamo sopra il terreno
CameraPosition.Y = HeightData(CameraPosition.X, _
CameraPosition.Y) + 0.3F
UpdateView()
EndSub
Avremo subito un errore di tipo IndexOutOfRangeException. In effetti, se ci pensiamo bene, la telecamera potrebbe anche trovarsi fuori dal
terreno. Beh, non è un problema: noi ci muoveremo dentro di esso, e, qualora andassimo fuori, lasceremmo la liberà di movimento
che c'era prima. Basta inserire un blocco Try per fermare l'eccezione:
PrivateSub UpdatePosition(ByVal Motion As Vector3)
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim RotMotion As Vector3 = Vector3.Transform(Motion, Rotation)
CameraPosition += RotMotion * MoveSpeed
Try
'CInt rende intere le variabili decimali
CameraPosition.Y = HeightData(CInt(CameraPosition.X), _
CInt(-CameraPosition.Z)) + 0.3F
Catch Ex As Exception
EndTry
UpdateView()
EndSub
Fate una prova. Non otterrete niente, ma noterete che se andate nell'angolo superiore destro del terreno, la vostra posizione su y cambia.
Infatti, ci siamo dimenticati che le coordinate X e Z sono modificate: il terreno è stato, infatti, traslato verso l'orgine degli
assi. Per ottenere le X e Z che corrispondono ai valori in HeightData, dobbiamo effettuare una trasformazione inversa alla traslazione
originaria:
PrivateSub UpdatePosition(ByVal Motion As Vector3)
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim RotMotion As Vector3 = Vector3.Transform(Motion, Rotation)
CameraPosition += RotMotion * MoveSpeed
Try
CameraPosition.Y = HeightData( _
CInt(CameraPosition.X + Me.TerrainWidth / 2), _
CInt(-CameraPosition.Z + Me.TerrainLength / 2)) + 0.5F
Catch Ex As Exception
EndTry
UpdateView()
EndSub
Ora possiamo muoverci, ma il movimento è qualitativamente molto scadente: procede a scatti e ci fa vedere attraverso il terreno. Non
è molto bello. Come si fa per procedere oltre?
Interpolazione Bilineare
Esiste una funzione matematica chiamata Interpolazione Bilineare che, in una funzione a tre dimensioni, dati in input quattro punti
sul piano e le loro altezze è capace di approssimare con ottima precisione l'altezza di un punto qualsiasi compreso tra i quattro
forniti. È quello che fa al caso nostro, infatti noi abbiamo solo una griglia di 256x256 valori. Possiamo sapere l'altezza del punto
(0,0) e del punto (1,1), perchè le leggiamo dalla height map, ma non possiamo dire niente sull'altezza del punto (0.5,0.5), ad esempio.
Per calcolarla occorre proprio l'interpolazione bilineare:
PrivateSub UpdatePosition(ByVal Motion As Vector3)
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim RotMotion As Vector3 = Vector3.Transform(Motion, Rotation)
CameraPosition += RotMotion * MoveSpeed
TryDim X1, X2, Z1, Z2 As Int32
With CameraPosition
'Arrotonda per eccesso e per difetto X e Z, ossia
'prende i quattro punti che circondano l'attuale
'posizione della telecamera sul piano
X1 = CInt(Math.Floor(.X))
X2 = CInt(Math.Ceiling(.X))
Z1 = CInt(Math.Floor(.Z))
Z2 = CInt(Math.Ceiling(.Z))
EndWith
'Se la telecamera si trova su un punto esattamente coincidente
'con un elemento di heightdata, l'interpolazione bilineare non
'funziona (infatti il denominatore della frazione diventa 0).
'Allora la prende direttamente dalla matriceIf X1 <> X2 And Z1 <> Z2 ThenDim Q11, Q12, Q21, Q22 AsSingle
'Calcola le altezze corrispondenti ai quattro punti
'prelevati prima. Ho usato Q perchè su wikipedia
'la formula usa un Q, ma in questo specifico caso si
'poteva anche mettere Y
Q11 = HeightData(X1 + Me.TerrainWidth / 2, _
-Z1 + Me.TerrainLength / 2)
Q12 = HeightData(X1 + Me.TerrainWidth / 2, _
-Z2 + Me.TerrainLength / 2)
Q21 = HeightData(X2 + Me.TerrainWidth / 2, _
-Z1 + Me.TerrainLength / 2)
Q22 = HeightData(X2 + Me.TerrainWidth / 2, _
-Z2 + Me.TerrainLength / 2)
Dim X, Y, Z AsSingle
X = CameraPosition.X
Z = CameraPosition.Z
'Esegue l'interpolazione bilineare
Y = (Q11 * (X2 - X) * (Z2 - Z) + _
Q21 * (X - X1) * (Z2 - Z) + _
Q12 * (X2 - X) * (Z - Z1) + _
Q22 * (X - X1) * (Z - Z1)) / ((X2 - X1) * (Z2 - Z1))
CameraPosition.Y = Y + 0.5F
Else
CameraPosition.Y = HeightData(X1 + Me.TerrainWidth / 2, _
-Z1 + Me.TerrainLength / 2)
EndIfCatch Ex As Exception
EndTry
UpdateView()
EndSub
Nel codice alla fine del capitolo ho sostituito il Try con un If, poiché il secondo usa meno memoria.
Imports Microsoft.Xna.Framework
Imports Microsoft.Xna.Framework.Input
Imports Microsoft.Xna.Framework.Graphics
Imports System.Runtime.InteropServices
PublicClass Game
Inherits Microsoft.Xna.Framework.Game
#Region "Strutture aggiuntive"
PrivateShared DoubleSize AsByte = Marshal.SizeOf(GetType(Double))
PrivateStructure VertexPositionNormalColor
Public Position As Vector3
Public Normal As Vector3
Public Color As Color
PublicShared SizeInBytes As Int16 = 7 * 4
PublicShared VertexElements() As VertexElement = New VertexElement() _
{ _
New VertexElement(0, 0, VertexElementFormat.Vector3, _
VertexElementMethod.Default, _
VertexElementUsage.Position, 0), _
New VertexElement(0, DoubleSize * 3, _
VertexElementFormat.Color, _
VertexElementMethod.Default, _
VertexElementUsage.Color, 0), _
New VertexElement(0, DoubleSize * 4, _
VertexElementFormat.Vector3, _
VertexElementMethod.Default, _
VertexElementUsage.Normal, 0) _
}
EndStructurePrivateStructure VertexPositionNormalMultitexture
Public Position As Vector3
Public Normal As Vector3
Public TextureCoordinate As Vector4
Public TextureWeight As Vector4
PublicShared SizeInBytes As Int16 = (3 + 3 + 4 + 4) * 4
PublicShared VertexElements As VertexElement() = New VertexElement() _
{ _
New VertexElement(0, 0, VertexElementFormat.Vector3, _
VertexElementMethod.Default, _
VertexElementUsage.Position, 0), _
New VertexElement(0, 4 * 3, _
VertexElementFormat.Vector3, _
VertexElementMethod.Default, _
VertexElementUsage.Normal, 0), _
New VertexElement(0, 4 * 6, _
VertexElementFormat.Vector4, _
VertexElementMethod.Default, _
VertexElementUsage.TextureCoordinate, 0), _
New VertexElement(0, 4 * 10, _
VertexElementFormat.Vector4, _
VertexElementMethod.Default, _
VertexElementUsage.TextureCoordinate, 1) _
}
EndStructurePrivateStructure SkyCubeFace
Public Vertices() As VertexPositionTexture
Public Indices() As Int32
Public Texture As Texture2D
EndStructure#End Region#Region "Variabili globali"
Private AppPath AsString = _
My.Application.Info.DirectoryPath
Private Graphics As GraphicsDeviceManager
'VERTICI, INDICI E BUFFER ------------------------Private Shader As Effect
Private Vertices() As VertexPositionNormalMultitexture
Private Indices() As Int32
Private VBuffer As VertexBuffer
Private IBuffer As IndexBuffer
Private VDeclaration As VertexDeclaration
Private SDeclaration As VertexDeclaration
Private WDeclaration As VertexDeclaration
'MATRICI -----------------------------------------Private ViewMatrix As Matrix
Private ProjectionMatrix As Matrix
Private WorldMatrix As Matrix
Private ReflectionViewMatrix As Matrix
'HEIGHTDATA --------------------------------------Private HeightData(,) AsSinglePrivateConst MinHeight AsSingle = 0
PrivateConst MaxHeight AsSingle = 30
Private TerrainWidth, TerrainLength As Int32
'TELECAMERA -------------------------------------Private PrevMouseState As MouseState
Private LeftRightRotation AsSingle = MathHelper.PiOver2
Private UpDownRotation AsSingle = -MathHelper.Pi / 10.0F
Private CameraPosition As Vector3
Private RotationSpeed AsSingle = 0.3
Private MoveSpeed AsSingle = 0.08
'TEXTURE ----------------------------------------Private Grass As Texture2D
Private Sand As Texture2D
Private Rock As Texture2D
Private Snow As Texture2D
'CIELO ------------------------------------------Private SkyFaces(4) As Texture2D
Private SkyCube(4) As SkyCubeFace
'ACQUA ------------------------------------------Private WaterHeight AsSingle = 5.0
Private RefractionTarget As RenderTarget2D
Private RefractionMap As Texture2D
Private ReflectionTarget As RenderTarget2D
Private ReflectionMap As Texture2D
Private WaterVertices() As VertexPositionTexture
Private WaterVBuffer As VertexBuffer
Private BumpMap As Texture2D
#End Region#Region "Funzioni utili"
PrivateFunction GetEffect(ByVal FileName AsString) As Effect
Dim CompEffect As CompiledEffect = _
Effect.CompileEffectFromFile(FileName, _
Nothing, Nothing, _
CompilerOptions.None, _
TargetPlatform.Windows)
ReturnNew Effect(Me.GraphicsDevice, _
CompEffect.GetEffectCode, _
CompilerOptions.None, Nothing)
EndFunctionPrivateFunction GetTexture(ByVal FileName AsString)
Return Texture2D.FromFile(Me.GraphicsDevice, FileName)
EndFunction#End Region#Region "Caricamento Heightmap"
PrivateSub LoadHeightMap(ByVal HeightMap As Texture2D)
TerrainWidth = HeightMap.Width
TerrainLength = HeightMap.Height
Dim Colors(TerrainWidth * TerrainLength - 1) As Color
HeightMap.GetData(Colors)
For I As Int32 = 0 To TerrainWidth - 1
ReDim HeightData(I, TerrainLength - 1)
NextFor X As Int32 = 0 To TerrainWidth - 1
For Z As Int32 = 0 To TerrainLength - 1
HeightData(X, Z) = MinHeight + _
(Colors(X + Z * TerrainWidth).R / 255) * _
(MaxHeight - MinHeight)
NextNext
VDeclaration = New VertexDeclaration(Me.GraphicsDevice, _
VertexPositionNormalMultitexture.VertexElements)
EndSubPrivateSub SetVertices()
ReDim Vertices(TerrainWidth * TerrainLength - 1)
Dim Strip AsSingle = (MaxHeight - MinHeight) / 4
For X As Int32 = 0 To TerrainWidth - 1
For Z As Int32 = 0 To TerrainLength - 1
With Vertices(X + Z * TerrainWidth)
Dim Y AsSingle = HeightData(X, Z)
.Position = New Vector3(X, Y, -Z)
.TextureCoordinate.X = X / 32
.TextureCoordinate.Y = Z / 32
.TextureWeight.X = MathHelper.Clamp(1.0F - Math.Abs(Y - 0) / 8.0F, 0, 1)
.TextureWeight.Y = MathHelper.Clamp(1.0F - Math.Abs(Y - 12) / 6.0F, 0, 1)
.TextureWeight.Z = MathHelper.Clamp(1.0F - Math.Abs(Y - 20) / 6.0F, 0, 1)
.TextureWeight.W = MathHelper.Clamp(1.0F - Math.Abs(Y - 30) / 6.0F, 0, 1)
Dim Total AsSingle = .TextureWeight.X
Total += .TextureWeight.Y
Total += .TextureWeight.Z
Total += .TextureWeight.W
.TextureWeight.X /= Total
.TextureWeight.Y /= Total
.TextureWeight.Z /= Total
.TextureWeight.W /= Total
EndWithNextNextEndSubPrivateSub SetIndices()
ReDim Indices((TerrainWidth - 1) * (TerrainLength - 1) * 6 - 1)
Dim Counter As Int32 = 0
For X As Int32 = 0 To TerrainWidth - 2
For Z As Int32 = 0 To TerrainLength - 2
Dim LowerLeft As Int16 = X + Z * TerrainWidth
Dim LowerRight As Int16 = (X + 1) + Z * TerrainWidth
Dim TopLeft As Int16 = X + (Z + 1) * TerrainWidth
Dim TopRight As Int16 = (X + 1) + (Z + 1) * TerrainWidth
Indices(Counter) = TopLeft
Indices(Counter + 1) = LowerRight
Indices(Counter + 2) = LowerLeft
Counter += 3
Indices(Counter) = TopLeft
Indices(Counter + 1) = TopRight
Indices(Counter + 2) = LowerRight
Counter += 3
NextNextEndSubPrivateSub SetNormals()
For I As Int32 = 0 To Vertices.Length - 1
Vertices(I).Normal = New Vector3(0, 0, 0)
NextFor I As Int16 = 0 To (Indices.Length / 3) - 1
Dim Index1 As Int16 = Indices(I * 3)
Dim Index2 As Int16 = Indices(I * 3 + 1)
Dim Index3 As Int16 = Indices(I * 3 + 2)
Dim Side1 As Vector3 = _
Vertices(Index1).Position - Vertices(Index3).Position
Dim Side2 As Vector3 = _
Vertices(Index1).Position - Vertices(Index2).Position
Dim Normal As Vector3 = Vector3.Cross(Side1, Side2)
Vertices(Index1).Normal += Normal
Vertices(Index2).Normal += Normal
Vertices(Index3).Normal += Normal
NextForEach V As VertexPositionNormalMultitexture In Vertices
V.Normal.Normalize()
NextEndSubPrivateSub CopyToBuffer()
VBuffer = New VertexBuffer(Me.GraphicsDevice, _
Vertices.Length * VertexPositionNormalMultitexture.SizeInBytes, _
BufferUsage.WriteOnly)
VBuffer.SetData(Vertices)
IBuffer = New IndexBuffer(Me.GraphicsDevice, _
GetType(Int32), Indices.Length, BufferUsage.WriteOnly)
IBuffer.SetData(Indices)
Me.GraphicsDevice.Indices = IBuffer
Me.GraphicsDevice.Vertices(0).SetSource( _
VBuffer, 0, VertexPositionNormalMultitexture.SizeInBytes)
EndSub#End Region#Region "Cielo"
PrivateFunction GetElements(Of T)(ByVal Sources() As T, ByVal ParamArray Indices() As Int32) As T()
Dim Result(Indices.Length - 1) As T
For I As Int16 = 0 To Indices.Length - 1
Result(I) = Sources(Indices(I))
NextReturn Result
EndFunctionPrivateSub LoadSkyDome()
Dim SkyVertices(7) As VertexPositionTexture
Dim HalfSize As Int16 = 200
Dim HalfHeight As Int16 = 150
SkyVertices(0).Position = New Vector3(-HalfSize, 200, HalfSize)
SkyVertices(1).Position = New Vector3(-HalfSize, 200, -HalfSize)
SkyVertices(2).Position = New Vector3(HalfSize, 200, HalfSize)
SkyVertices(3).Position = New Vector3(HalfSize, 200, -HalfSize)
SkyVertices(4).Position = New Vector3(-HalfSize, 0, HalfSize)
SkyVertices(5).Position = New Vector3(-HalfSize, 0, -HalfSize)
SkyVertices(6).Position = New Vector3(HalfSize, 0, HalfSize)
SkyVertices(7).Position = New Vector3(HalfSize, 0, -HalfSize)
SkyCube(0).Vertices = GetElements(SkyVertices, 0, 1, 2, 3)
SkyCube(1).Vertices = GetElements(SkyVertices, 5, 1, 4, 0)
SkyCube(2).Vertices = GetElements(SkyVertices, 4, 0, 6, 2)
SkyCube(3).Vertices = GetElements(SkyVertices, 6, 2, 7, 3)
SkyCube(4).Vertices = GetElements(SkyVertices, 7, 3, 5, 1)
With SkyCube(0)
.Indices = New Int32() {0, 1, 2, 2, 1, 3}
.Texture = SkyFaces(0)
.Vertices(0).TextureCoordinate = New Vector2(0, 1)
.Vertices(1).TextureCoordinate = New Vector2(0, 0)
.Vertices(2).TextureCoordinate = New Vector2(1, 1)
.Vertices(3).TextureCoordinate = New Vector2(1, 0)
EndWithFor I AsByte = 1 To 4
With SkyCube(I)
.Indices = New Int32() {0, 1, 2, 2, 1, 3}
.Texture = SkyFaces(I)
.Vertices(0).TextureCoordinate = New Vector2(0, 0.5)
.Vertices(1).TextureCoordinate = New Vector2(0, 0)
.Vertices(2).TextureCoordinate = New Vector2(1, 0.5)
.Vertices(3).TextureCoordinate = New Vector2(1, 0)
EndWithNext
SDeclaration = New VertexDeclaration(Me.GraphicsDevice, VertexPositionTexture.VertexElements)
EndSubPrivateSub DrawSky(ByVal View As Matrix)
Dim Prev As CullMode = Me.GraphicsDevice.RenderState.CullMode
Me.GraphicsDevice.RenderState.CullMode = CullMode.None
For I AsByte = 0 To 4
Shader.CurrentTechnique = Shader.Techniques("Textured")
Shader.Parameters("View").SetValue(View)
Shader.Parameters("Projection").SetValue(ProjectionMatrix)
Shader.Parameters("World").SetValue(Matrix.Identity)
Shader.Parameters("LightEnabled").SetValue(False)
Shader.Parameters("ATexture").SetValue(SkyFaces(I))
Shader.Begin()
ForEach Pass As EffectPass In Shader.CurrentTechnique.Passes
Pass.Begin()
WithMe.GraphicsDevice
.VertexDeclaration = SDeclaration
.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, SkyCube(I).Vertices, 0, SkyCube(I).Vertices.Length, SkyCube(I).Indices, 0, SkyCube(I).Indices.Length / 3)
EndWith
Pass.End()
Next
Shader.End()
NextMe.GraphicsDevice.RenderState.CullMode = Prev
EndSub#End Region#Region "Acqua"
PrivateFunction CreatePlane(ByVal Height AsSingle, ByVal View As Matrix, _
ByVal Normal As Vector3, ByVal ClipAbove AsBoolean)
Normal.Normalize()
Dim PlaneCoef AsNew Vector4(Normal, Height)
Dim HSMatrix As Matrix = _
Matrix.Transpose( _
Matrix.Invert(View * ProjectionMatrix))
If ClipAbove Then
PlaneCoef *= -1
EndIf
PlaneCoef = Vector4.Transform(PlaneCoef, HSMatrix)
ReturnNew Plane(PlaneCoef)
EndFunctionPrivateSub DrawRefractionMap()
Dim RefractionPlane As Plane = _
CreatePlane(WaterHeight + 1.5F, ViewMatrix, New Vector3(0, -1, 0), False)
Me.GraphicsDevice.ClipPlanes(0).Plane = RefractionPlane
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = True
Me.GraphicsDevice.SetRenderTarget(0, RefractionTarget)
Me.GraphicsDevice.Clear(ClearOptions.Target Or ClearOptions.DepthBuffer, Color.Black, 1.0F, 0)
DrawTerrain(ViewMatrix)
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = False
Me.GraphicsDevice.SetRenderTarget(0, Nothing)
RefractionMap = RefractionTarget.GetTexture()
Me.GraphicsDevice.Clear(Color.CornflowerBlue)
EndSubPrivateSub DrawReflectionMap()
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim Forward AsNew Vector3(0, 0, -1)
Dim Right AsNew Vector3(1, 0, 0)
Dim TargetPos As Vector3 = _
CameraPosition + Vector3.Transform(Forward, Rotation)
Dim ReflectionPos As Vector3 = CameraPosition
Dim ReflectionTargetPos As Vector3 = TargetPos
ReflectionPos.Y = 2 * WaterHeight - ReflectionPos.Y
ReflectionTargetPos.Y = 2 * WaterHeight - ReflectionTargetPos.Y
Dim ReflectionUp As Vector3 = Vector3.Cross( _
Vector3.Transform(Right, Rotation), ReflectionTargetPos - ReflectionPos)
ReflectionViewMatrix = Matrix.CreateLookAt( _
ReflectionPos, ReflectionTargetPos, ReflectionUp)
Dim ReflectionPlane As Plane = _
CreatePlane(WaterHeight - 0.5F, ReflectionViewMatrix, New Vector3(0, -1, 0), True)
Me.GraphicsDevice.ClipPlanes(0).Plane = ReflectionPlane
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = True
Me.GraphicsDevice.SetRenderTarget(0, ReflectionTarget)
Me.GraphicsDevice.Clear(ClearOptions.Target Or _
ClearOptions.DepthBuffer, Color.Black, 1.0F, 0)
DrawTerrain(ReflectionViewMatrix)
DrawSky(ReflectionViewMatrix)
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = False
Me.GraphicsDevice.SetRenderTarget(0, Nothing)
ReflectionMap = ReflectionTarget.GetTexture()
Me.GraphicsDevice.Clear(Color.CornflowerBlue)
EndSubPrivateSub SetWaterVertices()
ReDim WaterVertices(5)
Dim HalfSize As Int16 = 400
WaterVertices(0).Position = New Vector3(-HalfSize, WaterHeight, HalfSize)
WaterVertices(1).Position = New Vector3(-HalfSize, WaterHeight, -HalfSize)
WaterVertices(2).Position = New Vector3(HalfSize, WaterHeight, HalfSize)
WaterVertices(3).Position = New Vector3(HalfSize, WaterHeight, HalfSize)
WaterVertices(4).Position = New Vector3(-HalfSize, WaterHeight, -HalfSize)
WaterVertices(5).Position = New Vector3(HalfSize, WaterHeight, -HalfSize)
WaterVertices(0).TextureCoordinate = New Vector2(0, 1)
WaterVertices(1).TextureCoordinate = New Vector2(0, 0)
WaterVertices(2).TextureCoordinate = New Vector2(1, 1)
WaterVertices(3).TextureCoordinate = New Vector2(1, 1)
WaterVertices(4).TextureCoordinate = New Vector2(0, 0)
WaterVertices(5).TextureCoordinate = New Vector2(1, 0)
WaterVBuffer = New VertexBuffer(Me.GraphicsDevice, _
WaterVertices.Length * VertexPositionTexture.SizeInBytes, _
BufferUsage.WriteOnly)
WaterVBuffer.SetData(WaterVertices)
WDeclaration = New VertexDeclaration(Me.GraphicsDevice, VertexPositionTexture.VertexElements)
EndSubPrivateSub DrawWater(ByVal Time AsSingle)
Shader.CurrentTechnique = Shader.Techniques("Water")
Shader.Parameters("View").SetValue(ViewMatrix)
Shader.Parameters("Projection").SetValue(ProjectionMatrix)
Shader.Parameters("World").SetValue(Matrix.Identity)
Shader.Parameters("ReflectionView").SetValue(ReflectionViewMatrix)
Shader.Parameters("RefractionMap").SetValue(RefractionMap)
Shader.Parameters("ReflectionMap").SetValue(ReflectionMap)
Shader.Parameters("BumpMap").SetValue(BumpMap)
Shader.Parameters("WaveLength").SetValue(0.01F)
Shader.Parameters("WaveHeight").SetValue(0.3F)
Shader.Parameters("CameraPosition").SetValue(CameraPosition)
Shader.Parameters("Time").SetValue(Time)
Shader.Parameters("WindDirection").SetValue(New Vector3(0, 0, 1))
Shader.Parameters("WindForce").SetValue(0.0002F)
Dim Prev As CullMode = Me.GraphicsDevice.RenderState.CullMode
Me.GraphicsDevice.RenderState.CullMode = CullMode.None
Me.GraphicsDevice.Vertices(0).SetSource( _
WaterVBuffer, 0, VertexPositionTexture.SizeInBytes)
Shader.Begin()
ForEach Pass As EffectPass In Shader.CurrentTechnique.Passes
Pass.Begin()
WithMe.GraphicsDevice
.VertexDeclaration = WDeclaration
Me.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, WaterVertices.Length / 3)
EndWith
Pass.End()
Next
Shader.End()
Me.GraphicsDevice.RenderState.CullMode = Prev
EndSub#End Region#Region "Gestione telecamera"
PrivateSub ProcessMouseInput(ByVal ElapsedTime AsSingle)
Dim CurrentMouseState As MouseState = Mouse.GetState
If CurrentMouseState <> PrevMouseState ThenDim DX AsSingle = _
CurrentMouseState.X - PrevMouseState.X
Dim DY AsSingle = _
CurrentMouseState.Y - PrevMouseState.Y
LeftRightRotation -= DX * RotationSpeed * ElapsedTime
UpDownRotation -= DY * RotationSpeed * ElapsedTime
Mouse.SetPosition(Me.GraphicsDevice.Viewport.Width / 2, _
Me.GraphicsDevice.Viewport.Height / 2)
UpdateView()
EndIfEndSubPrivateSub ProcessKeyboardInput()
Dim Motion AsNew Vector3(0, 0, 0)
Dim KeyState As KeyboardState = Keyboard.GetState
If KeyState.IsKeyDown(Keys.W) Then
Motion += New Vector3(0, 0, -1)
EndIfIf KeyState.IsKeyDown(Keys.S) Then
Motion += New Vector3(0, 0, 1)
EndIfIf KeyState.IsKeyDown(Keys.D) Then
Motion += New Vector3(1, 0, 0)
EndIfIf KeyState.IsKeyDown(Keys.A) Then
Motion += New Vector3(-1, 0, 0)
EndIfIf KeyState.IsKeyDown(Keys.Up) Then
Motion += New Vector3(0, 1, 0)
EndIfIf KeyState.IsKeyDown(Keys.Down) Then
Motion += New Vector3(0, -1, 0)
EndIf
UpdatePosition(Motion)
EndSubPrivateSub UpdateView()
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim Forward AsNew Vector3(0, 0, -1)
Dim Up AsNew Vector3(0, 1, 0)
Dim RotForward As Vector3 = _
Vector3.Transform(Forward, Rotation)
Dim RotUp As Vector3 = Vector3.Transform(Up, Rotation)
Dim Target As Vector3 = CameraPosition + RotForward
ViewMatrix = Matrix.CreateLookAt(CameraPosition, Target, RotUp)
EndSubPrivateSub UpdatePosition(ByVal Motion As Vector3)
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim RotMotion As Vector3 = Vector3.Transform(Motion, Rotation)
CameraPosition += RotMotion * MoveSpeed
If (Math.Abs(CameraPosition.X) <= Me.TerrainWidth / 2) And _
(Math.Abs(CameraPosition.Z) <= Me.TerrainLength / 2) ThenDim X1, X2, Z1, Z2 As Int32
With CameraPosition
X1 = CInt(Math.Floor(.X))
X2 = CInt(Math.Ceiling(.X))
Z1 = CInt(Math.Floor(.Z))
Z2 = CInt(Math.Ceiling(.Z))
EndWithIf X1 <> X2 And Z1 <> Z2 ThenDim Q11, Q12, Q21, Q22 AsSingle
Q11 = HeightData(X1 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
Q12 = HeightData(X1 + Me.TerrainWidth / 2, -Z2 + Me.TerrainLength / 2)
Q21 = HeightData(X2 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
Q22 = HeightData(X2 + Me.TerrainWidth / 2, -Z2 + Me.TerrainLength / 2)
Dim X, Y, Z AsSingle
X = CameraPosition.X
Z = CameraPosition.Z
Y = (Q11 * (X2 - X) * (Z2 - Z) + _
Q21 * (X - X1) * (Z2 - Z) + _
Q12 * (X2 - X) * (Z - Z1) + _
Q22 * (X - X1) * (Z - Z1)) / ((X2 - X1) * (Z2 - Z1))
CameraPosition.Y = Y + 0.5F
Else
CameraPosition.Y = HeightData(X1 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
EndIfEndIf
UpdateView()
EndSub#End Region#Region "Gestione risorse"
SubNew()
Me.Graphics = New GraphicsDeviceManager(Me)
Me.Content.RootDirectory = "content"
EndSubProtectedOverridesSub Initialize()
MyBase.Initialize()
EndSubProtectedOverridesSub LoadContent()
Shader = GetEffect(AppPath & "\SimpleShader.fx")
Grass = GetTexture(AppPath & "\grass.dds")
Sand = GetTexture(AppPath & "\sand.dds")
Rock = GetTexture(AppPath & "\rock.dds")
Snow = GetTexture(AppPath & "\snow.dds")
BumpMap = GetTexture(AppPath & "\BumpMap.jpg")
For I AsByte = 0 To 4
SkyFaces(I) = GetTexture(AppPath & "\skyface" & (I + 1) & ".png")
NextDim Params As PresentationParameters = _
Me.GraphicsDevice.PresentationParameters
RefractionTarget = New RenderTarget2D(Me.GraphicsDevice, _
Params.BackBufferWidth, Params.BackBufferHeight, 1, _
Me.GraphicsDevice.DisplayMode.Format)
ReflectionTarget = New RenderTarget2D(Me.GraphicsDevice, _
Params.BackBufferWidth, Params.BackBufferHeight, 1, _
Me.GraphicsDevice.DisplayMode.Format)
LoadHeightMap(GetTexture(AppPath & "\HeightMap.bmp"))
LoadSkyDome()
SetVertices()
SetIndices()
SetNormals()
SetWaterVertices()
CopyToBuffer()
ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView( _
MathHelper.PiOver4, _
Me.GraphicsDevice.Viewport.AspectRatio, _
0.1, 1000)
CameraPosition = New Vector3(0, 40, TerrainLength / 2)
Mouse.SetPosition(Me.GraphicsDevice.Viewport.Width / 2, _
Me.GraphicsDevice.Viewport.Height / 2)
PrevMouseState = Mouse.GetState
UpdateView()
MyBase.LoadContent()
EndSubProtectedOverridesSub UnloadContent()
MyBase.UnloadContent()
EndSub#End Region#Region "Aggiornamento mondo 3D"
PrivateSub DrawTerrain(ByVal View As Matrix)
Shader.CurrentTechnique = Shader.Techniques("MultiTextured")
Shader.Parameters("View").SetValue(View)
Shader.Parameters("Projection").SetValue(ProjectionMatrix)
Shader.Parameters("World").SetValue( _
Matrix.CreateTranslation( _
New Vector3(-TerrainWidth / 2, 0, TerrainLength / 2)))
Dim LightDirection AsNew Vector3(27.73, -17.67, 6.14)
LightDirection.Normalize()
Shader.Parameters("LightEnabled").SetValue(True)
Shader.Parameters("LightDirection").SetValue(LightDirection)
Shader.Parameters("AmbientFactor").SetValue(0.3F)
Shader.Parameters("Texture0").SetValue(Sand)
Shader.Parameters("Texture1").SetValue(Grass)
Shader.Parameters("Texture2").SetValue(Rock)
Shader.Parameters("Texture3").SetValue(Snow)
Me.GraphicsDevice.Indices = IBuffer
Me.GraphicsDevice.Vertices(0).SetSource( _
VBuffer, 0, VertexPositionNormalMultitexture.SizeInBytes)
Shader.Begin()
ForEach Pass As EffectPass In Shader.CurrentTechnique.Passes
Pass.Begin()
WithMe.GraphicsDevice
.VertexDeclaration = VDeclaration
Me.GraphicsDevice.DrawIndexedPrimitives( _
PrimitiveType.TriangleList, 0, 0, _
Vertices.Length, 0, Indices.Length / 3)
EndWith
Pass.End()
Next
Shader.End()
EndSubProtectedOverridesSub Update(ByVal GameTime As GameTime)
ProcessMouseInput(GameTime.ElapsedGameTime.TotalSeconds)
ProcessKeyboardInput()
Dim k As KeyboardState = Keyboard.GetState
If k.IsKeyDown(Keys.M) Then
ReflectionMap.Save("prova.jpg", ImageFileFormat.Jpg)
Me.Exit()
EndIfMyBase.Update(GameTime)
EndSubProtectedOverridesSub Draw(ByVal gameTime As GameTime)
Me.Graphics.GraphicsDevice.Clear(Color.CornflowerBlue)
DrawRefractionMap()
DrawReflectionMap()
DrawSky(ViewMatrix)
DrawTerrain(ViewMatrix)
DrawWater(gameTime.TotalGameTime.TotalMilliseconds / 100.0F)
MyBase.Draw(gameTime)
EndSub#End RegionEndClass
The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.