1 module armos.graphics.mesh;
2 import armos.types;
3 import armos.math;
4 import armos.graphics;
5 
6 /++
7     ポリゴンで構成された形状を表すclassです.
8 +/
9 class Mesh {
10     public{
11         alias int IndexType;
12 
13         bool isVertsChanged   = false;
14         bool isFaceDirty      = false;
15         bool isIndicesChanged = false;
16 
17         Vector4f[]    vertices;
18         Vector3f[]    normals;
19         Vector3f[]    tangents;
20         Vector4f[]    texCoords0;
21         Vector4f[]    texCoords1;
22         alias texCoords0         texCoords; 
23         armos.types.FloatColor[] colors;
24         IndexType[]              indices;
25 
26         /// テクスチャ座標の数を表します.
27         size_t numTexCoords()const{
28             return texCoords.length;
29         }
30 
31         /// 頂点座標の数を表します.
32         size_t numVertices()const{
33             return vertices.length;
34         }
35 
36         /// 法線ベクトルの数を表します.
37         size_t numNormals()const{
38             return normals.length;
39         }
40 
41         /// 頂点インデックスの数を表します.
42         size_t numIndices()const{
43             return indices.length;
44         }
45 
46         /// meshの描画モードを返します.
47         PrimitiveMode primitiveMode()const{
48             return _primitiveMode;
49         }
50 
51         /// meshの描画モードを指定します.
52         Mesh primitiveMode(in PrimitiveMode mode){
53             _primitiveMode = mode;
54             return this;
55         }
56 
57         /++
58             テクスチャ座標を追加します.
59         +/
60         Mesh addTexCoord(in Vector2f vec){
61             addTexCoord(vec[0], vec[1]);
62             return this;
63         }
64 
65         /++
66             テクスチャ座標を追加します.
67         +/
68         Mesh addTexCoord(in float u, in float v){
69             texCoords ~= Vector4f(u, v, 0f, 1f);
70             return this;
71         }
72 
73         /++
74             頂点座標を追加します.
75         +/
76         Mesh addVertex(in Vector3f vec){
77             vertices ~= Vector4f(vec[0], vec[1], vec[2], 1);
78             isVertsChanged = true;
79             isFaceDirty = true;
80             return this;
81         };
82         unittest{
83             auto mesh = new Mesh;
84             mesh.addVertex(Vector3f(0, 1, 2));
85             mesh.addVertex(Vector3f(3, 4, 5));
86             mesh.addVertex(Vector3f(6, 7, 8));
87             assert(mesh.vertices[1][1] == 4.0);
88             assert(mesh.isFaceDirty);
89             assert(mesh.isVertsChanged);
90         }
91 
92         /++
93             頂点座標を追加します.
94         +/
95         Mesh addVertex(in float x, in float y, in float z){
96             addVertex(Vector3f(x, y, z));
97             return this;
98         }
99 
100         /++
101             法線ベクトルを追加します.
102         +/
103         Mesh addNormal(in Vector3f vec){
104             normals ~= vec;
105             return this;
106         }
107 
108         /++
109             法線ベクトルを追加します.
110         +/
111         Mesh addNormal(in float x, in float y, in float z){
112             addNormal(Vector3f(x, y, z));
113             return this;
114         }
115 
116         /++
117             頂点インデックスを追加します.
118         +/
119         Mesh addIndex(in IndexType index){
120             indices ~= index;
121             isIndicesChanged = true;
122             isFaceDirty = true;
123             return this;
124         };
125         unittest{
126             auto mesh = new Mesh;
127             mesh.addIndex(1);
128             mesh.addIndex(2);
129             mesh.addIndex(3);
130             assert(mesh.indices[1] == 2);
131             assert(mesh.isIndicesChanged);
132             assert(mesh.isFaceDirty);
133         }
134 
135         /++
136             meshを描画します.
137             Params:
138             renderMode = 面,線,点のどれを描画するか指定します.
139         +/
140         Mesh draw(in PolyRenderMode renderMode){
141             currentRenderer.draw(this, renderMode, false, false, false);
142             return this;
143         };
144 
145         /++
146             meshをワイヤフレームで描画します.
147         +/
148         Mesh drawWireFrame(){
149             draw(PolyRenderMode.WireFrame);
150             return this;
151         };
152 
153         /++
154             meshの頂点を点で描画します.
155         +/
156         Mesh drawVertices(){
157             draw(PolyRenderMode.Points);
158             return this;
159         };
160 
161         /++
162             meshの面を塗りつぶして描画します.
163         +/
164         Mesh drawFill(){
165             draw(PolyRenderMode.Fill);
166             return this;
167         };
168 
169         ///
170         Mesh calcNormal(){
171             switch (_primitiveMode) {
172                 case PrimitiveMode.Triangles:
173                     Vector3f[] normalSums = new Vector3f[numVertices];
174                     foreach (ref sum; normalSums) {
175                         sum = Vector3f.zero;
176                     }
177                     for (size_t i = 0; i < numIndices; i+=3){
178                         immutable v1 = vertices[indices[i]].xyz;
179                         immutable v2 = vertices[indices[i+1]].xyz;
180                         immutable v3 = vertices[indices[i+2]].xyz;
181                         immutable n = (v2-v1).vectorProduct(v3-v1).normalized;
182                         normalSums[indices[i]]   += n;
183                         normalSums[indices[i+1]] += n;
184                         normalSums[indices[i+2]] += n;
185                     }
186 
187                     import std.algorithm:map;
188                     import std.array:array;
189                     normals = normalSums.map!(n => n.normalized).array;
190                     break;
191                 default:
192                     return this;
193             }
194             return this;
195         }
196         
197         ///
198         Mesh opBinary(string op:"~")(Mesh rhs){
199             assert(this.primitiveMode == rhs.primitiveMode, "missmatch primitive mode");
200             import std.algorithm;
201             import std.conv;
202             import std.array:array;
203             auto result = new Mesh;
204             result.indices    = this.indices    ~ rhs.indices.map!(i => (i + this.vertices.length).to!int).array;
205             result.vertices   = this.vertices   ~ rhs.vertices;
206             result.normals    = this.normals    ~ rhs.normals;
207             result.tangents   = this.tangents   ~ rhs.tangents;
208             result.texCoords0 = this.texCoords0 ~ rhs.texCoords0;
209             result.texCoords1 = this.texCoords1 ~ rhs.texCoords1;
210             result.colors     = this.colors     ~ rhs.colors;
211             return result;
212         }
213         
214         unittest{
215             alias V = Vector4f;
216             auto m1 = new Mesh;
217             m1.vertices = [
218                 V(1, 0, 0, 1), 
219                 V(0, 2, 0, 1), 
220                 V(0, 0, 3, 1)
221             ];
222             m1.indices = [0, 1, 2];
223             
224             auto m2 = new Mesh;
225             m2.vertices = [
226                 V(4, 0, 0, 1), 
227                 V(0, 5, 0, 1), 
228                 V(0, 0, 6, 1)
229             ];
230             m2.indices = [0, 1, 2];
231             
232             auto m3 = m1 ~ m2;
233             assert(m3.vertices.length == 6);
234             assert(m3.vertices == m1.vertices ~ m2.vertices);
235             assert(m3.indices.length  == m1.indices.length+m2.indices.length);
236             assert(m3.indices == [0, 1, 2, 3, 4, 5]);
237         }
238         
239         ///
240         void opOpAssign(string op:"~")(Mesh rhs){
241             assert(this.primitiveMode == rhs.primitiveMode, "missmatch primitive mode");
242             import std.algorithm;
243             import std.conv;
244             import std.array:array;
245             this.indices    ~= rhs.indices.map!(i => (i + this.vertices.length).to!int).array;
246             this.vertices   ~= rhs.vertices;
247             this.normals    ~= rhs.normals;
248             this.tangents   ~= rhs.tangents;
249             this.texCoords0 ~= rhs.texCoords0;
250             this.texCoords1 ~= rhs.texCoords1;
251             this.colors     ~= rhs.colors;
252         }
253         
254         unittest{
255             alias V = Vector4f;
256             auto m1 = new Mesh;
257             m1.vertices = [
258                 V(1, 0, 0, 1), 
259                 V(0, 2, 0, 1), 
260                 V(0, 0, 3, 1)
261             ];
262             m1.indices = [0, 1, 2];
263             
264             auto m2 = new Mesh;
265             m2.vertices = [
266                 V(4, 0, 0, 1), 
267                 V(0, 5, 0, 1), 
268                 V(0, 0, 6, 1)
269             ];
270             m2.indices = [0, 1, 2];
271             
272             m1 ~= m2;
273             assert(m1.vertices.length == 6);
274             assert(m1.vertices == [
275                 V(1, 0, 0, 1), 
276                 V(0, 2, 0, 1), 
277                 V(0, 0, 3, 1)
278             ] ~ [
279                 V(4, 0, 0, 1), 
280                 V(0, 5, 0, 1), 
281                 V(0, 0, 6, 1)
282             ]);
283             assert(m1.indices.length  == 6);
284             assert(m1.indices == [0, 1, 2, 3, 4, 5]);
285         }
286     }//public
287 
288     private{
289         PrimitiveMode _primitiveMode = PrimitiveMode.Triangles;
290     }//private
291 }//class Mesh