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/>.
25 // ////////////////////////////////////////////////////////////////////////// //
26 final class EliteModel
{
28 static align(1) struct UV
{
33 static struct Texture
{
40 T
* xalloc(T
) (uint count
) {
41 import core
.stdc
.stdlib
: malloc
;
42 import core
.stdc
.string
: memset
;
44 auto res
= malloc(T
.sizeof
*count
);
45 if (res
is null) assert(0, "out of memory");
46 memset(res
, 0, T
.sizeof
*count
);
50 void xfree(T
) (ref T
* ptr
) {
52 import core
.stdc
.stdlib
: free
;
65 uint* mids
; // face material array
69 uint* inds
; // just a big array of indexes
70 uint subCount
; // submodels
71 uint* subfcs
; // faces in each submodel
72 ubyte* subsms
; // smoothness
84 this (const(char)[] fname
) { load(fname
); }
85 this (VFile fl
) { load(fl
); }
87 // don't unload textures here, as OpenGL context may be already destroyed
88 ~this () { freeData(); }
92 foreach (immutable idx
; 0..texCount
) xfree(texts
[idx
].data
);
113 import core
.stdc
.stdlib
: free
;
114 foreach (immutable idx
; 0..texCount
) xfree(texts
[idx
].data
);
118 glBindTexture(GL_TEXTURE_2D
, 0);
119 foreach (immutable idx
; 0..texCount
) {
120 if (texts
[idx
].tid
) {
121 glDeleteTextures(1, &texts
[idx
].tid
);
128 foreach (immutable idx
; 0..texCount
) {
129 if (texts
[idx
].tid
) continue;
130 if (texts
[idx
].data
is null) continue;
132 uint wrapOpt
= GL_REPEAT
;
133 uint filterOpt
= GL_LINEAR
;
135 glGenTextures(1, &texts
[idx
].tid
);
136 glBindTexture(GL_TEXTURE_2D
, texts
[idx
].tid
);
137 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrapOpt
);
138 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrapOpt
);
139 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, filterOpt
);
140 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, filterOpt
);
141 //float[4] bclr = 0.0;
142 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
143 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, texts
[idx
].width
, texts
[idx
].height
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, texts
[idx
].data
);
147 static uint glClearFlags () { pragma(inline
, true); return (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT
); }
149 // setup model and view matrices before calling this
150 // also, setup lighting
152 if (texCount
== 0) return;
154 glEnable(GL_TEXTURE_2D
);
155 glEnable(GL_DEPTH_TEST
);
156 //glDisable(GL_LIGHTING);
157 glDisable(GL_DITHER
);
160 glEnable(GL_RESCALE_NORMAL
);
161 glDisable(GL_SCISSOR_TEST
);
162 glDisable(GL_STENCIL_TEST
);
163 glDisable(GL_COLOR_MATERIAL
);
165 glDepthFunc(GL_LEQUAL
);
166 glHint(GL_PERSPECTIVE_CORRECTION_HINT
, GL_NICEST
);
168 glEnable(GL_CULL_FACE
);
169 //glCullFace(GL_FRONT);
173 glDisable(GL_CULL_FACE
); // this way we can draw any model
175 glEnableClientState(GL_VERTEX_ARRAY
);
176 glEnableClientState(GL_NORMAL_ARRAY
);
177 glEnableClientState(GL_TEXTURE_COORD_ARRAY
);
179 glVertexPointer(3, GL_FLOAT
, 0, verts
);
180 glNormalPointer(GL_FLOAT
, 0, norms
);
181 glTexCoordPointer(2, GL_FLOAT
, 0, uvs
);
184 foreach (immutable smidx
, uint len
; subfcs
[0..subCount
]) {
186 glShadeModel(subsms
[smidx
] ? GL_SMOOTH
: GL_FLAT
);
187 while (s
< faceCount
&& s
< end
) {
190 while (e
< faceCount
&& e
< end
&& mids
[e
] == tid
) ++e
;
191 glBindTexture(GL_TEXTURE_2D
, texts
[tid
].tid
);
192 glDrawElements(GL_TRIANGLES
, (e
-s
)*3, GL_UNSIGNED_INT
, inds
+s
*3);
197 glBindTexture(GL_TEXTURE_2D
, 0);
198 glDisableClientState(GL_VERTEX_ARRAY
);
199 glDisableClientState(GL_NORMAL_ARRAY
);
200 glDisableClientState(GL_TEXTURE_COORD_ARRAY
);
203 void glDrawExhaust () {
204 // "12.87 -6.34 -54.9 7.45 7.46 6"
205 // "-12.87 -6.34 -54.9 7.45 7.46 6"
207 glEnable(GL_TEXTURE_2D
);
208 glEnable(GL_DEPTH_TEST
);
209 //glDisable(GL_LIGHTING);
210 glDisable(GL_DITHER
);
211 glEnable(GL_COLOR_MATERIAL
);
214 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
216 glDepthFunc(GL_LEQUAL
);
217 glHint(GL_PERSPECTIVE_CORRECTION_HINT
, GL_NICEST
);
219 glEnable(GL_CULL_FACE
);
220 //glCullFace(GL_FRONT);
224 glDisable(GL_CULL_FACE
); // this way we can draw any model
225 glShadeModel(GL_FLAT
);
227 void drawTri() (in auto ref vec3 p0
, in auto ref vec3 p1
, in auto ref vec3 p2
) {
229 vec3 n
= (p1
-p0
).cross(p2
-p0
).normalized
;
230 glBegin(GL_TRIANGLES
);
231 glNormal3f(n
.x
, n
.y
, n
.z
);
232 glVertex3f(p0
.x
, p0
.y
, p0
.z
);
233 glVertex3f(p1
.x
, p1
.y
, p1
.z
);
234 glVertex3f(p2
.x
, p2
.y
, p2
.z
);
238 void drawCone() (/*in auto ref*/ vec3 pos
, /*in auto ref*/ vec3 size
) {
239 //float zlen = size.z;
240 //if (zlen < 0) zlen = -zlen;
241 auto zlen
= bbox
[1].z
-bbox
[0].z
;
242 if (zlen
< 0) zlen
= -zlen
;
245 glColor4f(0, 0.5, 0.8, 0.2);
249 pos
-vec3(size
.x
, size
.y
, 0),
250 pos
-vec3(0, 0, -zlen
),
251 pos
-vec3(size
.x
, -size
.y
, 0),
254 pos
-vec3(-size
.x
, -size
.y
, 0),
255 pos
-vec3(0, 0, -zlen
),
256 pos
-vec3(-size
.x
, size
.y
, 0),
259 pos
-vec3(-size
.x
, size
.y
, 0),
260 pos
-vec3(0, 0, -zlen
),
261 pos
-vec3(size
.x
, size
.y
, 0),
264 pos
-vec3(size
.x
, -size
.y
, 0),
265 pos
-vec3(0, 0, -zlen
),
266 pos
-vec3(-size
.x
, -size
.y
, 0),
270 //drawCone(vec3(12.87, -6.34, 54.9), vec3(7.45, 7.46, 6));
271 foreach (immutable ei
; 0..exCount
) {
272 drawCone(exPos
[ei
], exSize
[ei
]);
275 glColor4f(1, 1, 1, 1);
276 glDisable(GL_CULL_FACE
); // this way we can draw any model
281 void load (const(char)[] fname
) {
284 if (name
.length
== 0) {
285 name
= fname
.baseName
.stripExtension
.idup
;
286 if (name
.length
> 3 && name
[0..4] == "oxm:") name
= name
[4..$];
288 if (idName
.length
== 0) idName
= name
;
289 if (dispName
.length
== 0) dispName
= name
;
292 void load (VFile fl
) {
293 scope(failure
) freeData();
296 fl
.rawReadExact(sign
[]);
298 if (sign
== "EMD1") {
301 auto fcount
= fl
.readNum
!ushort;
302 if (fcount
== 0) throw new Exception("invalid model file");
303 // number of textures
304 auto tcount
= fl
.readNum
!ushort;
305 if (tcount
== 0 || tcount
> fcount
) throw new Exception("invalid model file");
307 mids
= xalloc
!uint(fcount
);
308 verts
= xalloc
!vec3(fcount
*3);
309 norms
= xalloc
!vec3(fcount
*3);
310 uvs
= xalloc
!UV(fcount
*3);
311 inds
= xalloc
!uint(fcount
*3);
313 subfcs
= xalloc
!uint(fcount
);
315 subsms
= xalloc
!ubyte(fcount
);
317 foreach (immutable idx
, ref n
; inds
[0..fcount
*3]) n
= cast(uint)idx
; // yep
320 foreach (immutable fidx
; 0..fcount
) {
322 mids
[fidx
] = fl
.readNum
!ushort;
323 if (mids
[fidx
] >= tcount
) throw new Exception("invalid model file");
325 foreach (immutable vn
; 0..3) {
327 uvs
[fidx
*3+vn
].u
= fl
.readNum
!float;
328 uvs
[fidx
*3+vn
].v
= fl
.readNum
!float;
330 verts
[fidx
*3+vn
].x
= fl
.readNum
!float;
331 verts
[fidx
*3+vn
].y
= fl
.readNum
!float;
332 verts
[fidx
*3+vn
].z
= fl
.readNum
!float;
334 norms
[fidx
*3+vn
].x
= fl
.readNum
!float;
335 norms
[fidx
*3+vn
].y
= fl
.readNum
!float;
336 norms
[fidx
*3+vn
].z
= fl
.readNum
!float;
340 texts
= xalloc
!Texture(tcount
);
342 foreach (immutable idx
; 0..tcount
) {
344 auto w
= fl
.readNum
!ushort;
345 auto h
= fl
.readNum
!ushort;
346 if (w
< 1 || h
< 1) throw new Exception("invalid model file");
348 auto csz
= fl
.readNum
!uint;
349 if (csz
== 0 || csz
> int.max
) throw new Exception("invalid model file");
352 auto zs
= wrapZLibStreamRO(fl
, VFSZLibMode
.ZLib
, w
*h
*4, ep
, csz
);
353 //auto data = new ubyte[](w*h*4);
354 ubyte* data
= xalloc
!ubyte(w
*h
*4);
355 scope(failure
) xfree(data
);
356 zs
.rawReadExact(data
[0..w
*h
*4]);
357 //texts ~= Texture(w, h, data, 0);
358 texts
[idx
].width
= w
;
359 texts
[idx
].height
= h
;
360 texts
[idx
].data
= data
;
366 } else if (sign
== "EMD2") {
369 auto fcount
= fl
.readNum
!ushort;
370 if (fcount
== 0) throw new Exception("invalid model file");
371 // number of textures
372 auto tcount
= fl
.readNum
!ushort;
373 if (tcount
== 0 || tcount
> fcount
) throw new Exception("invalid model file");
374 // number of submodels
375 auto scount
= fl
.readNum
!ushort;
376 if (scount
== 0 || scount
> fcount
) throw new Exception("invalid model file");
377 // number of exhausts
378 auto ecount
= fl
.readNum
!ushort;
379 if (/*ecount == 0 ||*/ ecount
> 255) throw new Exception("invalid model file");
381 mids
= xalloc
!uint(fcount
);
382 verts
= xalloc
!vec3(fcount
*3);
383 norms
= xalloc
!vec3(fcount
*3);
384 uvs
= xalloc
!UV(fcount
*3);
385 inds
= xalloc
!uint(fcount
*3);
387 subfcs
= xalloc
!uint(scount
);
388 subsms
= xalloc
!ubyte(scount
);
389 foreach (immutable idx
, ref n
; inds
[0..fcount
*3]) n
= cast(uint)idx
; // yep
392 exPos
= xalloc
!vec3(ecount
);
393 exSize
= xalloc
!vec3(ecount
);
397 auto len
= fl
.readNum
!ubyte;
399 auto s
= new char[](len
);
400 fl
.rawReadExact(s
[]);
401 return cast(string
)s
; // it is safe to cast here
411 foreach (ref uint c
; subfcs
[0..scount
]) c
= fl
.readNum
!ushort;
413 //foreach (ref ubyte v; subsms[0..scount]) v = fl.readNum!ubyte;
414 fl
.rawReadExact(subsms
[0..scount
]);
417 foreach (immutable ei
; 0..ecount
) {
418 exPos
[ei
].x
= fl
.readNum
!float;
419 exPos
[ei
].y
= fl
.readNum
!float;
420 exPos
[ei
].z
= fl
.readNum
!float;
421 exSize
[ei
].x
= fl
.readNum
!float;
422 exSize
[ei
].y
= fl
.readNum
!float;
423 exSize
[ei
].z
= fl
.readNum
!float;
427 foreach (immutable fidx
; 0..fcount
) {
429 mids
[fidx
] = fl
.readNum
!ushort;
430 if (mids
[fidx
] >= tcount
) throw new Exception("invalid model file");
432 foreach (immutable vn
; 0..3) {
434 uvs
[fidx
*3+vn
].u
= fl
.readNum
!float;
435 uvs
[fidx
*3+vn
].v
= fl
.readNum
!float;
437 verts
[fidx
*3+vn
].x
= fl
.readNum
!float;
438 verts
[fidx
*3+vn
].y
= fl
.readNum
!float;
439 verts
[fidx
*3+vn
].z
= fl
.readNum
!float;
441 norms
[fidx
*3+vn
].x
= fl
.readNum
!float;
442 norms
[fidx
*3+vn
].y
= fl
.readNum
!float;
443 norms
[fidx
*3+vn
].z
= fl
.readNum
!float;
447 // material (texture) colors
449 foreach (immutable _; 0..tcount) {
450 fl.writeNum!float(mat.colorAmb[0]);
451 fl.writeNum!float(mat.colorAmb[1]);
452 fl.writeNum!float(mat.colorAmb[2]);
453 fl.writeNum!float(mat.colorAmb[3]);
454 fl.writeNum!float(mat.colorDiff[0]);
455 fl.writeNum!float(mat.colorDiff[1]);
456 fl.writeNum!float(mat.colorDiff[2]);
457 fl.writeNum!float(mat.colorDiff[3]);
458 fl.writeNum!float(mat.colorSpec[0]);
459 fl.writeNum!float(mat.colorSpec[1]);
460 fl.writeNum!float(mat.colorSpec[2]);
461 fl.writeNum!float(mat.colorSpec[3]);
462 fl.writeNum!float(mat.shininess);
465 fl
.seek(tcount
*(13*float.sizeof
), Seek
.Cur
);
469 texts
= xalloc
!Texture(tcount
);
470 foreach (immutable idx
; 0..tcount
) {
472 auto w
= fl
.readNum
!ushort;
473 auto h
= fl
.readNum
!ushort;
474 if (w
< 1 || h
< 1) throw new Exception("invalid model file");
475 auto compType
= fl
.readNum
!ubyte;
477 ubyte* data
= xalloc
!ubyte(w
*h
*4);
478 scope(failure
) xfree(data
);
479 fl
.rawReadExact(data
[0..w
*h
*4]);
480 texts
[idx
].width
= w
;
481 texts
[idx
].height
= h
;
482 texts
[idx
].data
= data
;
485 } else if (compType
== 1) {
487 auto csz
= fl
.readNum
!uint;
488 if (csz
== 0 || csz
> int.max
) throw new Exception("invalid model file");
491 auto zs
= wrapZLibStreamRO(fl
, VFSZLibMode
.ZLib
, w
*h
*4, ep
, csz
);
492 //auto data = new ubyte[](w*h*4);
493 ubyte* data
= xalloc
!ubyte(w
*h
*4);
494 scope(failure
) xfree(data
);
495 zs
.rawReadExact(data
[0..w
*h
*4]);
496 texts
[idx
].width
= w
;
497 texts
[idx
].height
= h
;
498 texts
[idx
].data
= data
;
504 throw new Exception("invalid compression type");
508 throw new Exception("invalid model signature");
511 bbox
[0] = vec3(float.max
, float.max
, float.max
);
512 bbox
[1] = vec3(-float.max
, -float.max
, -float.max
);
513 foreach (const ref vec3 v
; verts
[0..faceCount
*3]) {
514 import std
.algorithm
: min
, max
;
515 bbox
[0].x
= min(bbox
[0].x
, v
.x
);
516 bbox
[0].y
= min(bbox
[0].y
, v
.y
);
517 bbox
[0].z
= min(bbox
[0].z
, v
.z
);
518 bbox
[1].x
= max(bbox
[1].x
, v
.x
);
519 bbox
[1].y
= max(bbox
[1].y
, v
.y
);
520 bbox
[1].z
= max(bbox
[1].z
, v
.z
);
526 // ////////////////////////////////////////////////////////////////////////// //
527 __gshared
bool doLights
= true;
528 __gshared
bool drawLights
= false;
530 __gshared
int lightCount
= 0;
531 __gshared
float[4][8] lightColor
;
532 __gshared
float[3][8] lightPos
;
536 // ////////////////////////////////////////////////////////////////////////// //
537 void lightsClear () {
542 void lightAdd (float x
, float y
, float z
, float r
, float g
, float b
) {
543 if (lightCount
< 8) {
544 lightColor
[lightCount
][0] = r
;
545 lightColor
[lightCount
][1] = g
;
546 lightColor
[lightCount
][2] = b
;
547 lightColor
[lightCount
][3] = 1.0f;
548 lightPos
[lightCount
][0] = x
;
549 lightPos
[lightCount
][1] = y
;
550 lightPos
[lightCount
][2] = z
;
556 // ////////////////////////////////////////////////////////////////////////// //
557 void drawModel (float angle
, EliteModel shipModel
) {
560 if (shipModel
is null) return;
562 //glClearColor(0.2, 0.2, 0.2, 0);
563 //glClear(shipModel.glClearFlags);
565 //glEnable(GL_DEPTH_TEST);
566 //glDepthFunc(GL_LEQUAL);
567 //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
571 glMatrixMode(GL_PROJECTION
);
574 90.0, // field of view in degree
579 float zz
= shipModel
.bbox
[1].z
-shipModel
.bbox
[0].z
;
581 //if (zz < 20) zz = 20;
583 0.0, 0.0, -zz
, // eye is at (0,0,5)
584 0.0, 0.0, 0, // center is at (0,0,0)
585 0.0, 1.0, 0.0, // up is in positive Y direction
588 // setup model matrix
589 glMatrixMode(GL_MODELVIEW
);
595 static immutable float[4] ldiff0 = [1.0, 1.0, 0.0, 1.0];
596 static immutable float[3] lpos0 = [-20.0, 0.0, -20.0-20];
597 glLightfv(GL_LIGHT0, GL_DIFFUSE, ldiff0.ptr);
598 glLightfv(GL_LIGHT0, GL_POSITION, lpos0.ptr);
602 static immutable float[4] ldiff1 = [1.0, 0.0, 0.0, 1.0];
603 static immutable float[3] lpos1 = [20.0, -20.0, 20.0-20];
604 glLightfv(GL_LIGHT1, GL_DIFFUSE, ldiff1.ptr);
605 glLightfv(GL_LIGHT1, GL_POSITION, lpos1.ptr);
622 glDisable(GL_LIGHTING
);
624 glShadeModel(GL_SMOOTH
);
625 __gshared
float[4][8] ldiff
= void;
626 __gshared
float[3][8] lpos
= void;
628 foreach (uint idx
; 0..lightCount
) {
629 ldiff
[idx
] = lightColor
.ptr
[idx
][];
630 lpos
[idx
] = lightPos
.ptr
[idx
][];
631 glColor4f(ldiff
[idx
][0], ldiff
[idx
][1], ldiff
[idx
][2], 1.0);
633 glBegin(/*GL_QUADS*/GL_TRIANGLE_FAN
);
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]);
637 glVertex3f(lpos
[idx
][0]-1, lpos
[idx
][1]+1, lpos
[idx
][2]);
643 glVertex3fv(lpos[idx].ptr);
651 __gshared
float[4] ambcol
;
656 glLightModelfv(GL_LIGHT_MODEL_AMBIENT
, ambcol
.ptr
);
657 foreach (uint idx
; 0..lightCount
) {
658 ldiff
[idx
] = lightColor
[idx
][];
659 lpos
[idx
] = lightPos
[idx
][];
660 //{ import std.stdio; writeln("LIGHT #", idx, " at ", lightPos[idx][], " color ", lightColor[idx][]); }
661 glEnable(GL_LIGHT0
+idx
);
662 //glLightfv(GL_LIGHT0+idx, GL_DIFFUSE, &lightColor[idx][0]);
663 //glLightfv(GL_LIGHT0+idx, GL_POSITION, &lightPos[idx][0]);
664 assert((&ldiff
[0])+1 == &ldiff
[1]);
665 glLightfv(GL_LIGHT0
+idx
, GL_DIFFUSE
, ldiff
[idx
].ptr
);
666 //glLightfv(GL_LIGHT0+idx, GL_SPECULAR, ldiff[idx].ptr);
667 glLightfv(GL_LIGHT0
+idx
, GL_POSITION
, lpos
[idx
].ptr
);
669 glEnable(GL_LIGHTING
);
673 static immutable float[4] ldiff0 = [1.0, 0.0, 0.0, 1.0];
674 static immutable float[3] lpos0 = [-90.0, 0.0, 90.0];
676 __gshared
float[4] ldiff0
= [1.0, 0.0, 0.0, 1.0];
677 __gshared
float[3] lpos0
= [-90.0, 0.0, 90.0];
678 //lightColor[0] = ldiff0[];
679 //lightPos[0] = lpos0[];
680 //glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor[0].ptr);
681 //glLightfv(GL_LIGHT0, GL_POSITION, lightPos[0].ptr);
682 glLightfv(GL_LIGHT0
, GL_DIFFUSE
, ldiff0
.ptr
);
683 glLightfv(GL_LIGHT0
, GL_POSITION
, lpos0
.ptr
);
684 //glEnable(GL_LIGHT0);
685 //glEnable(GL_LIGHTING);
719 glRotatef(angle
, 0.8, 0.5, 0.3);
721 glRotatef(angle, 0.8, 0.0, 0.0);
722 glRotatef(angle, 0.0, 0.3, 0.0);
723 glRotatef(angle, 0.0, 0.0, 0.1);
727 // enable color tracking
728 glEnable(GL_COLOR_MATERIAL
);
729 // set material properties which will be assigned by glColor
730 glColorMaterial(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
);
731 //glColorMaterial(GL_FRONT_AND_BACK, GL_SPECULAR);
732 // blue reflective properties
733 glColor3f(0.0f, 0.5f, 1.0f);
736 //glEnable(GL_LIGHTING);
738 shipModel
.glDrawExhaust();
741 foreach (uint idx
; 0..lightCount
) glDisable(GL_LIGHT0
+idx
);
742 glDisable(GL_LIGHTING
);
743 glShadeModel(GL_FLAT
);
745 //glDisable(GL_DEPTH_TEST);