/// <summary> /// Sets a cell at the given cell coordinates. /// </summary> public void SetCell(int x, int y, int z, CellMaterial material, sbyte density) { Chunk chunk; var coord = new Vector3int16(RoundDownDivide(x, ChunkSize), RoundDownDivide(y, ChunkSize), RoundDownDivide(z, ChunkSize)); lock (Locker) { if (!_chunks.TryGetValue(coord, out chunk)) { chunk = new Chunk(this, coord); _chunks[coord] = chunk; } } chunk[ Math.Abs(x % ChunkSize), Math.Abs(y % ChunkSize), Math.Abs(z % ChunkSize)] = new Cell(material, density); if (chunk.OccupiedCells == 0) { _chunks.TryRemove(coord); } }
private void GenerateVertex(ref Vector3int16 chunkPos, ref Vector3int16 cellPos, ref List <TerrainVertex> vertices, int lod, long t, ref byte v0, ref byte v1, ref sbyte d0, ref sbyte d1, ref Vector3 normal) { var p0 = (Vector3)(chunkPos + CornerIndex[v0] * lod); var p1 = (Vector3)(chunkPos + CornerIndex[v1] * lod); var q = InterpolateVoxelVector(t, ref p0, ref p1); vertices.Add(new TerrainVertex(ref q, ref normal)); }
public ReuseCell GetReusedIndex(Vector3int16 pos, byte rDir) { int rx = rDir & 0x01; int rz = (rDir >> 1) & 0x01; int ry = (rDir >> 2) & 0x01; int dx = pos.x - rx; int dy = pos.y - ry; int dz = pos.z - rz; Debug.Assert(dx >= 0 && dy >= 0 && dz >= 0); return(_cache[dx & 1][dy * chunkSize + dz]); }
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node) { Vector3int16 value = prop.CastValue <Vector3int16>(); XmlElement x = doc.CreateElement("X"); x.InnerText = value.X.ToString(); node.AppendChild(x); XmlElement y = doc.CreateElement("Y"); y.InnerText = value.Y.ToString(); node.AppendChild(y); XmlElement z = doc.CreateElement("Z"); z.InnerText = value.Z.ToString(); node.AppendChild(z); }
/// <summary> /// Returns the cell at the given cell coordinates. /// </summary> public Cell GetCell(int x, int y, int z) { Chunk chunk; var coord = new Vector3int16(RoundDownDivide(x, ChunkSize), RoundDownDivide(y, ChunkSize), RoundDownDivide(z, ChunkSize)); _chunks.TryGetValue(coord, out chunk); if (chunk == null) { return(default(Cell)); } var cell = chunk[ Math.Abs(x) % ChunkSize, Math.Abs(y) % ChunkSize, Math.Abs(z) % ChunkSize]; return(cell); }
public void GenLodCell(Terrain.Chunk chunk, int lod, out TerrainVertex[] resultVertices, out ushort[] resultIndices) { var vertices = new List <TerrainVertex>(); var indices = new List <ushort>(); const short chunkSize = Terrain.ChunkSize; for (short x = 0; x < chunkSize; x++) { for (short y = 0; y < chunkSize; y++) { for (short z = 0; z < chunkSize; z++) { var cellPos = new Vector3int16(x, y, z); PolygonizeCell(chunk.Position, cellPos, ref vertices, ref indices, lod); } } } resultVertices = vertices.ToArray(); resultIndices = indices.ToArray(); }
public ReuseCell this[Vector3int16 v] { set { this[v.x, v.y, v.z] = value; } }
private void PolygonizeCell(Vector3int16 offsetPos, Vector3int16 pos, ref List <TerrainVertex> vertices, ref List <ushort> indices, int lod) { Debug.Assert(lod >= 1, "Level of Detail must be greater than 1"); offsetPos *= Terrain.ChunkSize; offsetPos += pos * lod; byte directionMask = (byte)((pos.x > 0 ? 1 : 0) | ((pos.z > 0 ? 1 : 0) << 1) | ((pos.y > 0 ? 1 : 0) << 2)); sbyte[] density = new sbyte[8]; for (int i = 0; i < density.Length; i++) { density[i] = _volume[offsetPos + CornerIndex[i] * lod].Density; } byte caseCode = GetCaseCode(density); if ((caseCode ^ ((density[7] >> 7) & 0xFF)) == 0) //for this cases there is no triangulation { return; } var cornerNormals = new Vector3[8]; for (int i = 0; i < 8; i++) { var p = offsetPos + CornerIndex[i] * lod; float nx = (_volume.GetCell(p.x + 1, p.y, p.z).Density - _volume[p - Vector3int16.Right].Density) * 0.5f; float ny = (_volume[p + Vector3int16.Up].Density - _volume[p - Vector3int16.Up].Density) * 0.5f; float nz = (_volume[p + Vector3int16.Backward].Density - _volume[p - Vector3int16.Backward].Density) * 0.5f; cornerNormals[i].Set(nx, ny, nz); cornerNormals[i].Normalize(); } byte regularCellClass = RegularCellClass[caseCode]; ushort[] vertexLocations = RegularVertexData[caseCode]; var c = RegularCellData[regularCellClass]; long vertexCount = c.GetVertexCount(); long triangleCount = c.GetTriangleCount(); byte[] indexOffset = c.Indices(); //index offsets for current cell ushort[] mappedIndizes = new ushort[indexOffset.Length]; //array with real indizes for current cell for (int i = 0; i < vertexCount; i++) { byte edge = (byte)(vertexLocations[i] >> 8); byte reuseIndex = (byte)(edge & 0xF); //Vertex id which should be created or reused 1,2 or 3 byte rDir = (byte)(edge >> 4); //the direction to go to reach a previous cell for reusing byte v1 = (byte)((vertexLocations[i]) & 0x0F); //Second Corner Index byte v0 = (byte)((vertexLocations[i] >> 4) & 0x0F); //First Corner Index sbyte d0 = density[v0]; sbyte d1 = density[v1]; //Vector3f n0 = cornerNormals[v0]; //Vector3f n1 = cornerNormals[v1]; Debug.Assert(v1 > v0); int t = (d1 << 8) / (d1 - d0); int u = 0x0100 - t; float t0 = t / 256f; float t1 = u / 256f; int index = -1; if (UseCache && v1 != 7 && (rDir & directionMask) == rDir) { Debug.Assert(reuseIndex != 0); ReuseCell cell = _cache.GetReusedIndex(pos, rDir); index = cell.Verts[reuseIndex]; } if (index == -1) { var normal = cornerNormals[v0] * t0 + cornerNormals[v1] * t1; GenerateVertex(ref offsetPos, ref pos, ref vertices, lod, t, ref v0, ref v1, ref d0, ref d1, ref normal); index = vertices.Count - 1; } if ((rDir & 8) != 0) { _cache.SetReusableIndex(pos, reuseIndex, (ushort)(vertices.Count - 1)); } mappedIndizes[i] = (ushort)index; } for (int t = 0; t < triangleCount; t++) { for (int i = 0; i < 3; i++) { indices.Add(mappedIndizes[c.Indices()[t * 3 + i]]); } } }
public void Read(BinaryReader propReader) { var encode = propReader.ReadInt32(); short declaringTypeId; short propertyTag; DecodePropertyTag(encode, out declaringTypeId, out propertyTag); EncodeTag = encode; DeclaringTypeId = declaringTypeId; PropertyTag = propertyTag; CachedProperty cached; if (TypeRecord.Type.TaggedProperties.TryGetValue(encode, out cached)) { Name = cached.Name; Property = cached; PropertyType = cached.PropertyType; } DataType = (DataType)propReader.ReadByte(); while (propReader.BaseStream.Position < propReader.BaseStream.Length) { switch (DataType) { case DataType.String: Data.Add(propReader.ReadString()); break; case DataType.Content: Data.Add(propReader.ReadString()); break; case DataType.Boolean: Data.Add(propReader.ReadBoolean()); break; case DataType.Int16: Data.Add(propReader.ReadInt16()); break; case DataType.Int32: Data.Add(propReader.ReadInt32()); break; case DataType.Int64: Data.Add(propReader.ReadInt64()); break; case DataType.Single: Data.Add(propReader.ReadSingle()); break; case DataType.Double: Data.Add(propReader.ReadDouble()); break; case DataType.Enum: Data.Add(propReader.ReadInt16()); break; case DataType.FontFamily: Data.Add(new FontFamily(propReader.ReadString())); break; case DataType.Referent: var referent = propReader.ReadInt32(); Data.Add(referent == -1 ? Referent.Null : Context.GlobalReferents[referent]); break; case DataType.UserData: if (VisualC.CompareMemory(propReader.ReadBytes(3), _nullChars, 3) == 0) { Data.Add(null); } else { propReader.BaseStream.Position -= 3; // go back to start var id = propReader.ReadByte(); IDataType userData; switch (id) { case 0: userData = new Vector3(); break; case 1: userData = new Vector2(); break; case 2: userData = new Vector4(); break; case 3: userData = new Colour(); break; case 4: userData = new Axes(); break; case 5: userData = new CFrame(); break; case 6: userData = new UDim2(); break; case 7: userData = new ColourSequence(); break; case 8: userData = new NumberSequence(); break; case 9: userData = new NumberRange(); break; case 10: userData = new Faces(); break; case 11: userData = new Matrix3(); break; case 12: userData = new PhysicalProperties(); break; case 13: userData = new Plane(); break; case 15: userData = new Ray(); break; case 16: userData = new Region3(); break; case 17: userData = new Vector3int16(); break; case 18: userData = new Region3int16(); break; case 19: userData = new DateTime(); break; case 20: userData = new TimeSpan(); break; case 21: userData = new BinaryData(); break; case 22: userData = new MaterialNodeCollection(); break; case 23: userData = new InstanceId(); break; case 24: userData = new FontFamily(); break; default: throw new IndexOutOfRangeException($"No DataType with data ID \"{id}\" found."); } userData.Load(propReader); Data.Add(userData); } break; default: throw new ArgumentOutOfRangeException(); } } }
internal Region3int16(Attribute attr) { Min = new Vector3int16(attr); Max = new Vector3int16(attr); }
internal Chunk(Terrain terrain, Vector3int16 coord) { _terrain = terrain; Position = coord; _cells = new Cell[CellsPerChunk]; }
private void SetCell(Vector3int16 cellPos, CellMaterial material, sbyte density) { SetCell(cellPos.x, cellPos.y, cellPos.z, material, density); }
internal Cell GetCell(Vector3int16 cellPos) { return(GetCell(cellPos.x, cellPos.y, cellPos.z)); }
internal Cell this[Vector3int16 pos] { get { return(GetCell(pos)); } set { SetCell(pos, value.Material, value.Density); } }
internal void SetReusableIndex(Vector3int16 pos, byte reuseIndex, ushort p) { _cache[pos.x & 1][pos.y * chunkSize + pos.z].Verts[reuseIndex] = p; }
public void Save(BinaryRobloxFileWriter writer) { BinaryRobloxFile file = writer.File; File = file; INST inst = file.Classes[ClassIndex]; var props = new List <Property>(); foreach (int instId in inst.InstanceIds) { Instance instance = file.Instances[instId]; var instProps = instance.Properties; if (!instProps.TryGetValue(Name, out Property prop)) { throw new Exception($"Property {Name} must be defined in {instance.GetFullName()}!"); } else if (prop.Type != Type) { throw new Exception($"Property {Name} is not using the correct type in {instance.GetFullName()}!"); } props.Add(prop); } writer.Write(ClassIndex); writer.WriteString(Name); writer.Write(TypeId); switch (Type) { case PropertyType.String: props.ForEach(prop => { byte[] buffer = prop.HasRawBuffer ? prop.RawBuffer : null; if (buffer == null) { string value = prop.CastValue <string>(); buffer = Encoding.UTF8.GetBytes(value); } writer.Write(buffer.Length); writer.Write(buffer); }); break; case PropertyType.Bool: { props.ForEach(prop => { bool value = prop.CastValue <bool>(); writer.Write(value); }); break; } case PropertyType.Int: { var ints = props .Select(prop => prop.CastValue <int>()) .ToList(); writer.WriteInts(ints); break; } case PropertyType.Float: { var floats = props .Select(prop => prop.CastValue <float>()) .ToList(); writer.WriteFloats(floats); break; } case PropertyType.Double: { props.ForEach(prop => { double value = prop.CastValue <double>(); writer.Write(BinaryRobloxFileWriter.GetBytes(value)); }); break; } case PropertyType.UDim: { var UDim_Scales = new List <float>(); var UDim_Offsets = new List <int>(); props.ForEach(prop => { UDim value = prop.CastValue <UDim>(); UDim_Scales.Add(value.Scale); UDim_Offsets.Add(value.Offset); }); writer.WriteFloats(UDim_Scales); writer.WriteInts(UDim_Offsets); break; } case PropertyType.UDim2: { var UDim2_Scales_X = new List <float>(); var UDim2_Scales_Y = new List <float>(); var UDim2_Offsets_X = new List <int>(); var UDim2_Offsets_Y = new List <int>(); props.ForEach(prop => { UDim2 value = prop.CastValue <UDim2>(); UDim2_Scales_X.Add(value.X.Scale); UDim2_Scales_Y.Add(value.Y.Scale); UDim2_Offsets_X.Add(value.X.Offset); UDim2_Offsets_Y.Add(value.Y.Offset); }); writer.WriteFloats(UDim2_Scales_X); writer.WriteFloats(UDim2_Scales_Y); writer.WriteInts(UDim2_Offsets_X); writer.WriteInts(UDim2_Offsets_Y); break; } case PropertyType.Ray: { props.ForEach(prop => { Ray ray = prop.CastValue <Ray>(); Vector3 pos = ray.Origin; writer.Write(pos.X); writer.Write(pos.Y); writer.Write(pos.Z); Vector3 dir = ray.Direction; writer.Write(dir.X); writer.Write(dir.Y); writer.Write(dir.Z); }); break; } case PropertyType.Faces: case PropertyType.Axes: { props.ForEach(prop => { byte value = prop.CastValue <byte>(); writer.Write(value); }); break; } case PropertyType.BrickColor: { var brickColorIds = props .Select(prop => prop.CastValue <BrickColor>()) .Select(value => value.Number) .ToList(); writer.WriteInts(brickColorIds); break; } case PropertyType.Color3: { var Color3_R = new List <float>(); var Color3_G = new List <float>(); var Color3_B = new List <float>(); props.ForEach(prop => { Color3 value = prop.CastValue <Color3>(); Color3_R.Add(value.R); Color3_G.Add(value.G); Color3_B.Add(value.B); }); writer.WriteFloats(Color3_R); writer.WriteFloats(Color3_G); writer.WriteFloats(Color3_B); break; } case PropertyType.Vector2: { var Vector2_X = new List <float>(); var Vector2_Y = new List <float>(); props.ForEach(prop => { Vector2 value = prop.CastValue <Vector2>(); Vector2_X.Add(value.X); Vector2_Y.Add(value.Y); }); writer.WriteFloats(Vector2_X); writer.WriteFloats(Vector2_Y); break; } case PropertyType.Vector3: { var Vector3_X = new List <float>(); var Vector3_Y = new List <float>(); var Vector3_Z = new List <float>(); props.ForEach(prop => { Vector3 value = prop.CastValue <Vector3>(); Vector3_X.Add(value.X); Vector3_Y.Add(value.Y); Vector3_Z.Add(value.Z); }); writer.WriteFloats(Vector3_X); writer.WriteFloats(Vector3_Y); writer.WriteFloats(Vector3_Z); break; } case PropertyType.CFrame: case PropertyType.Quaternion: case PropertyType.OptionalCFrame: { var CFrame_X = new List <float>(); var CFrame_Y = new List <float>(); var CFrame_Z = new List <float>(); if (Type == PropertyType.OptionalCFrame) { writer.Write((byte)PropertyType.CFrame); } props.ForEach(prop => { CFrame value = null; if (prop.Value is Quaternion q) { value = q.ToCFrame(); } else { value = prop.CastValue <CFrame>(); } if (value == null) { value = new CFrame(); } Vector3 pos = value.Position; CFrame_X.Add(pos.X); CFrame_Y.Add(pos.Y); CFrame_Z.Add(pos.Z); int orientId = value.GetOrientId(); writer.Write((byte)(orientId + 1)); if (orientId == -1) { if (Type == PropertyType.Quaternion) { Quaternion quat = new Quaternion(value); writer.Write(quat.X); writer.Write(quat.Y); writer.Write(quat.Z); writer.Write(quat.W); } else { float[] components = value.GetComponents(); for (int i = 3; i < 12; i++) { float component = components[i]; writer.Write(component); } } } }); writer.WriteFloats(CFrame_X); writer.WriteFloats(CFrame_Y); writer.WriteFloats(CFrame_Z); if (Type == PropertyType.OptionalCFrame) { writer.Write((byte)PropertyType.Bool); props.ForEach(prop => { if (prop.Value is null) { writer.Write(false); return; } if (prop.Value is Optional <CFrame> optional) { writer.Write(optional.HasValue); return; } var cf = prop.Value as CFrame; writer.Write(cf != null); }); } break; } case PropertyType.Enum: { var enums = new List <uint>(); props.ForEach(prop => { if (prop.Value is uint raw) { enums.Add(raw); return; } int signed = (int)prop.Value; uint value = (uint)signed; enums.Add(value); }); writer.WriteInterleaved(enums); break; } case PropertyType.Ref: { var InstanceIds = new List <int>(); props.ForEach(prop => { int referent = -1; if (prop.Value != null) { Instance value = prop.CastValue <Instance>(); if (value.IsDescendantOf(File)) { string refValue = value.Referent; int.TryParse(refValue, out referent); } } InstanceIds.Add(referent); }); writer.WriteInstanceIds(InstanceIds); break; } case PropertyType.Vector3int16: { props.ForEach(prop => { Vector3int16 value = prop.CastValue <Vector3int16>(); writer.Write(value.X); writer.Write(value.Y); writer.Write(value.Z); }); break; } case PropertyType.NumberSequence: { props.ForEach(prop => { NumberSequence value = prop.CastValue <NumberSequence>(); var keyPoints = value.Keypoints; writer.Write(keyPoints.Length); foreach (var keyPoint in keyPoints) { writer.Write(keyPoint.Time); writer.Write(keyPoint.Value); writer.Write(keyPoint.Envelope); } }); break; } case PropertyType.ColorSequence: { props.ForEach(prop => { ColorSequence value = prop.CastValue <ColorSequence>(); var keyPoints = value.Keypoints; writer.Write(keyPoints.Length); foreach (var keyPoint in keyPoints) { Color3 color = keyPoint.Value; writer.Write(keyPoint.Time); writer.Write(color.R); writer.Write(color.G); writer.Write(color.B); writer.Write(keyPoint.Envelope); } }); break; } case PropertyType.NumberRange: { props.ForEach(prop => { NumberRange value = prop.CastValue <NumberRange>(); writer.Write(value.Min); writer.Write(value.Max); }); break; } case PropertyType.Rect: { var Rect_X0 = new List <float>(); var Rect_Y0 = new List <float>(); var Rect_X1 = new List <float>(); var Rect_Y1 = new List <float>(); props.ForEach(prop => { Rect value = prop.CastValue <Rect>(); Vector2 min = value.Min; Rect_X0.Add(min.X); Rect_Y0.Add(min.Y); Vector2 max = value.Max; Rect_X1.Add(max.X); Rect_Y1.Add(max.Y); }); writer.WriteFloats(Rect_X0); writer.WriteFloats(Rect_Y0); writer.WriteFloats(Rect_X1); writer.WriteFloats(Rect_Y1); break; } case PropertyType.PhysicalProperties: { props.ForEach(prop => { bool custom = (prop.Value != null); writer.Write(custom); if (custom) { PhysicalProperties value = prop.CastValue <PhysicalProperties>(); writer.Write(value.Density); writer.Write(value.Friction); writer.Write(value.Elasticity); writer.Write(value.FrictionWeight); writer.Write(value.ElasticityWeight); } }); break; } case PropertyType.Color3uint8: { var Color3uint8_R = new List <byte>(); var Color3uint8_G = new List <byte>(); var Color3uint8_B = new List <byte>(); props.ForEach(prop => { Color3uint8 value = prop.CastValue <Color3uint8>(); Color3uint8_R.Add(value.R); Color3uint8_G.Add(value.G); Color3uint8_B.Add(value.B); }); byte[] rBuffer = Color3uint8_R.ToArray(); writer.Write(rBuffer); byte[] gBuffer = Color3uint8_G.ToArray(); writer.Write(gBuffer); byte[] bBuffer = Color3uint8_B.ToArray(); writer.Write(bBuffer); break; } case PropertyType.Int64: { var longs = new List <long>(); props.ForEach(prop => { long value = prop.CastValue <long>(); longs.Add(value); }); writer.WriteInterleaved(longs, value => { // Move the sign bit to the front. return((value << 1) ^ (value >> 63)); }); break; } case PropertyType.SharedString: { var sharedKeys = new List <uint>(); SSTR sstr = file.SSTR; if (sstr == null) { sstr = new SSTR(); file.SSTR = sstr; } props.ForEach(prop => { var shared = prop.CastValue <SharedString>(); if (shared == null) { byte[] empty = Array.Empty <byte>(); shared = SharedString.FromBuffer(empty); } string key = shared.Key; if (!sstr.Lookup.ContainsKey(key)) { uint id = (uint)sstr.Lookup.Count; sstr.Strings.Add(id, shared); sstr.Lookup.Add(key, id); } uint hashId = sstr.Lookup[key]; sharedKeys.Add(hashId); }); writer.WriteInterleaved(sharedKeys); break; } case PropertyType.ProtectedString: { props.ForEach(prop => { var protect = prop.CastValue <ProtectedString>(); byte[] buffer = protect.RawBuffer; writer.Write(buffer.Length); writer.Write(buffer); }); break; } case PropertyType.UniqueId: { props.ForEach(prop => { var guid = prop.CastValue <Guid>(); byte[] buffer = guid.ToByteArray(); writer.Write(buffer); }); break; } default: { RobloxFile.LogError($"Unhandled property type: {Type} in {this}!"); break; } } }
internal Cell this[Vector3int16 index] { get { return(this[index.x, index.y, index.z]); } set { this[index.x, index.y, index.z] = value; } }
public Region3int16(Vector3int16 min = null, Vector3int16 max = null) { Min = min ?? new Vector3int16(); Max = max ?? new Vector3int16(); }
public Region3int16(Vector3int16 min, Vector3int16 max) { Min = min; Max = max; }