Added better track generation.

This commit is contained in:
Bas
2020-05-21 10:52:30 +02:00
parent e270f02bae
commit 193ef0d394
76 changed files with 846 additions and 87 deletions

View File

@@ -0,0 +1,22 @@
#include <openrct2/ride/Vehicle.h>
#include <openrct2/ride/VehicleSubpositionData.h>
#include "Openrct2-dll.h"
extern "C"
{
// Returns the amount of path nodes in the pathing route for the specified track type.
EXPORT int GetTrackElementRouteSize(int32_t trackVariant, int32_t typeAndDirection)
{
return gTrackVehicleInfo[trackVariant][typeAndDirection]->size;
}
// Returns the pathing route for the specified track element.
EXPORT void GetTrackElementRoute(int32_t trackVariant, int32_t typeAndDirection, rct_vehicle_info* nodes, int arraySize)
{
const rct_vehicle_info_list* list = gTrackVehicleInfo[trackVariant][typeAndDirection];
std::memcpy(nodes, list->info, sizeof(rct_vehicle_info) * arraySize);
}
}

View File

@@ -67,6 +67,7 @@ xcopy /i /y /f "$(SolutionDir)bin\dll-export\*" "$(SolutionDir)src\openrct2-unit
<ClCompile Include="Export\Game.cpp" />
<ClCompile Include="Export\Map.cpp" />
<ClCompile Include="Export\Sprites.cpp" />
<ClCompile Include="Export\Tracks.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Export\Openrct2-dll.h" />

View File

@@ -25,12 +25,106 @@ Transform:
m_GameObject: {fileID: 3460111253241373034}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.25, y: 0.25, z: 0.25}
m_LocalScale: {x: 0.05, y: 0.05, z: 0.05}
m_Children:
- {fileID: 3676241976214955814}
- {fileID: 8931575796167162921}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &5548823012095227079
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8931575796167162921}
- component: {fileID: 3552024548040068515}
- component: {fileID: 3049426981837551801}
- component: {fileID: 2258461748174447842}
m_Layer: 0
m_Name: Cube (1)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 4294967295
m_IsActive: 1
--- !u!4 &8931575796167162921
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5548823012095227079}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 2.5, y: 0.5, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 7165131111595459873}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &3552024548040068515
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5548823012095227079}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &3049426981837551801
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5548823012095227079}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: ce68aa93eb4498b4fa6a2a58f04c2bfb, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
--- !u!65 &2258461748174447842
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5548823012095227079}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
serializedVersion: 2
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &6000070329163251540
GameObject:
m_ObjectHideFlags: 0
@@ -58,7 +152,7 @@ Transform:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6000070329163251540}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0.5, z: 0}
m_LocalPosition: {x: -2.5, y: 0.5, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 7165131111595459873}

View File

@@ -98,7 +98,7 @@ LightmapSettings:
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 0}
m_UseShadowmask: 0
m_UseShadowmask: 1
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
@@ -464,7 +464,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 79be679884da2154dacd80be9669d5dc, type: 3}
m_Name:
m_EditorClassIdentifier:
selectedPark: Blackpool.sv6
selectedPark: Dynamite Dunes.sv6
--- !u!4 &1347800243
Transform:
m_ObjectHideFlags: 0
@@ -514,7 +514,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 8f4353bcde699fe4dbc7f9641bcd509e, type: 3}
m_Name:
m_EditorClassIdentifier:
generationFlags: 125
generationFlags: -1
surfaceGenerator:
id: 0
pathGenerator:
@@ -552,7 +552,7 @@ MonoBehaviour:
prefab: {fileID: 6681454114598486694, guid: d5e4b199bf0e87b47b14b43c86d8c12f,
type: 3}
00000002:
type: {class: PrefabGenerator, ns: Generation, asm: Assembly-CSharp}
type: {class: TrackGenerator, ns: Generation.Retro, asm: Assembly-CSharp}
data:
prefab: {fileID: 3460111253241373034, guid: ac7c5b20c9776c2458a7c5f077e645f0,
type: 3}

View File

@@ -1,4 +1,4 @@
using OpenRCT;
using Lib;
namespace Generation
{
@@ -8,7 +8,7 @@ namespace Generation
public interface IElementGenerator
{
/// <summary>
/// Creates a tile element at the specified position.
/// Creates a tile element at the specified tile position.
/// </summary>
void CreateElement(int x, int y, ref TileElement tile);

View File

@@ -1,4 +1,4 @@
using OpenRCT;
using Lib;
using UnityEngine;
namespace Generation

View File

@@ -1,4 +1,4 @@
using OpenRCT;
using Lib;
using UnityEngine;
namespace Generation.Retro
@@ -8,7 +8,6 @@ namespace Generation.Retro
/// </summary>
public class SmallSceneryGenerator : IElementGenerator
{
[Header("Meshes")]
[SerializeField] GameObject crossShape;
Map map;

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic;
using OpenRCT;
using Lib;
using UnityEngine;
namespace Generation.Retro

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using MeshBuilding;
using OpenRCT;
using Lib;
using UnityEngine;
namespace Generation.Retro

View File

@@ -0,0 +1,74 @@
using System.Collections.Generic;
using Lib;
using UnityEngine;
namespace Generation.Retro
{
public class TrackGenerator : IElementGenerator
{
static Dictionary<int, TrackNode[]> trackNodesCache = new Dictionary<int, TrackNode[]>();
[SerializeField] GameObject prefab;
Map map;
/// <inheritdoc/>
public void StartGenerator(Map map)
{
this.map = map;
}
/// <inheritdoc/>
public void FinishGenerator()
{
map = null;
}
/// <inheritdoc/>
public void CreateElement(int x, int y, ref TileElement tile)
{
TrackElement track = tile.AsTrack();
if (track.PartIndex != 0)
return;
int trackType = track.TrackType;
if (!trackNodesCache.TryGetValue(trackType, out TrackNode[] nodes))
{
nodes = OpenRCT2.GetTrackElementRoute(trackType);
trackNodesCache.Add(trackType, nodes);
}
const float trackOffset = -0.5f;
GameObject parent = new GameObject($"[{x}, {y}] rot: {tile.Rotation}, type: {trackType}")
{
isStatic = true
};
Transform tfParent = parent.transform;
tfParent.parent = map.transform;
tfParent.localPosition = Map.TileCoordsToUnity(x, tile.baseHeight, y);
tfParent.localRotation = Quaternion.Euler(0, tile.Rotation * 90f, 0);
for (int i = 0; i < nodes.Length; i++)
{
TrackNode node = nodes[i];
GameObject obj = GameObject.Instantiate(prefab, Vector3.zero, Quaternion.identity, tfParent);
obj.name = $"#{i} = dir: {node.direction}, bank: {node.bankRotation}, sprite: {node.vehicleSprite}";
Vector3 local = node.LocalPosition;
Transform tf = obj.transform;
tf.localPosition = new Vector3(local.x + trackOffset, local.y, local.z + trackOffset);
tf.localRotation = Quaternion.Euler(0, ((360f / 32f) * node.direction) + 270f, 0);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b626c25729c8694a9b1b122fdc92b0f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,4 +1,4 @@
using OpenRCT;
using Lib;
using UnityEngine;
namespace Generation.Retro

View File

@@ -6,18 +6,89 @@ namespace MeshBuilding
/// <summary>
/// Small class to easily create meshes through C#.
/// </summary>
public class MeshBuilder
public partial class MeshBuilder
{
/// <summary>
/// Returns all vertices currently added to the builder.
/// </summary>
public IReadOnlyCollection<Vertex> Vertices
=> vertices.Keys;
/// <summary>
/// Returns all vertices currently added to the builder.
/// </summary>
public Bounds Bounds
=> CalculateBounds();
// Dictionary of vertices and their indexes.
public readonly Dictionary<Vertex, int> vertices = new Dictionary<Vertex, int>(64);
readonly Dictionary<Vertex, int> vertices;
// List of triangles per submesh.
readonly List<List<int>> triangles = new List<List<int>>(1);
readonly List<List<int>> triangles;
Mesh mesh = null;
#region Constructors
/// <summary>
/// Creates a regular mesh builder.
/// </summary>
public MeshBuilder()
{
vertices = new Dictionary<Vertex, int>(64);
triangles = new List<List<int>>(1);
}
/// <summary>
/// Creates a mesh builder from an existing mesh.
/// </summary>
public MeshBuilder(Mesh mesh)
{
// Load vertices
Vector3[] meshVerts = mesh.vertices;
Vector3[] meshNormals = mesh.normals;
Vector2[] meshUvs = mesh.uv;
int vertexCount = mesh.vertexCount;
vertices = new Dictionary<Vertex, int>(vertexCount);
int index = 0;
for (int v = 0; v < vertexCount; v++)
{
Vertex vertex = new Vertex(
pos: meshVerts[v],
nor: meshNormals[v],
uvs: meshUvs[v]
);
if (vertices.ContainsKey(vertex))
continue;
vertices.Add(vertex, index);
index++;
}
// Load triangles
int submeshCount = mesh.subMeshCount;
triangles = new List<List<int>>(submeshCount);
for (int s = 0; s < submeshCount; s++)
{
List<int> buffer = new List<int>(0);
mesh.GetTriangles(buffer, s);
triangles[s] = buffer;
}
}
#endregion
/// <summary>
/// Add a vertex to the mesh. Returns the index of this vertex.
/// </summary>
@@ -117,6 +188,36 @@ namespace MeshBuilding
return mesh;
}
}
/// <summary>
/// Returns the bounds of all vertices.
/// </summary>
Bounds CalculateBounds()
{
float min_x = float.MaxValue, min_y = float.MaxValue, min_z = float.MaxValue,
max_x = float.MinValue, max_y = float.MinValue, max_z = float.MinValue;
foreach (Vertex vertex in vertices.Keys)
{
Vector3 pos = vertex.position;
if (min_x > pos.x) min_x = pos.x;
if (min_y > pos.y) min_y = pos.y;
if (min_z > pos.z) min_x = pos.z;
if (max_x < pos.x) max_x = pos.x;
if (max_y < pos.y) max_y = pos.y;
if (max_z < pos.z) max_x = pos.z;
}
Vector3 min = new Vector3(min_x, min_y, min_z);
Vector3 max = new Vector3(max_x, max_y, max_z);
Bounds bounds = new Bounds();
bounds.SetMinMax(min, max);
return bounds;
}
}
}

View File

@@ -44,7 +44,6 @@ namespace MeshBuilding
for (int p = 0; p < 4; p++)
{
//innerVerts[p] = new Vertex(matrix[p] * a.position + matrix[(p+1)%4] * b.position + matrix[(p+2)%4] * c.position + matrix[(p+3)%4] * d.position);
innerVerts[p] = new Vertex(
CalculateMatrix(matrix, p, a.position, b.position, c.position, d.position),
Vector3.up,

View File

@@ -1,6 +1,6 @@
using UnityEngine;
namespace OpenRCT
namespace Lib
{
/// <summary>
/// Static class to access OpenRCT2-Unity configuration.

View File

@@ -4,7 +4,7 @@ using System.Linq;
using UnityEditor;
using UnityEngine;
namespace OpenRCT
namespace Lib
{
/// <summary>
/// An editor that helps configuring OpenRCT2 settings in Unity, for example

View File

@@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using Utilities;
namespace Lib
{
/// <summary>
/// Property drawer for the <see cref="ScriptSelectorAttribute"/>.
/// </summary>
[CustomPropertyDrawer(typeof(ScriptSelectorAttribute))]
public class ScriptSelectorDrawer : PropertyDrawer
{
// Cache for the drawer, because the same drawer can be used for multiple properties.
static readonly Dictionary<string, DrawerData> cache = new Dictionary<string, DrawerData>();
// The settings for the drawer per property.
struct DrawerData
{
public bool foldout;
public MonoScript script;
}
/// <summary>
/// Draws the GUI for a property with a <see cref="ScriptSelectorAttribute"/>.
/// </summary>
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
float fieldSpacing = EditorGUIUtility.standardVerticalSpacing;
float singleLineHeight = EditorGUIUtility.singleLineHeight;
string cacheKey = GetCacheKey(property);
cache.TryGetValue(cacheKey, out DrawerData settings);
Rect rect = position;
rect.height = singleLineHeight;
if ((settings.foldout = EditorGUI.Foldout(rect, settings.foldout, label, toggleOnLabelClick: true)))
{
rect.y += (singleLineHeight + fieldSpacing);
EditorGUI.indentLevel++;
// Script
if (!TryFindManagedType(property.managedReferenceFullTypename, out Type managedType))
return;
MonoScript script;
if (settings.script != null)
script = settings.script;
else if (TryFindMonoScriptAsset(managedType.Name, out script))
settings.script = script;
else return;
GUIContent scriptLabel = new GUIContent(fieldInfo.FieldType.Name);
MonoScript selected = (MonoScript)EditorGUI.ObjectField(rect, scriptLabel, script, typeof(MonoScript), allowSceneObjects: false);
if (selected != script && ValidateSelectedScript(selected, fieldInfo, out object instance))
{
property.serializedObject.Update();
property.managedReferenceValue = instance;
property.serializedObject.ApplyModifiedProperties();
}
rect.y += (singleLineHeight + fieldSpacing);
// Serialized fields
SerializedProperty end = property.GetEndProperty();
bool any = property.NextVisible(true);
while (any && property.propertyPath != end.propertyPath)
{
float propertyHeight = EditorGUI.GetPropertyHeight(property);
rect.height = propertyHeight;
EditorGUI.PropertyField(rect, property, label, includeChildren: true);
rect.y += (propertyHeight + fieldSpacing);
any = property.NextVisible(false);
}
EditorGUI.indentLevel--;
}
cache[cacheKey] = settings;
}
/// <summary>
/// Gets the height of the property.
/// </summary>
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
cache.TryGetValue(GetCacheKey(property), out DrawerData settings);
if (!settings.foldout)
return EditorGUIUtility.singleLineHeight;
float editorHeight = EditorGUI.GetPropertyHeight(property, label, includeChildren: true);
return (editorHeight + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
}
/// <summary>
/// Try to find the managed type from the Unity provided type name.
/// </summary>
static bool TryFindManagedType(string fullUnityTypeName, out Type managedType)
{
string[] parts = fullUnityTypeName.Split(' ');
if (parts.Length == 2)
{
managedType = Type.GetType($"{parts[1]}, {parts[0]}");
return true;
}
Debug.LogWarning($"Could not find managed type '{fullUnityTypeName}'!");
managedType = null;
return false;
}
/// <summary>
/// Try to find the <see cref="MonoScript"/> asset that contains the specified class.
/// </summary>
static bool TryFindMonoScriptAsset(string className, out MonoScript script)
{
string[] assetGuids = AssetDatabase.FindAssets($"{className} t:MonoScript");
if (assetGuids.Length > 0)
{
string path = AssetDatabase.GUIDToAssetPath(assetGuids[0]);
script = AssetDatabase.LoadAssetAtPath<MonoScript>(path);
return true;
}
Debug.LogWarning($"Could not find MonoScript asset '{className}'!");
script = null;
return false;
}
/// <summary>
/// Validate the selected <see cref="MonoScript"/> to see whether it can
/// produce a valid instance for the specified property.
/// </summary>
static bool ValidateSelectedScript(MonoScript selected, FieldInfo fieldInfo, out object instance)
{
Type classType = selected.GetClass();
if (classType == null)
{
Debug.LogError($"The selected MonoScript '{selected.name}' does not have a matching class!");
instance = null;
return false;
}
Type fieldType = fieldInfo.FieldType;
if (!fieldType.IsAssignableFrom(classType))
{
Debug.LogError($"Cannot use script '{classType.Name}'! Only scripts that implement '{fieldType.Name}' are allowed.");
instance = null;
return false;
}
instance = Activator.CreateInstance(classType);
return true;
}
/// <summary>
/// Gets the key (path) of the cache.
/// </summary>
static string GetCacheKey(SerializedProperty property)
=> $"{property.propertyPath}<{property.serializedObject.targetObject}>";
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 739e17dce77728a4abc4e39d3c2812f1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,6 +1,6 @@
using System;
namespace OpenRCT
namespace Lib
{
/// <summary>
/// Flags of ownership of a specific tile.

View File

@@ -1,4 +1,4 @@
namespace OpenRCT
namespace Lib
{
public enum PeepAction : byte
{

Some files were not shown because too many files have changed in this diff Show More