Added textures to biomes and an expression parser.
This commit is contained in:
@ -40,5 +40,11 @@ namespace TransportGame.Model
|
||||
/// </summary>
|
||||
[XmlElement("vegetationDensity")]
|
||||
public Range VegetationDensity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an array of textures to use
|
||||
/// </summary>
|
||||
[XmlArray("textures")]
|
||||
public Texture[] Textures { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,17 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Assets.Scripts.Model
|
||||
namespace TransportGame.Model
|
||||
{
|
||||
class Texture
|
||||
[XmlRoot("texture")]
|
||||
public class Texture
|
||||
{
|
||||
[XmlAttribute("src")]
|
||||
public string Source { get; set; }
|
||||
|
||||
[XmlAttribute("expr")]
|
||||
public string Expression { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -8,83 +8,83 @@ using TransportGame.Business;
|
||||
|
||||
public class TerrainGeneratorScript : MonoBehaviour
|
||||
{
|
||||
private Map map = null;
|
||||
private Map map = null;
|
||||
|
||||
public int TerrainWidth = 1024;
|
||||
public int TerrainHeight = 1024;
|
||||
public GameObject WaterObject;
|
||||
public int TerrainWidth = 1024;
|
||||
public int TerrainHeight = 1024;
|
||||
public GameObject WaterObject;
|
||||
|
||||
// Use this for initialization
|
||||
void Start()
|
||||
{
|
||||
StartCoroutine(GenerateMap());
|
||||
}
|
||||
// Use this for initialization
|
||||
void Start()
|
||||
{
|
||||
StartCoroutine(GenerateMap());
|
||||
}
|
||||
|
||||
private void GenerateTerrainThread()
|
||||
{
|
||||
TerrainGenerator generator = new TerrainGenerator();
|
||||
map = generator.Generate(TerrainWidth, TerrainHeight);
|
||||
}
|
||||
private void GenerateTerrainThread()
|
||||
{
|
||||
TerrainGenerator generator = new TerrainGenerator();
|
||||
map = generator.Generate(TerrainWidth, TerrainHeight);
|
||||
}
|
||||
|
||||
private Mesh GenerateWater()
|
||||
{
|
||||
Mesh water = new Mesh();
|
||||
water.name = "water";
|
||||
water.vertices = new[] {
|
||||
new Vector3(0, map.WaterLevel, 0),
|
||||
new Vector3(0, map.WaterLevel, map.Height),
|
||||
new Vector3(map.Width, map.WaterLevel, 0),
|
||||
new Vector3(map.Width, map.WaterLevel, map.Height)
|
||||
};
|
||||
water.triangles = new[] { 0, 1, 2, 2, 1, 3 };
|
||||
water.uv = new[] {
|
||||
new Vector2(0, 0),
|
||||
new Vector2(0, 1),
|
||||
new Vector2(1, 0),
|
||||
new Vector2(1, 1)
|
||||
};
|
||||
water.RecalculateNormals();
|
||||
private Mesh GenerateWater()
|
||||
{
|
||||
Mesh water = new Mesh();
|
||||
water.name = "water";
|
||||
water.vertices = new[] {
|
||||
new Vector3(0, map.WaterLevel, 0),
|
||||
new Vector3(0, map.WaterLevel, map.Height),
|
||||
new Vector3(map.Width, map.WaterLevel, 0),
|
||||
new Vector3(map.Width, map.WaterLevel, map.Height)
|
||||
};
|
||||
water.triangles = new[] { 0, 1, 2, 2, 1, 3 };
|
||||
water.uv = new[] {
|
||||
new Vector2(0, 0),
|
||||
new Vector2(0, 1),
|
||||
new Vector2(1, 0),
|
||||
new Vector2(1, 1)
|
||||
};
|
||||
water.RecalculateNormals();
|
||||
|
||||
return water;
|
||||
}
|
||||
return water;
|
||||
}
|
||||
|
||||
private IEnumerator GenerateMap()
|
||||
{
|
||||
// Wait for the map generation thread
|
||||
private IEnumerator GenerateMap()
|
||||
{
|
||||
// Wait for the map generation thread
|
||||
foreach (var i in Task.RunAsync(GenerateTerrainThread))
|
||||
yield return i;
|
||||
|
||||
// Generate terrain data
|
||||
TerrainData terrainData = new TerrainData();
|
||||
terrainData.heightmapResolution = Mathf.Max(map.Height, map.Width) + 1;
|
||||
terrainData.size = new Vector3(map.Width, map.Biome.Height, map.Height);
|
||||
terrainData.SetDetailResolution(1024, 8);
|
||||
terrainData.SetHeights(0, 0, map.Heights);
|
||||
terrainData.name = "Generated Terrain Data";
|
||||
yield return null;
|
||||
// Generate terrain data
|
||||
TerrainData terrainData = new TerrainData();
|
||||
terrainData.heightmapResolution = Mathf.Max(map.Height, map.Width) + 1;
|
||||
terrainData.size = new Vector3(map.Width, map.Biome.Height, map.Height);
|
||||
terrainData.SetDetailResolution(1024, 8);
|
||||
terrainData.SetHeights(0, 0, map.Heights);
|
||||
terrainData.name = "Generated Terrain Data";
|
||||
yield return null;
|
||||
|
||||
// Create terrain object
|
||||
GameObject terrain = Terrain.CreateTerrainGameObject(terrainData);
|
||||
terrain.name = "Generated Terrain";
|
||||
yield return null;
|
||||
// Create terrain object
|
||||
GameObject terrain = Terrain.CreateTerrainGameObject(terrainData);
|
||||
terrain.name = "Generated Terrain";
|
||||
yield return null;
|
||||
|
||||
Terrain terrainComp = terrain.GetComponent<Terrain>();
|
||||
terrainComp.heightmapPixelError = 1;
|
||||
yield return null;
|
||||
Terrain terrainComp = terrain.GetComponent<Terrain>();
|
||||
terrainComp.heightmapPixelError = 1;
|
||||
yield return null;
|
||||
|
||||
// Set water
|
||||
if (WaterObject != null)
|
||||
{
|
||||
MeshFilter waterMesh = WaterObject.GetComponent<MeshFilter>();
|
||||
waterMesh.mesh = GenerateWater();
|
||||
}
|
||||
// Set water
|
||||
if (WaterObject != null)
|
||||
{
|
||||
MeshFilter waterMesh = WaterObject.GetComponent<MeshFilter>();
|
||||
waterMesh.mesh = GenerateWater();
|
||||
}
|
||||
|
||||
// Set up textures
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
}
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
425
Game/Assets/Scripts/Utils/Expression.cs
Normal file
425
Game/Assets/Scripts/Utils/Expression.cs
Normal file
@ -0,0 +1,425 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GraphingCalculator
|
||||
{
|
||||
public class Expression
|
||||
{
|
||||
#region Types
|
||||
|
||||
public enum TokenType
|
||||
{
|
||||
None, Literal, Identifier, Operator, ArgSeparator,
|
||||
LParanthesis, RParanthesis, Function
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Represents a token
|
||||
/// </summary>
|
||||
protected class Token
|
||||
{
|
||||
public TokenType Type { get; set; }
|
||||
public string Text { get; set; }
|
||||
|
||||
public Token(TokenType type, string text)
|
||||
{
|
||||
Type = type;
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Variables, properties
|
||||
|
||||
private string expressionString;
|
||||
private bool parsed = false;
|
||||
|
||||
protected List<Token> Tokens { get; private set; }
|
||||
protected List<Token> Postfix { get; private set; }
|
||||
public Dictionary<string, float> Variables { get; private set; }
|
||||
|
||||
public string ExpressionString
|
||||
{
|
||||
get { return expressionString; }
|
||||
set
|
||||
{
|
||||
expressionString = value;
|
||||
parsed = false;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other stuff
|
||||
|
||||
public void AddVariable(string name, float value)
|
||||
{
|
||||
Variables.Add(name, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public Expression()
|
||||
{
|
||||
Tokens = new List<Token>();
|
||||
Postfix = new List<Token>();
|
||||
Variables = new Dictionary<string, float>();
|
||||
|
||||
Variables.Add("pi", Mathf.PI);
|
||||
}
|
||||
|
||||
public Expression(string expr)
|
||||
: this()
|
||||
{
|
||||
expressionString = expr;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Parser
|
||||
|
||||
private void AnalyzeLex()
|
||||
{
|
||||
Tokens.Clear();
|
||||
|
||||
for (int i = 0; i < ExpressionString.Length; i++)
|
||||
{
|
||||
if (char.IsWhiteSpace(ExpressionString[i])) continue;
|
||||
|
||||
if (IsOperator(ExpressionString[i]))
|
||||
{
|
||||
// Handle unary minus
|
||||
if (IsUnaryMinus(ExpressionString[i]))
|
||||
Tokens.Add(new Token(TokenType.Operator, "u"));
|
||||
|
||||
else
|
||||
Tokens.Add(new Token(TokenType.Operator, ExpressionString[i].ToString()));
|
||||
}
|
||||
|
||||
else if (ExpressionString[i] == '(')
|
||||
Tokens.Add(new Token(TokenType.LParanthesis, ExpressionString[i].ToString()));
|
||||
|
||||
else if (ExpressionString[i] == ')')
|
||||
Tokens.Add(new Token(TokenType.RParanthesis, ExpressionString[i].ToString()));
|
||||
|
||||
else if (ExpressionString[i] == ',')
|
||||
Tokens.Add(new Token(TokenType.ArgSeparator, ExpressionString[i].ToString()));
|
||||
|
||||
else if (Char.IsDigit(ExpressionString[i]))
|
||||
Tokens.Add(new Token(TokenType.Literal, GetLiteral(ExpressionString, ref i)));
|
||||
|
||||
else if (Char.IsLetter(ExpressionString[i]))
|
||||
Tokens.Add(new Token(TokenType.Identifier, GetIdentifier(ExpressionString, ref i)));
|
||||
|
||||
else throw new Exception("Unrecognized character found!");
|
||||
}
|
||||
}
|
||||
|
||||
private void ConvertPostfix()
|
||||
{
|
||||
Stack<Token> stack = new Stack<Token>();
|
||||
|
||||
for (int i = 0; i < Tokens.Count; i++)
|
||||
{
|
||||
Token t = Tokens[i];
|
||||
|
||||
switch (t.Type)
|
||||
{
|
||||
case TokenType.Identifier:
|
||||
|
||||
// Followed by '(' means function
|
||||
if (i + 1 < Tokens.Count && Tokens[i + 1].Type == TokenType.LParanthesis)
|
||||
stack.Push(new Token(TokenType.Function, t.Text));
|
||||
|
||||
// Else, variable
|
||||
else Postfix.Add(t);
|
||||
|
||||
break;
|
||||
|
||||
case TokenType.Literal:
|
||||
Postfix.Add(t);
|
||||
break;
|
||||
|
||||
case TokenType.ArgSeparator:
|
||||
|
||||
// We pop everything from the stack until left paranthesis open
|
||||
while (stack.Peek().Type != TokenType.LParanthesis)
|
||||
{
|
||||
Postfix.Add(stack.Pop());
|
||||
|
||||
if (stack.Count == 0)
|
||||
throw new Exception("Syntax error! Unexpected comma.");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TokenType.Operator:
|
||||
|
||||
if (IsLeftAssociative(t.Text))
|
||||
{
|
||||
while (stack.Count != 0 && Precedence(t.Text) <= Precedence(stack.Peek().Text))
|
||||
Postfix.Add(stack.Pop());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
while (stack.Count != 0 && Precedence(t.Text) < Precedence(stack.Peek().Text))
|
||||
Postfix.Add(stack.Pop());
|
||||
}
|
||||
|
||||
stack.Push(t);
|
||||
break;
|
||||
|
||||
case TokenType.LParanthesis:
|
||||
stack.Push(t);
|
||||
break;
|
||||
|
||||
case TokenType.RParanthesis:
|
||||
|
||||
while (stack.Peek().Type != TokenType.LParanthesis)
|
||||
{
|
||||
Postfix.Add(stack.Pop());
|
||||
|
||||
if (stack.Count == 0)
|
||||
throw new Exception("Mismatched parantheses!");
|
||||
}
|
||||
|
||||
stack.Pop(); // Pop Lparanthesis
|
||||
|
||||
if (stack.Count > 0 && stack.Peek().Type == TokenType.Function)
|
||||
Postfix.Add(stack.Pop()); // Pop function name
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
if (stack.Peek().Type == TokenType.LParanthesis)
|
||||
throw new Exception("Mismatched parantheses!");
|
||||
|
||||
Postfix.Add(stack.Pop());
|
||||
}
|
||||
}
|
||||
|
||||
public void ParseExpression()
|
||||
{
|
||||
if (!parsed)
|
||||
{
|
||||
Tokens.Clear();
|
||||
Postfix.Clear();
|
||||
|
||||
AnalyzeLex();
|
||||
ConvertPostfix();
|
||||
}
|
||||
}
|
||||
|
||||
public float Evaluate()
|
||||
{
|
||||
// Parse expression first
|
||||
ParseExpression();
|
||||
|
||||
// Expression is empty, so is result
|
||||
if (Postfix.Count == 0)
|
||||
return 0;
|
||||
|
||||
Stack<float> stack = new Stack<float>();
|
||||
|
||||
foreach (var t in Postfix)
|
||||
{
|
||||
switch (t.Type)
|
||||
{
|
||||
// We already replace functions, so identifiers are variables
|
||||
case TokenType.Identifier:
|
||||
|
||||
if (!Variables.ContainsKey(t.Text))
|
||||
throw new Exception("Undefined variable '" + t.Text + "'.");
|
||||
|
||||
stack.Push(Variables[t.Text]);
|
||||
break;
|
||||
|
||||
case TokenType.Literal:
|
||||
stack.Push(float.Parse(t.Text));
|
||||
break;
|
||||
|
||||
case TokenType.Operator:
|
||||
switch (t.Text)
|
||||
{
|
||||
case "u":
|
||||
stack.Push(stack.Pop() * -1);
|
||||
break;
|
||||
|
||||
case "+":
|
||||
stack.Push(stack.Pop() + stack.Pop());
|
||||
break;
|
||||
|
||||
case "-":
|
||||
{
|
||||
float b = stack.Pop();
|
||||
float a = stack.Pop();
|
||||
stack.Push(a - b);
|
||||
}
|
||||
break;
|
||||
|
||||
case "*":
|
||||
stack.Push(stack.Pop() * stack.Pop());
|
||||
break;
|
||||
|
||||
case "/":
|
||||
{
|
||||
float b = stack.Pop();
|
||||
float a = stack.Pop();
|
||||
stack.Push(a / b);
|
||||
}
|
||||
break;
|
||||
|
||||
case "%":
|
||||
{
|
||||
float b = stack.Pop();
|
||||
float a = stack.Pop();
|
||||
stack.Push(a % b);
|
||||
}
|
||||
break;
|
||||
|
||||
case "^":
|
||||
{
|
||||
float b = stack.Pop();
|
||||
float a = stack.Pop();
|
||||
stack.Push(Mathf.Pow(a, b));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TokenType.Function:
|
||||
EvaluateFunction(t.Text, ref stack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return stack.Pop();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Helper routines
|
||||
|
||||
private bool IsUnaryMinus(char c)
|
||||
{
|
||||
if (c == '-')
|
||||
{
|
||||
// Nothing in front, definitely unary
|
||||
if (Tokens.Count == 0)
|
||||
return true;
|
||||
|
||||
// See what's in front
|
||||
TokenType inFront = Tokens.Last().Type;
|
||||
|
||||
// If what's in front cannot be an operand, than it is unary minus
|
||||
return inFront == TokenType.ArgSeparator || inFront == TokenType.LParanthesis || inFront == TokenType.Operator;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void EvaluateFunction(string func, ref Stack<float> stack)
|
||||
{
|
||||
switch (func)
|
||||
{
|
||||
case "sin": stack.Push(Mathf.Sin(stack.Pop())); break;
|
||||
case "cos": stack.Push(Mathf.Cos(stack.Pop())); break;
|
||||
case "tan": stack.Push(Mathf.Tan(stack.Pop())); break;
|
||||
case "ctan": stack.Push(1 / Mathf.Tan(stack.Pop())); break;
|
||||
|
||||
case "arcsin": stack.Push(Mathf.Asin(stack.Pop())); break;
|
||||
case "asin": stack.Push(Mathf.Asin(stack.Pop())); break;
|
||||
case "arccos": stack.Push(Mathf.Acos(stack.Pop())); break;
|
||||
case "acos": stack.Push(Mathf.Acos(stack.Pop())); break;
|
||||
case "arctan": stack.Push(Mathf.Atan(stack.Pop())); break;
|
||||
case "atan": stack.Push(Mathf.Atan(stack.Pop())); break;
|
||||
|
||||
case "truncate":
|
||||
case "floor": stack.Push(Mathf.Floor(stack.Pop())); break;
|
||||
case "ceil":
|
||||
case "ceiling": stack.Push(Mathf.Ceil(stack.Pop())); break;
|
||||
|
||||
case "sqrt": stack.Push(Mathf.Sqrt(stack.Pop())); break;
|
||||
case "cbrt": stack.Push(Mathf.Pow(stack.Pop(), 1.0f / 3.0f)); break;
|
||||
case "root": stack.Push(Mathf.Pow(stack.Pop(), 1 / stack.Pop())); break;
|
||||
|
||||
case "abs": stack.Push(Math.Abs(stack.Pop())); break;
|
||||
case "max": stack.Push(Math.Max(stack.Pop(), stack.Pop())); break;
|
||||
case "min": stack.Push(Math.Min(stack.Pop(), stack.Pop())); break;
|
||||
|
||||
case "lg": stack.Push(Mathf.Log10(stack.Pop())); break;
|
||||
case "log": stack.Push(Mathf.Log(stack.Pop(), stack.Pop())); break;
|
||||
|
||||
case "clamp01": stack.Push(Mathf.Clamp01(stack.Pop())); break;
|
||||
case "clamp": stack.Push(Mathf.Clamp(stack.Pop(), stack.Pop(), stack.Pop())); break;
|
||||
case "lerp": stack.Push(Mathf.Lerp(stack.Pop(), stack.Pop(), stack.Pop())); break;
|
||||
|
||||
default: throw new Exception("Undefined function '" + func + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsLeftAssociative(string op)
|
||||
{
|
||||
return (op != "^");
|
||||
}
|
||||
|
||||
private static int Precedence(string op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case "+":
|
||||
case "-": return 1;
|
||||
|
||||
case "*":
|
||||
case "/":
|
||||
case "%": return 2;
|
||||
|
||||
case "u":
|
||||
case "^": return 3;
|
||||
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsOperator(char c)
|
||||
{
|
||||
const string operators = "+-*/%^";
|
||||
return operators.Contains(c);
|
||||
}
|
||||
|
||||
private static string GetIdentifier(string s, ref int index)
|
||||
{
|
||||
int start = index;
|
||||
|
||||
while (index < s.Length && (char.IsLetterOrDigit(s[index]) || s[index] == '_'))
|
||||
++index;
|
||||
|
||||
index -= 1;
|
||||
|
||||
return s.Substring(start, index + 1 - start);
|
||||
}
|
||||
|
||||
private static string GetLiteral(string s, ref int index)
|
||||
{
|
||||
int start = index;
|
||||
|
||||
while (index < s.Length && (char.IsDigit(s[index]) || s[index] == '.'))
|
||||
++index;
|
||||
|
||||
index -= 1;
|
||||
|
||||
return s.Substring(start, index + 1 - start);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user