-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathCrft.Component.cs
More file actions
183 lines (166 loc) · 7.06 KB
/
Copy pathCrft.Component.cs
File metadata and controls
183 lines (166 loc) · 7.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
* Idmr.LfdReader.dll, Library file to read and write LFD resource files
* Copyright (C) 2009-2026 Michael Gaisser (mjgaisser@gmail.com)
* Licensed under the MPL v2.0 or later
*
* Full notice in help/Idmr.LfdReader.chm
* Version: 3.0
*/
/* CHANGE LOG
* v3.0, 260821
* [UPD] Indexers replaced with ReadOnlyCollection
* v2.2, 230401
* [NEW] Shape.IsTwoSided and Shape.IsGouraudShaded properties
* v2.1, 221030
* [NEW] Lod.Line class, and Shape.Lines property
* v2.0, 210309
* [NEW] Created
*/
using System;
using System.Collections.ObjectModel;
namespace Idmr.LfdReader
{
public partial class Crft : Resource
{
/// <summary>Represents a complete mesh object.</summary>
public class Component
{
/// <summary>Initialize the mesh with the specified number of Lods (levels of detail).</summary>
/// <param name="lodCount">The count to create.</param>
public Component(int lodCount)
{
Lod[] lods = new Lod[lodCount];
for (int i = 0; i < lodCount; i++) lods[i] = new Lod();
Lods = Array.AsReadOnly(lods);
}
/// <summary>Gets the Lods.</summary>
public ReadOnlyCollection<Lod> Lods { get; internal set; }
}
/// <summary>Represents a single Level of Detail (LOD) mesh.</summary>
public class Lod
{
/// <summary>Gets the distance at which the LOD becomes active.</summary>
public int Distance { get; internal set; }
/// <summary>At 0x1, after the signature.</summary>
public byte Unknown1 { get; internal set; }
/// <summary>At 0x3.</summary>
public byte Unknown2 { get; internal set; }
/// <summary>Gets the color indices.</summary>
public ReadOnlyCollection<byte> ColorIndices { get; internal set; }
/// <summary>Gets the minimum value for the bounding box.</summary>
public Vertex16 MinimumBound { get; internal set; }
/// <summary>Gets the maximum value for the bounding box.</summary>
public Vertex16 MaximumBound { get; internal set; }
/// <summary>Gets the vertices for the Lod.</summary>
public ReadOnlyCollection<Vertex16> MeshVertices { get; internal set; }
/// <summary>Gets the Shapes for the Lod.</summary>
public ReadOnlyCollection<Shape> Shapes { get; internal set; }
// <summary>Gets the unknown data at the end of the Lod</summary>
// <remarks>Might be texture related? Each entry is read-only</remarks>
//public Indexer<UnknownData> UnkData { get; internal set; }
/// <summary>Represents a single point in 3D space.</summary>
public class Vertex16
{
internal Vertex16(byte[] raw, ref int offset)
{
X = BitConverter.ToInt16(raw, offset);
offset += 2;
Y = BitConverter.ToInt16(raw, offset);
offset += 2;
Z = BitConverter.ToInt16(raw, offset);
offset += 2;
}
/// <summary>Gets the X value.</summary>
public short X { get; internal set; }
/// <summary>Gets the Y value.</summary>
public short Y { get; internal set; }
/// <summary>Gets the Z value.</summary>
public short Z { get; internal set; }
/// <summary>Provides quick access to the values.</summary>
/// <param name="index">0-2 for {X, Y, Z}.</param>
/// <returns>The appropriate value, otherwise silently returns <b>0</b>.</returns>
public short this[int index]
{
get
{
if (index == 0) return X;
else if (index == 1) return Y;
else if (index == 2) return Z;
else return 0;
}
internal set
{
if (index == 0) X = value;
else if (index == 1) Y = value;
else if (index == 2) Z = value;
}
}
}
/// <summary>Represents a direction in 3D space.</summary>
/// <remarks>This is a derived class, only adds the <see cref="Magnitude"/> calculation to the inherited <see cref="Vertex16"/>.</remarks>
public class Vector16 : Vertex16
{
internal Vector16(byte[] raw, ref int offset) : base(raw, ref offset) { }
/// <summary>Gets the RSS length of the vector.</summary>
public short Magnitude => (short)Math.Sqrt(X * X + Y * Y + Z * Z);
}
/// <summary>Represents a single line or face within the mesh.</summary>
public class Shape
{
/// <summary>Gets the normal vector of the shape.</summary>
public Vector16 FaceNormal { get; internal set; }
/// <summary>Gets the Shape's Type.</summary>
public byte Type { get; internal set; }
/// <summary>Gets the data array.</summary>
/// <remarks>Length is (Type & 0x0F)*2 + 3. The array of pairs are Vertex indices, the remaining 3 are unknown.</remarks>
public ReadOnlyCollection<byte> Data { get; internal set; }
/// <summary>Gets if the Shape is double-sided.</summary>
/// <remarks>Top bit of <see cref="Type"/>, if <see langword="false"/> then the shape is single-sided.</remarks>
public bool IsTwoSided => (Type & 0x80) == 0x80;
/// <summary>Gets if the Shape uses Gouraud (interpolated) shading.</summary>
/// <remarks>Second bit of <see cref="Type"/>, if <see langword="false"/> then the shape is flat shaded.</remarks>
public bool IsGouraudShaded => (Type & 0x40) == 0x40;
/// <summary>From the separate array following the Shape collection.</summary>
/// <remarks>Looks unique among the other Shapes and can be 0 to ShapeCount, likely an ID value.</remarks>
public byte Unknown1 { get; internal set; }
/// <summary>From the separate array following the Shape collection.</summary>
/// <remarks>Immediately follows Unknown1.</remarks>
public short Unknown2 { get; internal set; }
/// <summary>Gets the lines made up of vertex pairs from <see cref="Data"/>.</summary>
public ReadOnlyCollection<Line> Lines { get; internal set; }
}
/// <summary>Represents a single line within a mesh</summary>
/// <remarks>The indices points to a <see cref="Vertex16"/> within <see cref="MeshVertices"/>.</remarks>
public class Line
{
/// <summary>Initialize with the indicated vertices.</summary>
/// <param name="vertex1">The index of the start vertex.</param>
/// <param name="vertex2">The index of the end vertex</param>
public Line (int vertex1, int vertex2)
{
Vertex1 = vertex1;
Vertex2 = vertex2;
}
/// <summary>Gets the index of the starting vertex.</summary>
public int Vertex1 { get; internal set; }
/// <summary>Gets the index of the end vertex</summary>
public int Vertex2 { get; internal set; }
}
// <summary>Represents an unknown data set at the end of the Mesh data</summary>
/*public class UnknownData
{
/// <summary>From the preceding jump array</summary>
/// <remarks>Probably an ID value, looks unique and can be 0 to ShapeCount</remarks>
public byte Unknown { get; internal set; }
/// <summary>Gets the type of the <see cref="Data"/> structure</summary>
/// <remarks>Looks like this can either equal <b>1</b> or <b>2</b>.</remarks>
public byte Type { get; internal set; }
/// <summary>Gets the data array</summary>
/// <remarks>If Type==1, data is { Unk, ArrayCount } followed by 3*ArrayCount bytes<br/>
/// If Type==2, data is 16 bytes.<br/>
/// Each value is read-only</remarks>
public Indexer<byte> Data { get; internal set; }
}*/
}
}
}