Added player character and step in/out of dialogue in scene 101

This commit is contained in:
2026-08-15 15:22:47 +02:00
parent a42f25d5fa
commit 38ba2b0eff
2019 changed files with 181227 additions and 265 deletions
@@ -0,0 +1,5 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.ProBuilder")]
[assembly: InternalsVisibleTo("Unity.ProBuilder.Editor")]
[assembly: InternalsVisibleTo("Unity.ProBuilder.Editor.Tests")]
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 389ebe3277e864eb2bb6f14edb419a33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a4b6fa9dcb27fb9449331018a9db0c0c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,193 @@
using System.Text;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
namespace UnityEngine.ProBuilder.Stl
{
/// <summary>
/// Describes the file format of an STL file.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public enum FileType
{
/// <summary>Use the [ASCII](https://en.wikipedia.org/wiki/ASCII) file format.</summary>
Ascii,
/// <summary>Use the [Binary](https://en.wikipedia.org/wiki/Binary_file) file format.</summary>
Binary
};
/**
* Export STL files from Unity mesh assets.
*/
static class pb_Stl
{
/**
* Write a mesh file to STL.
*/
public static bool WriteFile(string path, Mesh mesh, FileType type = FileType.Ascii, bool convertToRightHandedCoordinates = true)
{
return WriteFile(path, new Mesh[] { mesh }, type, convertToRightHandedCoordinates);
}
/**
* Write a collection of mesh assets to an STL file.
* No transformations are performed on meshes in this method.
* Eg, if you want to export a set of a meshes in a transform
* hierarchy the meshes should be transformed prior to this call.
*
* string path - Where to write the file.
* IList<Mesh> meshes - The mesh assets to write.
* FileType type - How to format the file (in ASCII or binary).
*/
public static bool WriteFile(string path, IList<Mesh> meshes, FileType type = FileType.Ascii, bool convertToRightHandedCoordinates = true)
{
try
{
switch(type)
{
case FileType.Binary:
{
// http://paulbourke.net/dataformats/stl/
// http://www.fabbers.com/tech/STL_Format
using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create), new ASCIIEncoding()))
{
// 80 byte header
writer.Write(new byte[80]);
uint totalTriangleCount = (uint) (meshes.Sum(x => x.triangles.Length) / 3);
// unsigned long facet count (4 bytes)
writer.Write( totalTriangleCount );
foreach(Mesh mesh in meshes)
{
Vector3[] v = convertToRightHandedCoordinates ? Left2Right(mesh.vertices) : mesh.vertices;
Vector3[] n = convertToRightHandedCoordinates ? Left2Right(mesh.normals) : mesh.normals;
int[] t = mesh.triangles;
int triangleCount = t.Length;
if(convertToRightHandedCoordinates)
System.Array.Reverse(t);
for(int i = 0; i < triangleCount; i += 3)
{
int a = t[i], b = t[i+1], c = t[i+2];
Vector3 avg = AvgNrm(n[a], n[b], n[c]);
writer.Write(avg.x);
writer.Write(avg.y);
writer.Write(avg.z);
writer.Write(v[a].x);
writer.Write(v[a].y);
writer.Write(v[a].z);
writer.Write(v[b].x);
writer.Write(v[b].y);
writer.Write(v[b].z);
writer.Write(v[c].x);
writer.Write(v[c].y);
writer.Write(v[c].z);
// specification says attribute byte count should be set to 0.
writer.Write( (ushort)0 );
}
}
}
}
break;
default:
string model = WriteString(meshes);
File.WriteAllText(path, model);
break;
}
}
catch(System.Exception e)
{
UnityEngine.Debug.LogError(e.ToString());
return false;
}
return true;
}
/**
* Write a Unity mesh to an ASCII STL string.
*/
public static string WriteString(Mesh mesh, bool convertToRightHandedCoordinates = true)
{
return WriteString(new Mesh[] { mesh }, convertToRightHandedCoordinates);
}
/**
* Write a set of meshes to an ASCII string in STL format.
*/
public static string WriteString(IList<Mesh> meshes, bool convertToRightHandedCoordinates = true)
{
StringBuilder sb = new StringBuilder();
string name = meshes.Count == 1 ? meshes[0].name : "Composite Mesh";
sb.AppendLine(string.Format("solid {0}", name));
foreach(Mesh mesh in meshes)
{
Vector3[] v = convertToRightHandedCoordinates ? Left2Right(mesh.vertices) : mesh.vertices;
Vector3[] n = convertToRightHandedCoordinates ? Left2Right(mesh.normals) : mesh.normals;
int[] t = mesh.triangles;
if(convertToRightHandedCoordinates) System.Array.Reverse(t);
int triLen = t.Length;
for(int i = 0; i < triLen; i+=3)
{
int a = t[i];
int b = t[i+1];
int c = t[i+2];
Vector3 nrm = AvgNrm(n[a], n[b], n[c]);
sb.AppendLine(string.Format("facet normal {0} {1} {2}", nrm.x, nrm.y, nrm.z));
sb.AppendLine("outer loop");
sb.AppendLine(string.Format("\tvertex {0} {1} {2}", v[a].x, v[a].y, v[a].z));
sb.AppendLine(string.Format("\tvertex {0} {1} {2}", v[b].x, v[b].y, v[b].z));
sb.AppendLine(string.Format("\tvertex {0} {1} {2}", v[c].x, v[c].y, v[c].z));
sb.AppendLine("endloop");
sb.AppendLine("endfacet");
}
}
sb.AppendLine(string.Format("endsolid {0}", name));
return sb.ToString();
}
private static Vector3[] Left2Right(Vector3[] v)
{
Vector3[] r = new Vector3[v.Length];
for (int i = 0; i < v.Length; i++)
r[i] = new Vector3(v[i].z, -v[i].x, v[i].y);
return r;
}
/**
* Average of 3 vectors.
*/
private static Vector3 AvgNrm(Vector3 a, Vector3 b, Vector3 c)
{
return new Vector3(
(a.x + b.x + c.x) / 3f,
(a.y + b.y + c.y) / 3f,
(a.z + b.z + c.z) / 3f );
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 396694d6dbd4734419cea5be4a942c9a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,97 @@
using UnityEngine;
using System.Linq;
using System.Collections.Generic;
namespace UnityEngine.ProBuilder.Stl
{
/**
* Provides menu items for writing STL files from a scene selection.
*/
static class pb_Stl_Exporter
{
/**
* Export a hierarchy of GameObjects to path with file type.
*/
public static bool Export(string path, GameObject[] gameObjects, FileType type)
{
Mesh[] meshes = CreateWorldSpaceMeshesWithTransforms(gameObjects.Select(x => x.transform).ToArray());
bool success = false;
if(meshes != null && meshes.Length > 0)
{
if(!string.IsNullOrEmpty(path))
success = pb_Stl.WriteFile(path, meshes, type);
}
for(int i = 0; meshes != null && i < meshes.Length; i++)
Object.DestroyImmediate(meshes[i]);
return success;
}
/**
* Extracts a list of mesh values with their relative transformations intact.
*/
private static Mesh[] CreateWorldSpaceMeshesWithTransforms(IList<Transform> transforms)
{
if(transforms == null || transforms.Count < 1)
return null;
// move root node to center of selection
Vector3 p = Vector3.zero;
for(int i = 0; i < transforms.Count; i++)
p += transforms[i].position;
Vector3 mesh_center = p / (float) transforms.Count;
GameObject root = new GameObject();
root.name = "ROOT";
root.transform.position = mesh_center;
// copy all transforms to new root gameobject
foreach(Transform t in transforms)
{
GameObject go = (GameObject) GameObject.Instantiate(t.gameObject);
go.transform.SetParent(t.parent, false);
go.transform.SetParent(root.transform, true);
}
// move root to 0,0,0 so mesh transformations are relative to origin
root.transform.position = Vector3.zero;
// create new meshes by iterating the root node and transforming vertex & normal
// values (ignoring all other mesh attributes since STL doesn't care about them)
List<MeshFilter> mfs = root.GetComponentsInChildren<MeshFilter>().Where(x => x.sharedMesh != null).ToList();
int meshCount = mfs.Count;
Mesh[] meshes = new Mesh[meshCount];
for(int i = 0; i < meshCount; i++)
{
Transform t = mfs[i].transform;
Vector3[] v = mfs[i].sharedMesh.vertices;
Vector3[] n = mfs[i].sharedMesh.normals;
for(int it = 0; it < v.Length; it++)
{
v[it] = t.TransformPoint(v[it]);
n[it] = t.TransformDirection(n[it]);
}
Mesh m = new Mesh();
m.name = mfs[i].name;
m.vertices = v;
m.normals = n;
m.triangles = mfs[i].sharedMesh.triangles;
meshes[i] = m;
}
// Cleanup
GameObject.DestroyImmediate(root);
return meshes;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9cfc7aced6c45c0499ba73f07e633bac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,294 @@
#pragma warning disable 0219
using UnityEngine;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace UnityEngine.ProBuilder.Stl
{
/**
* Import methods for STL files.
*/
static class pb_Stl_Importer
{
const int MAX_FACETS_PER_MESH = 65535 / 3;
class Facet
{
public Vector3 normal;
public Vector3 a, b, c;
public override string ToString()
{
return string.Format("{0:F2}: {1:F2}, {2:F2}, {3:F2}", normal, a, b, c);
}
}
/**
* Import an STL file at path.
*/
public static Mesh[] Import(string path)
{
if( IsBinary(path) )
{
try
{
return ImportBinary(path);
}
catch(System.Exception e)
{
UnityEngine.Debug.LogWarning(string.Format("Failed importing mesh at path {0}.\n{1}", path, e.ToString()));
return null;
}
}
else
{
return ImportAscii(path);
}
}
private static Mesh[] ImportBinary(string path)
{
Facet[] facets;
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
using (BinaryReader br = new BinaryReader(fs, new ASCIIEncoding()))
{
// read header
byte[] header = br.ReadBytes(80);
uint facetCount = br.ReadUInt32();
facets = new Facet[facetCount];
for(uint i = 0; i < facetCount; i++)
facets[i] = br.GetFacet();
}
}
return CreateMeshWithFacets(facets);
}
private static Facet GetFacet(this BinaryReader binaryReader)
{
Facet facet = new Facet();
facet.normal = binaryReader.GetVector3();
// maintain counter-clockwise orientation of vertices:
facet.a = binaryReader.GetVector3();
facet.c = binaryReader.GetVector3();
facet.b = binaryReader.GetVector3();
binaryReader.ReadUInt16(); // padding
return facet;
}
private static Vector3 GetVector3(this BinaryReader binaryReader)
{
Vector3 vector3 = new Vector3();
for (int i = 0; i < 3; i++)
vector3[i] = binaryReader.ReadSingle();
return vector3.UnityCoordTrafo();
}
private static Vector3 UnityCoordTrafo(this Vector3 vector3)
{
return new Vector3(-vector3.y, vector3.z, vector3.x);
}
const int SOLID = 1;
const int FACET = 2;
const int OUTER = 3;
const int VERTEX = 4;
const int ENDLOOP = 5;
const int ENDFACET = 6;
const int ENDSOLID = 7;
const int EMPTY = 0;
private static int ReadState(string line)
{
if(line.StartsWith("solid"))
return SOLID;
else if(line.StartsWith("facet"))
return FACET;
else if(line.StartsWith("outer"))
return OUTER;
else if(line.StartsWith("vertex"))
return VERTEX;
else if(line.StartsWith("endloop"))
return ENDLOOP;
else if(line.StartsWith("endfacet"))
return ENDFACET;
else if(line.StartsWith("endsolid"))
return ENDSOLID;
else
return EMPTY;
}
private static Mesh[] ImportAscii(string path)
{
List<Facet> facets = new List<Facet>();
using(StreamReader sr = new StreamReader(path))
{
string line;
int state = EMPTY, vertex = 0;
Facet f = null;
bool exit = false;
while(sr.Peek() > 0 && !exit)
{
line = sr.ReadLine().Trim();
int previousState = state;
state = ReadState(line);
switch(state)
{
case SOLID:
continue;
case FACET:
f = new Facet();
f.normal = StringToVec3(line.Replace("facet normal ", ""));
break;
case OUTER:
vertex = 0;
break;
case VERTEX:
// maintain counter-clockwise orientation of vertices:
if (vertex == 0) f.a = StringToVec3(line.Replace("vertex ", ""));
else if(vertex == 2) f.c = StringToVec3(line.Replace("vertex ", ""));
else if (vertex == 1) f.b = StringToVec3(line.Replace("vertex ", ""));
vertex++;
break;
case ENDLOOP:
break;
case ENDFACET:
facets.Add(f);
break;
case ENDSOLID:
exit = true;
break;
case EMPTY:
default:
break;
}
}
}
return CreateMeshWithFacets(facets);
}
private static Vector3 StringToVec3(string str)
{
string[] split = str.Trim().Split(null);
Vector3 v = new Vector3();
float.TryParse(split[0], out v.x);
float.TryParse(split[1], out v.y);
float.TryParse(split[2], out v.z);
return v.UnityCoordTrafo();
}
/**
* Read the first 80 bytes of a file and if they are all 0x0 it's likely
* that this file is binary.
*/
private static bool IsBinary(string path)
{
// http://stackoverflow.com/questions/968935/compare-binary-files-in-c-sharp
FileInfo file = new FileInfo(path);
if(file.Length < 130)
return false;
var isBinary = false;
using(FileStream f0 = file.OpenRead())
{
using(BufferedStream bs0 = new BufferedStream(f0))
{
for(long i = 0; i < 80; i++)
{
var readByte = bs0.ReadByte();
if (readByte == 0x0)
{
isBinary = true;
break;
}
}
}
}
if (!isBinary)
{
using (FileStream f0 = file.OpenRead())
{
using (BufferedStream bs0 = new BufferedStream(f0))
{
var byteArray = new byte[6];
for (var i = 0; i < 6; i++)
{
byteArray[i] = (byte)bs0.ReadByte();
}
var text = Encoding.UTF8.GetString(byteArray);
isBinary = text != "solid ";
}
}
}
return isBinary;
}
/**
* @todo test with > USHORT_MAX vertex count meshes
*/
private static Mesh[] CreateMeshWithFacets(IList<Facet> facets)
{
int fl = facets.Count, f = 0, mvc = MAX_FACETS_PER_MESH * 3;
Mesh[] meshes = new Mesh[fl / MAX_FACETS_PER_MESH + 1];
for(int i = 0; i < meshes.Length; i++)
{
int len = System.Math.Min(mvc, (fl - f) * 3);
Vector3[] v = new Vector3[len];
Vector3[] n = new Vector3[len];
int[] t = new int[len];
for(int it = 0; it < len; it += 3)
{
v[it ] = facets[f].a;
v[it+1] = facets[f].b;
v[it+2] = facets[f].c;
n[it ] = facets[f].normal;
n[it+1] = facets[f].normal;
n[it+2] = facets[f].normal;
t[it ] = it;
t[it+1] = it+1;
t[it+2] = it+2;
f++;
}
meshes[i] = new Mesh();
meshes[i].vertices = v;
meshes[i].normals = n;
meshes[i].triangles = t;
}
return meshes;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7dc1dc9c919fa434c915ad64201952dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,12 @@
{
"name": "Unity.ProBuilder.Stl",
"references": [],
"optionalUnityReferences": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": []
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b1c3ae93a8471b34fa031e31cfdd1fbb
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: