1 /* Written by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 // ////////////////////////////////////////////////////////////////////////// //
25 final class EliteModel
{
27 static align(1) struct UV
{
32 static struct Texture
{
39 T
* xalloc(T
) (uint count
) {
40 import core
.stdc
.stdlib
: malloc
;
41 import core
.stdc
.string
: memset
;
43 auto res
= malloc(T
.sizeof
*count
);
44 if (res
is null) assert(0, "out of memory");
45 memset(res
, 0, T
.sizeof
*count
);
49 void xfree(T
) (ref T
* ptr
) {
51 import core
.stdc
.stdlib
: free
;
64 uint* mids
; // face material array
68 uint* inds
; // just a big array of indexes
69 uint subCount
; // submodels
70 uint* subfcs
; // faces in each submodel
71 ubyte* subsms
; // smoothness
83 this (const(char)[] fname
) { load(fname
); }
84 this (VFile fl
) { load(fl
); }
86 // don't unload textures here, as OpenGL context may be already destroyed
87 ~this () { freeData(); }
91 foreach (immutable idx
; 0..texCount
) xfree(texts
[idx
].data
);
112 import core
.stdc
.stdlib
: free
;
113 foreach (immutable idx
; 0..texCount
) xfree(texts
[idx
].data
);
117 glBindTexture(GL_TEXTURE_2D
, 0);
118 foreach (immutable idx
; 0..texCount
) {
119 if (texts
[idx
].tid
) {
120 glDeleteTextures(1, &texts
[idx
].tid
);
127 foreach (immutable idx
; 0..texCount
) {
128 if (texts
[idx
].tid
) continue;
129 if (texts
[idx
].data
is null) continue;
131 uint wrapOpt
= GL_REPEAT
;
132 uint filterOpt
= GL_LINEAR
;
134 glGenTextures(1, &texts
[idx
].tid
);
135 glBindTexture(GL_TEXTURE_2D
, texts
[idx
].tid
);
136 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrapOpt
);
137 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrapOpt
);
138 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, filterOpt
);
139 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, filterOpt
);
140 //float[4] bclr = 0.0;
141 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
142 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, texts
[idx
].width
, texts
[idx
].height
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, texts
[idx
].data
);
146 static uint glClearFlags () { pragma(inline
, true); return (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT
); }
148 // setup model and view matrices before calling this
149 // also, setup lighting
151 if (texCount
== 0) return;
153 glEnable(GL_TEXTURE_2D
);
154 glEnable(GL_DEPTH_TEST
);
155 //glDisable(GL_LIGHTING);
156 glDisable(GL_DITHER
);
159 glEnable(GL_RESCALE_NORMAL
);
160 glDisable(GL_SCISSOR_TEST
);
161 glDisable(GL_STENCIL_TEST
);
162 glDisable(GL_COLOR_MATERIAL
);
164 glDepthFunc(GL_LEQUAL
);
165 glHint(GL_PERSPECTIVE_CORRECTION_HINT
, GL_NICEST
);
167 glEnable(GL_CULL_FACE
);
168 //glCullFace(GL_FRONT);
172 glDisable(GL_CULL_FACE
); // this way we can draw any model
174 glEnableClientState(GL_VERTEX_ARRAY
);
175 glEnableClientState(GL_NORMAL_ARRAY
);
176 glEnableClientState(GL_TEXTURE_COORD_ARRAY
);
178 glVertexPointer(3, GL_FLOAT
, 0, verts
);
179 glNormalPointer(GL_FLOAT
, 0, norms
);
180 glTexCoordPointer(2, GL_FLOAT
, 0, uvs
);
183 foreach (immutable smidx
, uint len
; subfcs
[0..subCount
]) {
185 glShadeModel(subsms
[smidx
] ? GL_SMOOTH
: GL_FLAT
);
186 while (s
< faceCount
&& s
< end
) {
189 while (e
< faceCount
&& e
< end
&& mids
[e
] == tid
) ++e
;
190 glBindTexture(GL_TEXTURE_2D
, texts
[tid
].tid
);
191 glDrawElements(GL_TRIANGLES
, (e
-s
)*3, GL_UNSIGNED_INT
, inds
+s
*3);
196 glBindTexture(GL_TEXTURE_2D
, 0);
197 glDisableClientState(GL_VERTEX_ARRAY
);
198 glDisableClientState(GL_NORMAL_ARRAY
);
199 glDisableClientState(GL_TEXTURE_COORD_ARRAY
);
202 void glDrawExhaust () {
203 // "12.87 -6.34 -54.9 7.45 7.46 6"
204 // "-12.87 -6.34 -54.9 7.45 7.46 6"
206 glEnable(GL_TEXTURE_2D
);
207 glEnable(GL_DEPTH_TEST
);
208 //glDisable(GL_LIGHTING);
209 glDisable(GL_DITHER
);
210 glEnable(GL_COLOR_MATERIAL
);
213 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
215 glDepthFunc(GL_LEQUAL
);
216 glHint(GL_PERSPECTIVE_CORRECTION_HINT
, GL_NICEST
);
218 glEnable(GL_CULL_FACE
);
219 //glCullFace(GL_FRONT);
223 glDisable(GL_CULL_FACE
); // this way we can draw any model
224 glShadeModel(GL_FLAT
);
226 void drawTri() (in auto ref vec3 p0
, in auto ref vec3 p1
, in auto ref vec3 p2
) {
228 vec3 n
= (p1
-p0
).cross(p2
-p0
).normalized
;
229 glBegin(GL_TRIANGLES
);
230 glNormal3f(n
.x
, n
.y
, n
.z
);
231 glVertex3f(p0
.x
, p0
.y
, p0
.z
);
232 glVertex3f(p1
.x
, p1
.y
, p1
.z
);
233 glVertex3f(p2
.x
, p2
.y
, p2
.z
);
237 void drawCone() (/*in auto ref*/ vec3 pos
, /*in auto ref*/ vec3 size
) {
238 //float zlen = size.z;
239 //if (zlen < 0) zlen = -zlen;
240 auto zlen
= bbox
[1].z
-bbox
[0].z
;
241 if (zlen
< 0) zlen
= -zlen
;
244 glColor4f(0, 0.5, 0.8, 0.2);
248 pos
-vec3(size
.x
, size
.y
, 0),
249 pos
-vec3(0, 0, -zlen
),
250 pos
-vec3(size
.x
, -size
.y
, 0),
253 pos
-vec3(-size
.x
, -size
.y
, 0),
254 pos
-vec3(0, 0, -zlen
),
255 pos
-vec3(-size
.x
, size
.y
, 0),
258 pos
-vec3(-size
.x
, size
.y
, 0),
259 pos
-vec3(0, 0, -zlen
),
260 pos
-vec3(size
.x
, size
.y
, 0),
263 pos
-vec3(size
.x
, -size
.y
, 0),
264 pos
-vec3(0, 0, -zlen
),
265 pos
-vec3(-size
.x
, -size
.y
, 0),
269 //drawCone(vec3(12.87, -6.34, 54.9), vec3(7.45, 7.46, 6));
270 foreach (immutable ei
; 0..exCount
) {
271 drawCone(exPos
[ei
], exSize
[ei
]);
274 glColor4f(1, 1, 1, 1);
275 glDisable(GL_CULL_FACE
); // this way we can draw any model
280 void load (const(char)[] fname
) {
283 if (name
.length
== 0) {
284 name
= fname
.baseName
.stripExtension
.idup
;
285 if (name
.length
> 3 && name
[0..4] == "oxm:") name
= name
[4..$];
287 if (idName
.length
== 0) idName
= name
;
288 if (dispName
.length
== 0) dispName
= name
;
291 void load (VFile fl
) {
292 scope(failure
) freeData();
295 fl
.rawReadExact(sign
[]);
297 if (sign
== "EMD1") {
300 auto fcount
= fl
.readNum
!ushort;
301 if (fcount
== 0) throw new Exception("invalid model file");
302 // number of textures
303 auto tcount
= fl
.readNum
!ushort;
304 if (tcount
== 0 || tcount
> fcount
) throw new Exception("invalid model file");
306 mids
= xalloc
!uint(fcount
);
307 verts
= xalloc
!vec3(fcount
*3);
308 norms
= xalloc
!vec3(fcount
*3);
309 uvs
= xalloc
!UV(fcount
*3);
310 inds
= xalloc
!uint(fcount
*3);
312 subfcs
= xalloc
!uint(fcount
);
314 subsms
= xalloc
!ubyte(fcount
);
316 foreach (immutable idx
, ref n
; inds
[0..fcount
*3]) n
= cast(uint)idx
; // yep
319 foreach (immutable fidx
; 0..fcount
) {
321 mids
[fidx
] = fl
.readNum
!ushort;
322 if (mids
[fidx
] >= tcount
) throw new Exception("invalid model file");
324 foreach (immutable vn
; 0..3) {
326 uvs
[fidx
*3+vn
].u
= fl
.readNum
!float;
327 uvs
[fidx
*3+vn
].v
= fl
.readNum
!float;
329 verts
[fidx
*3+vn
].x
= fl
.readNum
!float;
330 verts
[fidx
*3+vn
].y
= fl
.readNum
!float;
331 verts
[fidx
*3+vn
].z
= fl
.readNum
!float;
333 norms
[fidx
*3+vn
].x
= fl
.readNum
!float;
334 norms
[fidx
*3+vn
].y
= fl
.readNum
!float;
335 norms
[fidx
*3+vn
].z
= fl
.readNum
!float;
339 texts
= xalloc
!Texture(tcount
);
341 foreach (immutable idx
; 0..tcount
) {
343 auto w
= fl
.readNum
!ushort;
344 auto h
= fl
.readNum
!ushort;
345 if (w
< 1 || h
< 1) throw new Exception("invalid model file");
347 auto csz
= fl
.readNum
!uint;
348 if (csz
== 0 || csz
> int.max
) throw new Exception("invalid model file");
351 auto zs
= wrapZLibStreamRO(fl
, VFSZLibMode
.ZLib
, w
*h
*4, ep
, csz
);
352 //auto data = new ubyte[](w*h*4);
353 ubyte* data
= xalloc
!ubyte(w
*h
*4);
354 scope(failure
) xfree(data
);
355 zs
.rawReadExact(data
[0..w
*h
*4]);
356 //texts ~= Texture(w, h, data, 0);
357 texts
[idx
].width
= w
;
358 texts
[idx
].height
= h
;
359 texts
[idx
].data
= data
;
365 } else if (sign
== "EMD2") {
368 auto fcount
= fl
.readNum
!ushort;
369 if (fcount
== 0) throw new Exception("invalid model file");
370 // number of textures
371 auto tcount
= fl
.readNum
!ushort;
372 if (tcount
== 0 || tcount
> fcount
) throw new Exception("invalid model file");
373 // number of submodels
374 auto scount
= fl
.readNum
!ushort;
375 if (scount
== 0 || scount
> fcount
) throw new Exception("invalid model file");
376 // number of exhausts
377 auto ecount
= fl
.readNum
!ushort;
378 if (/*ecount == 0 ||*/ ecount
> 255) throw new Exception("invalid model file");
380 mids
= xalloc
!uint(fcount
);
381 verts
= xalloc
!vec3(fcount
*3);
382 norms
= xalloc
!vec3(fcount
*3);
383 uvs
= xalloc
!UV(fcount
*3);
384 inds
= xalloc
!uint(fcount
*3);
386 subfcs
= xalloc
!uint(scount
);
387 subsms
= xalloc
!ubyte(scount
);
388 foreach (immutable idx
, ref n
; inds
[0..fcount
*3]) n
= cast(uint)idx
; // yep
391 exPos
= xalloc
!vec3(ecount
);
392 exSize
= xalloc
!vec3(ecount
);
396 auto len
= fl
.readNum
!ubyte;
398 auto s
= new char[](len
);
399 fl
.rawReadExact(s
[]);
400 return cast(string
)s
; // it is safe to cast here
410 foreach (ref uint c
; subfcs
[0..scount
]) c
= fl
.readNum
!ushort;
412 //foreach (ref ubyte v; subsms[0..scount]) v = fl.readNum!ubyte;
413 fl
.rawReadExact(subsms
[0..scount
]);
416 foreach (immutable ei
; 0..ecount
) {
417 exPos
[ei
].x
= fl
.readNum
!float;
418 exPos
[ei
].y
= fl
.readNum
!float;
419 exPos
[ei
].z
= fl
.readNum
!float;
420 exSize
[ei
].x
= fl
.readNum
!float;
421 exSize
[ei
].y
= fl
.readNum
!float;
422 exSize
[ei
].z
= fl
.readNum
!float;
426 foreach (immutable fidx
; 0..fcount
) {
428 mids
[fidx
] = fl
.readNum
!ushort;
429 if (mids
[fidx
] >= tcount
) throw new Exception("invalid model file");
431 foreach (immutable vn
; 0..3) {
433 uvs
[fidx
*3+vn
].u
= fl
.readNum
!float;
434 uvs
[fidx
*3+vn
].v
= fl
.readNum
!float;
436 verts
[fidx
*3+vn
].x
= fl
.readNum
!float;
437 verts
[fidx
*3+vn
].y
= fl
.readNum
!float;
438 verts
[fidx
*3+vn
].z
= fl
.readNum
!float;
440 norms
[fidx
*3+vn
].x
= fl
.readNum
!float;
441 norms
[fidx
*3+vn
].y
= fl
.readNum
!float;
442 norms
[fidx
*3+vn
].z
= fl
.readNum
!float;
446 // material (texture) colors
448 foreach (immutable _; 0..tcount) {
449 fl.writeNum!float(mat.colorAmb[0]);
450 fl.writeNum!float(mat.colorAmb[1]);
451 fl.writeNum!float(mat.colorAmb[2]);
452 fl.writeNum!float(mat.colorAmb[3]);
453 fl.writeNum!float(mat.colorDiff[0]);
454 fl.writeNum!float(mat.colorDiff[1]);
455 fl.writeNum!float(mat.colorDiff[2]);
456 fl.writeNum!float(mat.colorDiff[3]);
457 fl.writeNum!float(mat.colorSpec[0]);
458 fl.writeNum!float(mat.colorSpec[1]);
459 fl.writeNum!float(mat.colorSpec[2]);
460 fl.writeNum!float(mat.colorSpec[3]);
461 fl.writeNum!float(mat.shininess);
464 fl
.seek(tcount
*(13*float.sizeof
), Seek
.Cur
);
468 texts
= xalloc
!Texture(tcount
);
469 foreach (immutable idx
; 0..tcount
) {
471 auto w
= fl
.readNum
!ushort;
472 auto h
= fl
.readNum
!ushort;
473 if (w
< 1 || h
< 1) throw new Exception("invalid model file");
474 auto compType
= fl
.readNum
!ubyte;
476 ubyte* data
= xalloc
!ubyte(w
*h
*4);
477 scope(failure
) xfree(data
);
478 fl
.rawReadExact(data
[0..w
*h
*4]);
479 texts
[idx
].width
= w
;
480 texts
[idx
].height
= h
;
481 texts
[idx
].data
= data
;
484 } else if (compType
== 1) {
486 auto csz
= fl
.readNum
!uint;
487 if (csz
== 0 || csz
> int.max
) throw new Exception("invalid model file");
490 auto zs
= wrapZLibStreamRO(fl
, VFSZLibMode
.ZLib
, w
*h
*4, ep
, csz
);
491 //auto data = new ubyte[](w*h*4);
492 ubyte* data
= xalloc
!ubyte(w
*h
*4);
493 scope(failure
) xfree(data
);
494 zs
.rawReadExact(data
[0..w
*h
*4]);
495 texts
[idx
].width
= w
;
496 texts
[idx
].height
= h
;
497 texts
[idx
].data
= data
;
503 throw new Exception("invalid compression type");
507 throw new Exception("invalid model signature");
510 bbox
[0] = vec3(float.max
, float.max
, float.max
);
511 bbox
[1] = vec3(-float.max
, -float.max
, -float.max
);
512 foreach (const ref vec3 v
; verts
[0..faceCount
*3]) {
513 import std
.algorithm
: min
, max
;
514 bbox
[0].x
= min(bbox
[0].x
, v
.x
);
515 bbox
[0].y
= min(bbox
[0].y
, v
.y
);
516 bbox
[0].z
= min(bbox
[0].z
, v
.z
);
517 bbox
[1].x
= max(bbox
[1].x
, v
.x
);
518 bbox
[1].y
= max(bbox
[1].y
, v
.y
);
519 bbox
[1].z
= max(bbox
[1].z
, v
.z
);
525 // ////////////////////////////////////////////////////////////////////////// //
526 __gshared
bool doLights
= true;
527 __gshared
bool drawLights
= false;
529 __gshared
int lightCount
= 0;
530 __gshared
float[4][8] lightColor
;
531 __gshared
float[3][8] lightPos
;
535 // ////////////////////////////////////////////////////////////////////////// //
536 void lightsClear () {
541 void lightAdd (float x
, float y
, float z
, float r
, float g
, float b
) {
542 if (lightCount
< 8) {
543 lightColor
[lightCount
][0] = r
;
544 lightColor
[lightCount
][1] = g
;
545 lightColor
[lightCount
][2] = b
;
546 lightColor
[lightCount
][3] = 1.0f;
547 lightPos
[lightCount
][0] = x
;
548 lightPos
[lightCount
][1] = y
;
549 lightPos
[lightCount
][2] = z
;
555 // ////////////////////////////////////////////////////////////////////////// //
556 void drawModel (float angle
, EliteModel shipModel
) {
559 if (shipModel
is null) return;
561 //glClearColor(0.2, 0.2, 0.2, 0);
562 //glClear(shipModel.glClearFlags);
564 //glEnable(GL_DEPTH_TEST);
565 //glDepthFunc(GL_LEQUAL);
566 //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
570 glMatrixMode(GL_PROJECTION
);
573 90.0, // field of view in degree
578 float zz
= shipModel
.bbox
[1].z
-shipModel
.bbox
[0].z
;
580 //if (zz < 20) zz = 20;
582 0.0, 0.0, -zz
, // eye is at (0,0,5)
583 0.0, 0.0, 0, // center is at (0,0,0)
584 0.0, 1.0, 0.0, // up is in positive Y direction
587 // setup model matrix
588 glMatrixMode(GL_MODELVIEW
);
594 static immutable float[4] ldiff0 = [1.0, 1.0, 0.0, 1.0];
595 static immutable float[3] lpos0 = [-20.0, 0.0, -20.0-20];
596 glLightfv(GL_LIGHT0, GL_DIFFUSE, ldiff0.ptr);
597 glLightfv(GL_LIGHT0, GL_POSITION, lpos0.ptr);
601 static immutable float[4] ldiff1 = [1.0, 0.0, 0.0, 1.0];
602 static immutable float[3] lpos1 = [20.0, -20.0, 20.0-20];
603 glLightfv(GL_LIGHT1, GL_DIFFUSE, ldiff1.ptr);
604 glLightfv(GL_LIGHT1, GL_POSITION, lpos1.ptr);
621 glDisable(GL_LIGHTING
);
623 glShadeModel(GL_SMOOTH
);
624 __gshared
float[4][8] ldiff
= void;
625 __gshared
float[3][8] lpos
= void;
627 foreach (uint idx
; 0..lightCount
) {
628 ldiff
[idx
] = lightColor
.ptr
[idx
][];
629 lpos
[idx
] = lightPos
.ptr
[idx
][];
630 glColor4f(ldiff
[idx
][0], ldiff
[idx
][1], ldiff
[idx
][2], 1.0);
632 glBegin(/*GL_QUADS*/GL_TRIANGLE_FAN
);
633 glVertex3f(lpos
[idx
][0]-1, lpos
[idx
][1]-1, lpos
[idx
][2]);
634 glVertex3f(lpos
[idx
][0]+1, lpos
[idx
][1]-1, lpos
[idx
][2]);
635 glVertex3f(lpos
[idx
][0]+1, lpos
[idx
][1]+1, lpos
[idx
][2]);
636 glVertex3f(lpos
[idx
][0]-1, lpos
[idx
][1]+1, lpos
[idx
][2]);
642 glVertex3fv(lpos[idx].ptr);
650 __gshared
float[4] ambcol
;
655 glLightModelfv(GL_LIGHT_MODEL_AMBIENT
, ambcol
.ptr
);
656 foreach (uint idx
; 0..lightCount
) {
657 ldiff
[idx
] = lightColor
[idx
][];
658 lpos
[idx
] = lightPos
[idx
][];
659 //{ import std.stdio; writeln("LIGHT #", idx, " at ", lightPos[idx][], " color ", lightColor[idx][]); }
660 glEnable(GL_LIGHT0
+idx
);
661 //glLightfv(GL_LIGHT0+idx, GL_DIFFUSE, &lightColor[idx][0]);
662 //glLightfv(GL_LIGHT0+idx, GL_POSITION, &lightPos[idx][0]);
663 assert((&ldiff
[0])+1 == &ldiff
[1]);
664 glLightfv(GL_LIGHT0
+idx
, GL_DIFFUSE
, ldiff
[idx
].ptr
);
665 //glLightfv(GL_LIGHT0+idx, GL_SPECULAR, ldiff[idx].ptr);
666 glLightfv(GL_LIGHT0
+idx
, GL_POSITION
, lpos
[idx
].ptr
);
668 glEnable(GL_LIGHTING
);
672 static immutable float[4] ldiff0 = [1.0, 0.0, 0.0, 1.0];
673 static immutable float[3] lpos0 = [-90.0, 0.0, 90.0];
675 __gshared
float[4] ldiff0
= [1.0, 0.0, 0.0, 1.0];
676 __gshared
float[3] lpos0
= [-90.0, 0.0, 90.0];
677 //lightColor[0] = ldiff0[];
678 //lightPos[0] = lpos0[];
679 //glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor[0].ptr);
680 //glLightfv(GL_LIGHT0, GL_POSITION, lightPos[0].ptr);
681 glLightfv(GL_LIGHT0
, GL_DIFFUSE
, ldiff0
.ptr
);
682 glLightfv(GL_LIGHT0
, GL_POSITION
, lpos0
.ptr
);
683 //glEnable(GL_LIGHT0);
684 //glEnable(GL_LIGHTING);
718 glRotatef(angle
, 0.8, 0.5, 0.3);
720 glRotatef(angle, 0.8, 0.0, 0.0);
721 glRotatef(angle, 0.0, 0.3, 0.0);
722 glRotatef(angle, 0.0, 0.0, 0.1);
726 // enable color tracking
727 glEnable(GL_COLOR_MATERIAL
);
728 // set material properties which will be assigned by glColor
729 glColorMaterial(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
);
730 //glColorMaterial(GL_FRONT_AND_BACK, GL_SPECULAR);
731 // blue reflective properties
732 glColor3f(0.0f, 0.5f, 1.0f);
735 //glEnable(GL_LIGHTING);
737 shipModel
.glDrawExhaust();
740 foreach (uint idx
; 0..lightCount
) glDisable(GL_LIGHT0
+idx
);
741 glDisable(GL_LIGHTING
);
742 glShadeModel(GL_FLAT
);
744 //glDisable(GL_DEPTH_TEST);